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)) {
+ rv = nghttp3_qpack_encoder_write_duplicate_insert(encoder, ebuf,
+ (size_t)dres.index);
+ if (rv != 0) {
+ return rv;
+ }
+ rv = nghttp3_qpack_encoder_dtable_duplicate_add(encoder,
+ (size_t)dres.index);
+ if (rv != 0) {
+ return rv;
+ }
+
+ new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx);
+ dres.index = (nghttp3_ssize)new_ent->absidx;
+ }
+ *pmax_cnt = nghttp3_max(*pmax_cnt, (size_t)(dres.index + 1));
+ *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)(dres.index + 1));
+
+ return nghttp3_qpack_encoder_write_dynamic_indexed(
+ encoder, rbuf, (size_t)dres.index, base);
+ }
+
+ if (sres.index != -1) {
+ if (just_index && qpack_encoder_can_index_nv(encoder, nv, *pmin_cnt)) {
+ rv = nghttp3_qpack_encoder_write_static_insert(encoder, ebuf,
+ (size_t)sres.index, nv);
+ if (rv != 0) {
+ return rv;
+ }
+ rv = nghttp3_qpack_encoder_dtable_static_add(encoder, (size_t)sres.index,
+ nv, hash);
+ if (rv != 0) {
+ return rv;
+ }
+ if (allow_blocking) {
+ new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx);
+ *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1);
+ *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1);
+
+ return nghttp3_qpack_encoder_write_dynamic_indexed(
+ encoder, rbuf, new_ent->absidx, base);
+ }
+ }
+
+ return nghttp3_qpack_encoder_write_static_indexed_name(
+ encoder, rbuf, (size_t)sres.index, nv);
+ }
+
+ if (dres.index != -1) {
+ if (just_index &&
+ qpack_encoder_can_index_nv(
+ encoder, nv,
+ allow_blocking ? *pmin_cnt
+ : nghttp3_min((size_t)dres.index + 1, *pmin_cnt))) {
+ rv = nghttp3_qpack_encoder_write_dynamic_insert(encoder, ebuf,
+ (size_t)dres.index, nv);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (!allow_blocking) {
+ *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)dres.index + 1);
+ }
+
+ rv = nghttp3_qpack_encoder_dtable_dynamic_add(encoder, (size_t)dres.index,
+ nv, hash);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (allow_blocking) {
+ new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx);
+ *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1);
+ *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1);
+
+ return nghttp3_qpack_encoder_write_dynamic_indexed(
+ encoder, rbuf, new_ent->absidx, base);
+ }
+ }
+
+ *pmax_cnt = nghttp3_max(*pmax_cnt, (size_t)(dres.index + 1));
+ *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)(dres.index + 1));
+
+ return nghttp3_qpack_encoder_write_dynamic_indexed_name(
+ encoder, rbuf, (size_t)dres.index, base, nv);
+ }
+
+ if (just_index && qpack_encoder_can_index_nv(encoder, nv, *pmin_cnt)) {
+ rv = nghttp3_qpack_encoder_dtable_literal_add(encoder, nv, token, hash);
+ if (rv != 0) {
+ return rv;
+ }
+ rv = nghttp3_qpack_encoder_write_literal_insert(encoder, ebuf, nv);
+ if (rv != 0) {
+ return rv;
+ }
+ if (allow_blocking) {
+ new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx);
+ *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1);
+ *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1);
+
+ return nghttp3_qpack_encoder_write_dynamic_indexed(encoder, rbuf,
+ new_ent->absidx, base);
+ }
+ }
+
+ return nghttp3_qpack_encoder_write_literal(encoder, rbuf, nv);
+}
+
+nghttp3_qpack_lookup_result
+nghttp3_qpack_lookup_stable(const nghttp3_nv *nv, int32_t token,
+ nghttp3_qpack_indexing_mode indexing_mode) {
+ nghttp3_qpack_lookup_result res = {(nghttp3_ssize)token_stable[token].absidx,
+ 0, -1};
+ nghttp3_qpack_static_entry *ent;
+ nghttp3_qpack_static_header *hdr;
+ size_t i;
+
+ assert(token >= 0);
+
+ if (indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_NEVER) {
+ return res;
+ }
+
+ for (i = (size_t)token;
+ i < nghttp3_arraylen(token_stable) && token_stable[i].token == token;
+ ++i) {
+ ent = &token_stable[i];
+ hdr = &stable[ent->absidx];
+ if (hdr->value.len == nv->valuelen &&
+ memeq(hdr->value.base, nv->value, nv->valuelen)) {
+ res.index = (nghttp3_ssize)ent->absidx;
+ res.name_value_match = 1;
+ return res;
+ }
+ }
+ return res;
+}
+
+nghttp3_qpack_lookup_result nghttp3_qpack_encoder_lookup_dtable(
+ nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token,
+ uint32_t hash, nghttp3_qpack_indexing_mode indexing_mode, uint64_t krcnt,
+ int allow_blocking) {
+ nghttp3_qpack_lookup_result res = {-1, 0, -1};
+ int exact_match = 0;
+ nghttp3_qpack_entry *match, *pb_match;
+
+ encoder_qpack_map_find(encoder, &exact_match, &match, &pb_match, nv, token,
+ hash, krcnt, allow_blocking,
+ indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_NEVER);
+ if (match) {
+ res.index = (nghttp3_ssize)match->absidx;
+ res.name_value_match = exact_match;
+ }
+ if (pb_match) {
+ res.pb_index = (nghttp3_ssize)pb_match->absidx;
+ }
+
+ return res;
+}
+
+int nghttp3_qpack_header_block_ref_new(nghttp3_qpack_header_block_ref **pref,
+ uint64_t max_cnt, uint64_t min_cnt,
+ const nghttp3_mem *mem) {
+ nghttp3_qpack_header_block_ref *ref =
+ nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_header_block_ref));
+
+ if (ref == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ ref->max_cnts_pe.index = NGHTTP3_PQ_BAD_INDEX;
+ ref->min_cnts_pe.index = NGHTTP3_PQ_BAD_INDEX;
+ ref->max_cnt = max_cnt;
+ ref->min_cnt = min_cnt;
+
+ *pref = ref;
+
+ return 0;
+}
+
+void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref,
+ const nghttp3_mem *mem) {
+ nghttp3_mem_free(mem, ref);
+}
+
+static int ref_max_cnt_greater(const nghttp3_pq_entry *lhsx,
+ const nghttp3_pq_entry *rhsx) {
+ const nghttp3_qpack_header_block_ref *lhs =
+ nghttp3_struct_of(lhsx, nghttp3_qpack_header_block_ref, max_cnts_pe);
+ const nghttp3_qpack_header_block_ref *rhs =
+ nghttp3_struct_of(rhsx, nghttp3_qpack_header_block_ref, max_cnts_pe);
+
+ return lhs->max_cnt > rhs->max_cnt;
+}
+
+int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id,
+ const nghttp3_mem *mem) {
+ int rv;
+ nghttp3_qpack_stream *stream;
+
+ stream = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream));
+ if (stream == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ rv = nghttp3_ringbuf_init(&stream->refs, 4,
+ sizeof(nghttp3_qpack_header_block_ref *), mem);
+ if (rv != 0) {
+ nghttp3_mem_free(mem, stream);
+ return rv;
+ }
+
+ nghttp3_pq_init(&stream->max_cnts, ref_max_cnt_greater, mem);
+
+ stream->stream_id = stream_id;
+
+ *pstream = stream;
+
+ return 0;
+}
+
+void nghttp3_qpack_stream_del(nghttp3_qpack_stream *stream,
+ const nghttp3_mem *mem) {
+ nghttp3_qpack_header_block_ref *ref;
+ size_t i, len;
+
+ if (stream == NULL) {
+ return;
+ }
+
+ nghttp3_pq_free(&stream->max_cnts);
+
+ len = nghttp3_ringbuf_len(&stream->refs);
+ for (i = 0; i < len; ++i) {
+ ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs,
+ i);
+ nghttp3_qpack_header_block_ref_del(ref, mem);
+ }
+
+ nghttp3_ringbuf_free(&stream->refs);
+
+ nghttp3_mem_free(mem, stream);
+}
+
+uint64_t nghttp3_qpack_stream_get_max_cnt(const nghttp3_qpack_stream *stream) {
+ nghttp3_qpack_header_block_ref *ref;
+
+ if (nghttp3_pq_empty(&stream->max_cnts)) {
+ return 0;
+ }
+
+ ref = nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
+ nghttp3_qpack_header_block_ref, max_cnts_pe);
+ return ref->max_cnt;
+}
+
+int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream,
+ nghttp3_qpack_header_block_ref *ref) {
+ nghttp3_qpack_header_block_ref **dest;
+ int rv;
+
+ if (nghttp3_ringbuf_full(&stream->refs)) {
+ rv = nghttp3_ringbuf_reserve(&stream->refs,
+ nghttp3_ringbuf_len(&stream->refs) * 2);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ dest = nghttp3_ringbuf_push_back(&stream->refs);
+ *dest = ref;
+
+ return nghttp3_pq_push(&stream->max_cnts, &ref->max_cnts_pe);
+}
+
+void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream) {
+ nghttp3_qpack_header_block_ref *ref;
+
+ assert(nghttp3_ringbuf_len(&stream->refs));
+
+ ref =
+ *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0);
+
+ assert(ref->max_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX);
+
+ nghttp3_pq_remove(&stream->max_cnts, &ref->max_cnts_pe);
+
+ nghttp3_ringbuf_pop_front(&stream->refs);
+}
+
+int nghttp3_qpack_encoder_write_static_indexed(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ uint64_t absidx) {
+ DEBUGF("qpack::encode: Indexed Field Line (static) absidx=%" PRIu64 "\n",
+ absidx);
+ return qpack_write_number(rbuf, 0xc0, absidx, 6, encoder->ctx.mem);
+}
+
+int nghttp3_qpack_encoder_write_dynamic_indexed(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ uint64_t absidx,
+ uint64_t base) {
+ DEBUGF("qpack::encode: Indexed Field Line (dynamic) absidx=%" PRIu64
+ " base=%" PRIu64 "\n",
+ absidx, base);
+
+ if (absidx < base) {
+ return qpack_write_number(rbuf, 0x80, base - absidx - 1, 6,
+ encoder->ctx.mem);
+ }
+
+ return qpack_write_number(rbuf, 0x10, absidx - base, 4, encoder->ctx.mem);
+}
+
+/*
+ * qpack_encoder_write_indexed_name writes generic indexed name. |fb|
+ * is the first byte. |nameidx| is an index of referenced name.
+ * |prefix| is a prefix of variable integer encoding. |nv| is a
+ * header field to encode.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int qpack_encoder_write_indexed_name(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *buf, uint8_t fb,
+ uint64_t nameidx, size_t prefix,
+ const nghttp3_nv *nv) {
+ int rv;
+ size_t len = nghttp3_qpack_put_varint_len(nameidx, prefix);
+ uint8_t *p;
+ size_t hlen;
+ int h = 0;
+
+ hlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen);
+ if (hlen < nv->valuelen) {
+ h = 1;
+ len += nghttp3_qpack_put_varint_len(hlen, 7) + hlen;
+ } else {
+ len += nghttp3_qpack_put_varint_len(nv->valuelen, 7) + nv->valuelen;
+ }
+
+ rv = reserve_buf(buf, len, encoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = buf->last;
+
+ *p = fb;
+ p = nghttp3_qpack_put_varint(p, nameidx, prefix);
+
+ if (h) {
+ *p = 0x80;
+ p = nghttp3_qpack_put_varint(p, hlen, 7);
+ p = nghttp3_qpack_huffman_encode(p, nv->value, nv->valuelen);
+ } else {
+ *p = 0;
+ p = nghttp3_qpack_put_varint(p, nv->valuelen, 7);
+ if (nv->valuelen) {
+ p = nghttp3_cpymem(p, nv->value, nv->valuelen);
+ }
+ }
+
+ assert((size_t)(p - buf->last) == len);
+
+ buf->last = p;
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_write_static_indexed_name(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx,
+ const nghttp3_nv *nv) {
+ uint8_t fb =
+ (uint8_t)(0x50 | ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x20 : 0));
+
+ DEBUGF("qpack::encode: Literal Field Line With Name Reference (static) "
+ "absidx=%" PRIu64 " never=%d\n",
+ absidx, (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) != 0);
+ return qpack_encoder_write_indexed_name(encoder, rbuf, fb, absidx, 4, nv);
+}
+
+int nghttp3_qpack_encoder_write_dynamic_indexed_name(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx,
+ uint64_t base, const nghttp3_nv *nv) {
+ uint8_t fb;
+
+ DEBUGF("qpack::encode: Literal Field Line With Name Reference (dynamic) "
+ "absidx=%" PRIu64 " base=%" PRIu64 " never=%d\n",
+ absidx, base, (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) != 0);
+
+ if (absidx < base) {
+ fb = (uint8_t)(0x40 |
+ ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x20 : 0));
+ return qpack_encoder_write_indexed_name(encoder, rbuf, fb,
+ base - absidx - 1, 4, nv);
+ }
+
+ fb = (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x08 : 0;
+ return qpack_encoder_write_indexed_name(encoder, rbuf, fb, absidx - base, 3,
+ nv);
+}
+
+/*
+ * qpack_encoder_write_literal writes generic literal header field
+ * representation. |fb| is a first byte. |prefix| is a prefix of
+ * variable integer encoding for name length. |nv| is a header field
+ * to encode.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+static int qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *buf, uint8_t fb,
+ size_t prefix, const nghttp3_nv *nv) {
+ int rv;
+ size_t len;
+ uint8_t *p;
+ size_t nhlen, vhlen;
+ int nh = 0, vh = 0;
+
+ nhlen = nghttp3_qpack_huffman_encode_count(nv->name, nv->namelen);
+ if (nhlen < nv->namelen) {
+ nh = 1;
+ len = nghttp3_qpack_put_varint_len(nhlen, prefix) + nhlen;
+ } else {
+ len = nghttp3_qpack_put_varint_len(nv->namelen, prefix) + nv->namelen;
+ }
+
+ vhlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen);
+ if (vhlen < nv->valuelen) {
+ vh = 1;
+ len += nghttp3_qpack_put_varint_len(vhlen, 7) + vhlen;
+ } else {
+ len += nghttp3_qpack_put_varint_len(nv->valuelen, 7) + nv->valuelen;
+ }
+
+ rv = reserve_buf(buf, len, encoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = buf->last;
+
+ *p = fb;
+ if (nh) {
+ *p |= (uint8_t)(1 << prefix);
+ p = nghttp3_qpack_put_varint(p, nhlen, prefix);
+ p = nghttp3_qpack_huffman_encode(p, nv->name, nv->namelen);
+ } else {
+ p = nghttp3_qpack_put_varint(p, nv->namelen, prefix);
+ if (nv->namelen) {
+ p = nghttp3_cpymem(p, nv->name, nv->namelen);
+ }
+ }
+
+ *p = 0;
+
+ if (vh) {
+ *p |= 0x80;
+ p = nghttp3_qpack_put_varint(p, vhlen, 7);
+ p = nghttp3_qpack_huffman_encode(p, nv->value, nv->valuelen);
+ } else {
+ p = nghttp3_qpack_put_varint(p, nv->valuelen, 7);
+ if (nv->valuelen) {
+ p = nghttp3_cpymem(p, nv->value, nv->valuelen);
+ }
+ }
+
+ assert((size_t)(p - buf->last) == len);
+
+ buf->last = p;
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ const nghttp3_nv *nv) {
+ uint8_t fb =
+ (uint8_t)(0x20 | ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x10 : 0));
+
+ DEBUGF("qpack::encode: Literal Field Line With Literal Name\n");
+ return qpack_encoder_write_literal(encoder, rbuf, fb, 3, nv);
+}
+
+int nghttp3_qpack_encoder_write_static_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx,
+ const nghttp3_nv *nv) {
+ DEBUGF("qpack::encode: Insert With Name Reference (static) absidx=%" PRIu64
+ "\n",
+ absidx);
+ return qpack_encoder_write_indexed_name(encoder, ebuf, 0xc0, absidx, 6, nv);
+}
+
+int nghttp3_qpack_encoder_write_dynamic_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx,
+ const nghttp3_nv *nv) {
+ DEBUGF("qpack::encode: Insert With Name Reference (dynamic) absidx=%" PRIu64
+ "\n",
+ absidx);
+ return qpack_encoder_write_indexed_name(
+ encoder, ebuf, 0x80, encoder->ctx.next_absidx - absidx - 1, 6, nv);
+}
+
+int nghttp3_qpack_encoder_write_duplicate_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx) {
+ uint64_t idx = encoder->ctx.next_absidx - absidx - 1;
+ size_t len = nghttp3_qpack_put_varint_len(idx, 5);
+ uint8_t *p;
+ int rv;
+
+ DEBUGF("qpack::encode: Insert duplicate absidx=%" PRIu64 "\n", absidx);
+
+ rv = reserve_buf(ebuf, len, encoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = ebuf->last;
+
+ *p = 0;
+ p = nghttp3_qpack_put_varint(p, idx, 5);
+
+ assert((size_t)(p - ebuf->last) == len);
+
+ ebuf->last = p;
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_write_literal_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ const nghttp3_nv *nv) {
+ DEBUGF("qpack::encode: Insert With Literal Name\n");
+ return qpack_encoder_write_literal(encoder, ebuf, 0x40, 5, nv);
+}
+
+int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx,
+ nghttp3_qpack_nv *qnv,
+ nghttp3_qpack_map *dtable_map,
+ uint32_t hash) {
+ nghttp3_qpack_entry *new_ent, **p, *ent;
+ const nghttp3_mem *mem = ctx->mem;
+ size_t space;
+ size_t i;
+ int rv;
+
+ space = table_space(qnv->name->len, qnv->value->len);
+
+ assert(space <= ctx->max_dtable_capacity);
+
+ while (ctx->dtable_size + space > ctx->max_dtable_capacity) {
+ i = nghttp3_ringbuf_len(&ctx->dtable);
+ assert(i);
+ ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1);
+
+ ctx->dtable_size -= table_space(ent->nv.name->len, ent->nv.value->len);
+
+ nghttp3_ringbuf_pop_back(&ctx->dtable);
+ if (dtable_map) {
+ qpack_map_remove(dtable_map, ent);
+ }
+
+ nghttp3_qpack_entry_free(ent);
+ nghttp3_mem_free(mem, ent);
+ }
+
+ new_ent = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_entry));
+ if (new_ent == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ nghttp3_qpack_entry_init(new_ent, qnv, ctx->dtable_sum, ctx->next_absidx++,
+ hash);
+
+ if (nghttp3_ringbuf_full(&ctx->dtable)) {
+ rv = nghttp3_ringbuf_reserve(&ctx->dtable,
+ nghttp3_ringbuf_len(&ctx->dtable) * 2);
+ if (rv != 0) {
+ goto fail;
+ }
+ }
+
+ p = nghttp3_ringbuf_push_front(&ctx->dtable);
+ *p = new_ent;
+
+ if (dtable_map) {
+ qpack_map_insert(dtable_map, new_ent);
+ }
+
+ ctx->dtable_size += space;
+ ctx->dtable_sum += space;
+
+ return 0;
+
+fail:
+ nghttp3_qpack_entry_free(new_ent);
+ nghttp3_mem_free(mem, new_ent);
+
+ return rv;
+}
+
+int nghttp3_qpack_encoder_dtable_static_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx,
+ const nghttp3_nv *nv,
+ uint32_t hash) {
+ const nghttp3_qpack_static_header *shd;
+ nghttp3_qpack_nv qnv;
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ int rv;
+
+ rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ assert(nghttp3_arraylen(stable) > absidx);
+
+ shd = &stable[absidx];
+
+ qnv.name = (nghttp3_rcbuf *)&shd->name;
+ qnv.token = shd->token;
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv,
+ &encoder->dtable_map, hash);
+
+ nghttp3_rcbuf_decref(qnv.value);
+
+ return rv;
+}
+
+int nghttp3_qpack_encoder_dtable_dynamic_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx,
+ const nghttp3_nv *nv,
+ uint32_t hash) {
+ nghttp3_qpack_nv qnv;
+ nghttp3_qpack_entry *ent;
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ int rv;
+
+ rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx);
+
+ qnv.name = ent->nv.name;
+ qnv.token = ent->nv.token;
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ nghttp3_rcbuf_incref(qnv.name);
+
+ rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv,
+ &encoder->dtable_map, hash);
+
+ nghttp3_rcbuf_decref(qnv.value);
+ nghttp3_rcbuf_decref(qnv.name);
+
+ return rv;
+}
+
+int nghttp3_qpack_encoder_dtable_duplicate_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx) {
+ nghttp3_qpack_nv qnv;
+ nghttp3_qpack_entry *ent;
+ int rv;
+
+ ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx);
+
+ qnv = ent->nv;
+ nghttp3_rcbuf_incref(qnv.name);
+ nghttp3_rcbuf_incref(qnv.value);
+
+ rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv,
+ &encoder->dtable_map, ent->hash);
+
+ nghttp3_rcbuf_decref(qnv.name);
+ nghttp3_rcbuf_decref(qnv.value);
+
+ return rv;
+}
+
+int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder,
+ const nghttp3_nv *nv,
+ int32_t token, uint32_t hash) {
+ nghttp3_qpack_nv qnv;
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ int rv;
+
+ rv = nghttp3_rcbuf_new2(&qnv.name, nv->name, nv->namelen, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem);
+ if (rv != 0) {
+ nghttp3_rcbuf_decref(qnv.name);
+ return rv;
+ }
+
+ qnv.token = token;
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv,
+ &encoder->dtable_map, hash);
+
+ nghttp3_rcbuf_decref(qnv.value);
+ nghttp3_rcbuf_decref(qnv.name);
+
+ return rv;
+}
+
+nghttp3_qpack_entry *
+nghttp3_qpack_context_dtable_get(nghttp3_qpack_context *ctx, uint64_t absidx) {
+ size_t relidx;
+
+ assert(ctx->next_absidx > absidx);
+ assert(ctx->next_absidx - absidx - 1 < nghttp3_ringbuf_len(&ctx->dtable));
+
+ relidx = (size_t)(ctx->next_absidx - absidx - 1);
+
+ return *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, relidx);
+}
+
+nghttp3_qpack_entry *
+nghttp3_qpack_context_dtable_top(nghttp3_qpack_context *ctx) {
+ assert(nghttp3_ringbuf_len(&ctx->dtable));
+ return *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, 0);
+}
+
+void nghttp3_qpack_entry_init(nghttp3_qpack_entry *ent, nghttp3_qpack_nv *qnv,
+ size_t sum, uint64_t absidx, uint32_t hash) {
+ ent->nv = *qnv;
+ ent->map_next = NULL;
+ ent->sum = sum;
+ ent->absidx = absidx;
+ ent->hash = hash;
+
+ nghttp3_rcbuf_incref(ent->nv.name);
+ nghttp3_rcbuf_incref(ent->nv.value);
+}
+
+void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent) {
+ nghttp3_rcbuf_decref(ent->nv.value);
+ nghttp3_rcbuf_decref(ent->nv.name);
+}
+
+int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream) {
+ nghttp3_blocked_streams_key bsk = {
+ nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
+ nghttp3_qpack_header_block_ref, max_cnts_pe)
+ ->max_cnt,
+ (uint64_t)stream->stream_id};
+
+ return nghttp3_ksl_insert(&encoder->blocked_streams, NULL, &bsk, stream);
+}
+
+void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream) {
+ nghttp3_blocked_streams_key bsk = {
+ nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
+ nghttp3_qpack_header_block_ref, max_cnts_pe)
+ ->max_cnt,
+ (uint64_t)stream->stream_id};
+ nghttp3_ksl_it it;
+
+ /* This is purely debugging purpose only */
+ it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, &bsk);
+
+ assert(!nghttp3_ksl_it_end(&it));
+ assert(nghttp3_ksl_it_get(&it) == stream);
+
+ nghttp3_ksl_remove_hint(&encoder->blocked_streams, NULL, &it, &bsk);
+}
+
+void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder,
+ uint64_t max_cnt) {
+ nghttp3_blocked_streams_key bsk = {max_cnt, 0};
+ nghttp3_ksl_it it;
+
+ it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, &bsk);
+
+ for (; !nghttp3_ksl_it_end(&it);) {
+ bsk = *(nghttp3_blocked_streams_key *)nghttp3_ksl_it_key(&it);
+ nghttp3_ksl_remove_hint(&encoder->blocked_streams, &it, &it, &bsk);
+ }
+}
+
+int nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id) {
+ nghttp3_qpack_stream *stream =
+ nghttp3_qpack_encoder_find_stream(encoder, stream_id);
+ const nghttp3_mem *mem = encoder->ctx.mem;
+ nghttp3_qpack_header_block_ref *ref;
+
+ if (stream == NULL) {
+ return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR;
+ }
+
+ assert(nghttp3_ringbuf_len(&stream->refs));
+
+ ref =
+ *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0);
+
+ DEBUGF("qpack::encoder: Header acknowledgement stream=%ld ricnt=%" PRIu64
+ " krcnt=%" PRIu64 "\n",
+ stream_id, ref->max_cnt, encoder->krcnt);
+
+ if (encoder->krcnt < ref->max_cnt) {
+ encoder->krcnt = ref->max_cnt;
+
+ nghttp3_qpack_encoder_unblock(encoder, ref->max_cnt);
+ }
+
+ nghttp3_qpack_stream_pop_ref(stream);
+
+ assert(ref->min_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX);
+
+ nghttp3_pq_remove(&encoder->min_cnts, &ref->min_cnts_pe);
+
+ nghttp3_qpack_header_block_ref_del(ref, mem);
+
+ if (nghttp3_ringbuf_len(&stream->refs)) {
+ return 0;
+ }
+
+ qpack_encoder_remove_stream(encoder, stream);
+
+ nghttp3_qpack_stream_del(stream, mem);
+
+ return 0;
+}
+
+int nghttp3_qpack_encoder_add_icnt(nghttp3_qpack_encoder *encoder, uint64_t n) {
+ if (n == 0 || encoder->ctx.next_absidx - encoder->krcnt < n) {
+ return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR;
+ }
+ encoder->krcnt += n;
+
+ nghttp3_qpack_encoder_unblock(encoder, encoder->krcnt);
+
+ return 0;
+}
+
+void nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder) {
+ encoder->krcnt = encoder->ctx.next_absidx;
+
+ nghttp3_ksl_clear(&encoder->blocked_streams);
+ nghttp3_pq_clear(&encoder->min_cnts);
+ nghttp3_map_each_free(&encoder->streams, map_stream_free,
+ (void *)encoder->ctx.mem);
+ nghttp3_map_clear(&encoder->streams);
+}
+
+void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id) {
+ nghttp3_qpack_stream *stream =
+ nghttp3_qpack_encoder_find_stream(encoder, stream_id);
+ const nghttp3_mem *mem = encoder->ctx.mem;
+
+ if (stream == NULL) {
+ return;
+ }
+
+ if (nghttp3_qpack_encoder_stream_is_blocked(encoder, stream)) {
+ nghttp3_qpack_encoder_unblock_stream(encoder, stream);
+ }
+
+ qpack_encoder_remove_stream(encoder, stream);
+
+ nghttp3_qpack_stream_del(stream, mem);
+}
+
+size_t
+nghttp3_qpack_encoder_get_num_blocked_streams(nghttp3_qpack_encoder *encoder) {
+ return nghttp3_ksl_len(&encoder->blocked_streams);
+}
+
+int nghttp3_qpack_encoder_write_field_section_prefix(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, uint64_t ricnt,
+ uint64_t base) {
+ size_t max_ents =
+ encoder->ctx.hard_max_dtable_capacity / NGHTTP3_QPACK_ENTRY_OVERHEAD;
+ uint64_t encricnt = ricnt == 0 ? 0 : (ricnt % (2 * max_ents)) + 1;
+ int sign = base < ricnt;
+ uint64_t delta_base = sign ? ricnt - base - 1 : base - ricnt;
+ size_t len = nghttp3_qpack_put_varint_len(encricnt, 8) +
+ nghttp3_qpack_put_varint_len(delta_base, 7);
+ uint8_t *p;
+ int rv;
+
+ DEBUGF("qpack::encode: ricnt=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64 "\n",
+ ricnt, base, encoder->ctx.next_absidx);
+
+ rv = reserve_buf(pbuf, len, encoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = pbuf->last;
+
+ p = nghttp3_qpack_put_varint(p, encricnt, 8);
+ if (sign) {
+ *p = 0x80;
+ } else {
+ *p = 0;
+ }
+ p = nghttp3_qpack_put_varint(p, delta_base, 7);
+
+ assert((size_t)(p - pbuf->last) == len);
+
+ pbuf->last = p;
+
+ return 0;
+}
+
+/*
+ * qpack_read_varint reads |rstate->prefix| prefixed integer stored
+ * from |begin|. The |end| represents the 1 beyond the last of the
+ * valid contiguous memory region from |begin|. The decoded integer
+ * must be less than or equal to NGHTTP3_QPACK_INT_MAX.
+ *
+ * If the |rstate->left| is nonzero, it is used as an initial value,
+ * and this function assumes the |begin| starts with intermediate
+ * data. |rstate->shift| is used as initial integer shift.
+ *
+ * If an entire integer is decoded successfully, the |*fin| is set to
+ * nonzero.
+ *
+ * This function stores the decoded integer in |rstate->left| if it
+ * succeeds, including partial decoding (in this case, number of shift
+ * to make in the next call will be stored in |rstate->shift|) and
+ * returns number of bytes processed, or returns negative error code
+ * NGHTTP3_ERR_QPACK_FATAL, indicating decoding error.
+ */
+static nghttp3_ssize qpack_read_varint(int *fin,
+ nghttp3_qpack_read_state *rstate,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ uint64_t k = (uint8_t)((1 << rstate->prefix) - 1);
+ uint64_t n = rstate->left;
+ uint64_t add;
+ const uint8_t *p = begin;
+ size_t shift = rstate->shift;
+
+ rstate->shift = 0;
+ *fin = 0;
+
+ if (n == 0) {
+ if (((*p) & k) != k) {
+ rstate->left = (*p) & k;
+ *fin = 1;
+ return 1;
+ }
+
+ n = k;
+
+ if (++p == end) {
+ rstate->left = n;
+ return (nghttp3_ssize)(p - begin);
+ }
+ }
+
+ for (; p != end; ++p, shift += 7) {
+ add = (*p) & 0x7f;
+
+ if (shift > 62) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ if ((NGHTTP3_QPACK_INT_MAX >> shift) < add) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ add <<= shift;
+
+ if (NGHTTP3_QPACK_INT_MAX - add < n) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ n += add;
+
+ if (((*p) & (1 << 7)) == 0) {
+ break;
+ }
+ }
+
+ rstate->shift = shift;
+
+ if (p == end) {
+ rstate->left = n;
+ return (nghttp3_ssize)(p - begin);
+ }
+
+ rstate->left = n;
+ *fin = 1;
+ return (nghttp3_ssize)(p + 1 - begin);
+}
+
+nghttp3_ssize nghttp3_qpack_encoder_read_decoder(nghttp3_qpack_encoder *encoder,
+ const uint8_t *src,
+ size_t srclen) {
+ const uint8_t *p = src, *end;
+ int rv;
+ nghttp3_ssize nread;
+ int rfin;
+
+ if (encoder->ctx.bad) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ if (srclen == 0) {
+ return 0;
+ }
+
+ end = src + srclen;
+
+ for (; p != end;) {
+ switch (encoder->state) {
+ case NGHTTP3_QPACK_DS_STATE_OPCODE:
+ if ((*p) & 0x80) {
+ DEBUGF("qpack::encode: OPCODE_SECTION_ACK\n");
+ encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK;
+ encoder->rstate.prefix = 7;
+ } else if ((*p) & 0x40) {
+ DEBUGF("qpack::encode: OPCODE_STREAM_CANCEL\n");
+ encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL;
+ encoder->rstate.prefix = 6;
+ } else {
+ DEBUGF("qpack::encode: OPCODE_ICNT_INCREMENT\n");
+ encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT;
+ encoder->rstate.prefix = 6;
+ }
+ encoder->state = NGHTTP3_QPACK_DS_STATE_READ_NUMBER;
+ /* fall through */
+ case NGHTTP3_QPACK_DS_STATE_READ_NUMBER:
+ nread = qpack_read_varint(&rfin, &encoder->rstate, p, end);
+ if (nread < 0) {
+ assert(nread == NGHTTP3_ERR_QPACK_FATAL);
+ rv = NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ return p - src;
+ }
+
+ switch (encoder->opcode) {
+ case NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT:
+ rv = nghttp3_qpack_encoder_add_icnt(encoder, encoder->rstate.left);
+ if (rv != 0) {
+ goto fail;
+ }
+ break;
+ case NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK:
+ rv = nghttp3_qpack_encoder_ack_header(encoder,
+ (int64_t)encoder->rstate.left);
+ if (rv != 0) {
+ goto fail;
+ }
+ break;
+ case NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL:
+ nghttp3_qpack_encoder_cancel_stream(encoder,
+ (int64_t)encoder->rstate.left);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ encoder->state = NGHTTP3_QPACK_DS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&encoder->rstate);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+ }
+
+ return p - src;
+
+fail:
+ encoder->ctx.bad = 1;
+ return rv;
+}
+
+size_t nghttp3_qpack_put_varint_len(uint64_t n, size_t prefix) {
+ size_t k = (size_t)((1 << prefix) - 1);
+ size_t len = 0;
+
+ if (n < k) {
+ return 1;
+ }
+
+ n -= k;
+ ++len;
+
+ for (; n >= 128; n >>= 7, ++len)
+ ;
+
+ return len + 1;
+}
+
+uint8_t *nghttp3_qpack_put_varint(uint8_t *buf, uint64_t n, size_t prefix) {
+ size_t k = (size_t)((1 << prefix) - 1);
+
+ *buf = (uint8_t)(*buf & ~k);
+
+ if (n < k) {
+ *buf = (uint8_t)(*buf | n);
+ return buf + 1;
+ }
+
+ *buf = (uint8_t)(*buf | k);
+ ++buf;
+
+ n -= k;
+
+ for (; n >= 128; n >>= 7) {
+ *buf++ = (uint8_t)((1 << 7) | (n & 0x7f));
+ }
+
+ *buf++ = (uint8_t)n;
+
+ return buf;
+}
+
+void nghttp3_qpack_read_state_free(nghttp3_qpack_read_state *rstate) {
+ nghttp3_rcbuf_decref(rstate->value);
+ nghttp3_rcbuf_decref(rstate->name);
+}
+
+void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate) {
+ rstate->name = NULL;
+ rstate->value = NULL;
+ nghttp3_buf_init(&rstate->namebuf);
+ nghttp3_buf_init(&rstate->valuebuf);
+ rstate->left = 0;
+ rstate->prefix = 0;
+ rstate->shift = 0;
+ rstate->absidx = 0;
+ rstate->never = 0;
+ rstate->dynamic = 0;
+ rstate->huffman_encoded = 0;
+}
+
+int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder,
+ size_t hard_max_dtable_capacity,
+ size_t max_blocked_streams,
+ const nghttp3_mem *mem) {
+ int rv;
+
+ rv = qpack_context_init(&decoder->ctx, hard_max_dtable_capacity,
+ max_blocked_streams, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
+ decoder->opcode = 0;
+ decoder->written_icnt = 0;
+ decoder->max_concurrent_streams = 0;
+
+ nghttp3_qpack_read_state_reset(&decoder->rstate);
+ nghttp3_buf_init(&decoder->dbuf);
+
+ return 0;
+}
+
+void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder) {
+ nghttp3_buf_free(&decoder->dbuf, decoder->ctx.mem);
+ nghttp3_qpack_read_state_free(&decoder->rstate);
+ qpack_context_free(&decoder->ctx);
+}
+
+/*
+ * qpack_read_huffman_string decodes huffman string in buffer [begin,
+ * end) and writes the decoded string to |dest|. This function
+ * assumes the buffer pointed by |dest| has enough space.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_FATAL
+ * Could not decode huffman string.
+ */
+static nghttp3_ssize qpack_read_huffman_string(nghttp3_qpack_read_state *rstate,
+ nghttp3_buf *dest,
+ const uint8_t *begin,
+ const uint8_t *end) {
+ nghttp3_ssize nwrite;
+ size_t len = (size_t)(end - begin);
+ int fin = 0;
+
+ if (len >= rstate->left) {
+ len = (size_t)rstate->left;
+ fin = 1;
+ }
+
+ nwrite = nghttp3_qpack_huffman_decode(&rstate->huffman_ctx, dest->last, begin,
+ len, fin);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ if (nghttp3_qpack_huffman_decode_failure_state(&rstate->huffman_ctx)) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ dest->last += nwrite;
+ rstate->left -= len;
+ return (nghttp3_ssize)len;
+}
+
+static nghttp3_ssize qpack_read_string(nghttp3_qpack_read_state *rstate,
+ nghttp3_buf *dest, const uint8_t *begin,
+ const uint8_t *end) {
+ size_t len = (size_t)(end - begin);
+ size_t n = (size_t)nghttp3_min((uint64_t)len, rstate->left);
+
+ dest->last = nghttp3_cpymem(dest->last, begin, n);
+
+ rstate->left -= n;
+ return (nghttp3_ssize)n;
+}
+
+/*
+ * qpack_decoder_validate_index checks rstate->absidx is acceptable.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGHTTP3_ERR_QPACK_FATAL
+ * rstate->absidx is invalid.
+ */
+static int qpack_decoder_validate_index(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_read_state *rstate) {
+ if (rstate->dynamic) {
+ return rstate->absidx < decoder->ctx.next_absidx &&
+ decoder->ctx.next_absidx - rstate->absidx - 1 <
+ nghttp3_ringbuf_len(&decoder->ctx.dtable)
+ ? 0
+ : NGHTTP3_ERR_QPACK_FATAL;
+ }
+ return rstate->absidx < nghttp3_arraylen(stable) ? 0
+ : NGHTTP3_ERR_QPACK_FATAL;
+}
+
+static void qpack_read_state_check_huffman(nghttp3_qpack_read_state *rstate,
+ const uint8_t b) {
+ rstate->huffman_encoded = (b & (1 << rstate->prefix)) != 0;
+}
+
+static void qpack_read_state_terminate_name(nghttp3_qpack_read_state *rstate) {
+ *rstate->namebuf.last = '\0';
+ rstate->name->len = nghttp3_buf_len(&rstate->namebuf);
+}
+
+static void qpack_read_state_terminate_value(nghttp3_qpack_read_state *rstate) {
+ *rstate->valuebuf.last = '\0';
+ rstate->value->len = nghttp3_buf_len(&rstate->valuebuf);
+}
+
+nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder,
+ const uint8_t *src,
+ size_t srclen) {
+ const uint8_t *p = src, *end;
+ int rv;
+ int busy = 0;
+ const nghttp3_mem *mem = decoder->ctx.mem;
+ nghttp3_ssize nread;
+ int rfin;
+
+ if (decoder->ctx.bad) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ if (srclen == 0) {
+ return 0;
+ }
+
+ end = src + srclen;
+
+ for (; p != end || busy;) {
+ busy = 0;
+ switch (decoder->state) {
+ case NGHTTP3_QPACK_ES_STATE_OPCODE:
+ if ((*p) & 0x80) {
+ DEBUGF("qpack::decode: OPCODE_INSERT_INDEXED\n");
+ decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED;
+ decoder->rstate.dynamic = !((*p) & 0x40);
+ decoder->rstate.prefix = 6;
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX;
+ } else if ((*p) & 0x40) {
+ DEBUGF("qpack::decode: OPCODE_INSERT\n");
+ decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_INSERT;
+ decoder->rstate.dynamic = 0;
+ decoder->rstate.prefix = 5;
+ decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN;
+ } else if ((*p) & 0x20) {
+ DEBUGF("qpack::decode: OPCODE_SET_DTABLE_TABLE_CAP\n");
+ decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP;
+ decoder->rstate.prefix = 5;
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX;
+ } else if (!((*p) & 0x20)) {
+ DEBUGF("qpack::decode: OPCODE_DUPLICATE\n");
+ decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_DUPLICATE;
+ decoder->rstate.dynamic = 1;
+ decoder->rstate.prefix = 5;
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX;
+ } else {
+ DEBUGF("qpack::decode: unknown opcode %02x\n", *p);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+ break;
+ case NGHTTP3_QPACK_ES_STATE_READ_INDEX:
+ nread = qpack_read_varint(&rfin, &decoder->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ return p - src;
+ }
+
+ if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP) {
+ DEBUGF("qpack::decode: Set dtable capacity to %" PRIu64 "\n",
+ decoder->rstate.left);
+ rv = nghttp3_qpack_decoder_set_max_dtable_capacity(
+ decoder, (size_t)decoder->rstate.left);
+ if (rv != 0) {
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&decoder->rstate);
+ break;
+ }
+
+ rv = nghttp3_qpack_decoder_rel2abs(decoder, &decoder->rstate);
+ if (rv < 0) {
+ goto fail;
+ }
+
+ switch (decoder->opcode) {
+ case NGHTTP3_QPACK_ES_OPCODE_DUPLICATE:
+ rv = nghttp3_qpack_decoder_dtable_duplicate_add(decoder);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&decoder->rstate);
+
+ break;
+ case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED:
+ decoder->rstate.prefix = 7;
+ decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN;
+
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ break;
+ case NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN:
+ qpack_read_state_check_huffman(&decoder->rstate, *p);
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAMELEN;
+ decoder->rstate.left = 0;
+ decoder->rstate.shift = 0;
+ /* Fall through */
+ case NGHTTP3_QPACK_ES_STATE_READ_NAMELEN:
+ nread = qpack_read_varint(&rfin, &decoder->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ return p - src;
+ }
+
+ if (decoder->rstate.left > NGHTTP3_QPACK_MAX_NAMELEN) {
+ rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE;
+ goto fail;
+ }
+
+ if (decoder->rstate.huffman_encoded) {
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN;
+ nghttp3_qpack_huffman_decode_context_init(&decoder->rstate.huffman_ctx);
+ rv = nghttp3_rcbuf_new(&decoder->rstate.name,
+ (size_t)decoder->rstate.left * 2 + 1, mem);
+ } else {
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAME;
+ rv = nghttp3_rcbuf_new(&decoder->rstate.name,
+ (size_t)decoder->rstate.left + 1, mem);
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ nghttp3_buf_wrap_init(&decoder->rstate.namebuf,
+ decoder->rstate.name->base,
+ decoder->rstate.name->len);
+ break;
+ case NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN:
+ nread = qpack_read_huffman_string(&decoder->rstate,
+ &decoder->rstate.namebuf, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (decoder->rstate.left) {
+ return p - src;
+ }
+
+ qpack_read_state_terminate_name(&decoder->rstate);
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN;
+ decoder->rstate.prefix = 7;
+ break;
+ case NGHTTP3_QPACK_ES_STATE_READ_NAME:
+ nread =
+ qpack_read_string(&decoder->rstate, &decoder->rstate.namebuf, p, end);
+ if (nread < 0) {
+ rv = (int)nread;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (decoder->rstate.left) {
+ return p - src;
+ }
+
+ qpack_read_state_terminate_name(&decoder->rstate);
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN;
+ decoder->rstate.prefix = 7;
+ break;
+ case NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN:
+ qpack_read_state_check_huffman(&decoder->rstate, *p);
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUELEN;
+ decoder->rstate.left = 0;
+ decoder->rstate.shift = 0;
+ /* Fall through */
+ case NGHTTP3_QPACK_ES_STATE_READ_VALUELEN:
+ nread = qpack_read_varint(&rfin, &decoder->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ return p - src;
+ }
+
+ if (decoder->rstate.left > NGHTTP3_QPACK_MAX_VALUELEN) {
+ rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE;
+ goto fail;
+ }
+
+ if (decoder->rstate.huffman_encoded) {
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN;
+ nghttp3_qpack_huffman_decode_context_init(&decoder->rstate.huffman_ctx);
+ rv = nghttp3_rcbuf_new(&decoder->rstate.value,
+ (size_t)decoder->rstate.left * 2 + 1, mem);
+ } else {
+ decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUE;
+ rv = nghttp3_rcbuf_new(&decoder->rstate.value,
+ (size_t)decoder->rstate.left + 1, mem);
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ nghttp3_buf_wrap_init(&decoder->rstate.valuebuf,
+ decoder->rstate.value->base,
+ decoder->rstate.value->len);
+
+ /* value might be 0 length */
+ busy = 1;
+ break;
+ case NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN:
+ nread = qpack_read_huffman_string(&decoder->rstate,
+ &decoder->rstate.valuebuf, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (decoder->rstate.left) {
+ return p - src;
+ }
+
+ qpack_read_state_terminate_value(&decoder->rstate);
+
+ switch (decoder->opcode) {
+ case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED:
+ rv = nghttp3_qpack_decoder_dtable_indexed_add(decoder);
+ break;
+ case NGHTTP3_QPACK_ES_OPCODE_INSERT:
+ rv = nghttp3_qpack_decoder_dtable_literal_add(decoder);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&decoder->rstate);
+ break;
+ case NGHTTP3_QPACK_ES_STATE_READ_VALUE:
+ nread = qpack_read_string(&decoder->rstate, &decoder->rstate.valuebuf, p,
+ end);
+ if (nread < 0) {
+ rv = (int)nread;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (decoder->rstate.left) {
+ return p - src;
+ }
+
+ qpack_read_state_terminate_value(&decoder->rstate);
+
+ switch (decoder->opcode) {
+ case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED:
+ rv = nghttp3_qpack_decoder_dtable_indexed_add(decoder);
+ break;
+ case NGHTTP3_QPACK_ES_OPCODE_INSERT:
+ rv = nghttp3_qpack_decoder_dtable_literal_add(decoder);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&decoder->rstate);
+ break;
+ }
+ }
+
+ return p - src;
+
+fail:
+ decoder->ctx.bad = 1;
+ return rv;
+}
+
+int nghttp3_qpack_decoder_set_max_dtable_capacity(
+ nghttp3_qpack_decoder *decoder, size_t max_dtable_capacity) {
+ nghttp3_qpack_entry *ent;
+ size_t i;
+ nghttp3_qpack_context *ctx = &decoder->ctx;
+ const nghttp3_mem *mem = ctx->mem;
+
+ if (max_dtable_capacity > decoder->ctx.hard_max_dtable_capacity) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ ctx->max_dtable_capacity = max_dtable_capacity;
+
+ while (ctx->dtable_size > max_dtable_capacity) {
+ i = nghttp3_ringbuf_len(&ctx->dtable);
+ assert(i);
+ ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1);
+
+ ctx->dtable_size -= table_space(ent->nv.name->len, ent->nv.value->len);
+
+ nghttp3_ringbuf_pop_back(&ctx->dtable);
+ nghttp3_qpack_entry_free(ent);
+ nghttp3_mem_free(mem, ent);
+ }
+
+ return 0;
+}
+
+int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder) {
+ DEBUGF("qpack::decode: Insert With Name Reference (%s) absidx=%" PRIu64 ": "
+ "value=%*s\n",
+ decoder->rstate.dynamic ? "dynamic" : "static", decoder->rstate.absidx,
+ (int)decoder->rstate.value->len, decoder->rstate.value->base);
+
+ if (decoder->rstate.dynamic) {
+ return nghttp3_qpack_decoder_dtable_dynamic_add(decoder);
+ }
+
+ return nghttp3_qpack_decoder_dtable_static_add(decoder);
+}
+
+int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder) {
+ nghttp3_qpack_nv qnv;
+ int rv;
+ const nghttp3_qpack_static_header *shd;
+
+ shd = &stable[decoder->rstate.absidx];
+
+ if (table_space(shd->name.len, decoder->rstate.value->len) >
+ decoder->ctx.max_dtable_capacity) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+
+ qnv.name = (nghttp3_rcbuf *)&shd->name;
+ qnv.value = decoder->rstate.value;
+ qnv.token = shd->token;
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0);
+
+ nghttp3_rcbuf_decref(qnv.value);
+
+ return rv;
+}
+
+int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder) {
+ nghttp3_qpack_nv qnv;
+ int rv;
+ nghttp3_qpack_entry *ent;
+
+ ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx);
+
+ if (table_space(ent->nv.name->len, decoder->rstate.value->len) >
+ decoder->ctx.max_dtable_capacity) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+
+ qnv.name = ent->nv.name;
+ qnv.value = decoder->rstate.value;
+ qnv.token = ent->nv.token;
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ nghttp3_rcbuf_incref(qnv.name);
+
+ rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0);
+
+ nghttp3_rcbuf_decref(qnv.value);
+ nghttp3_rcbuf_decref(qnv.name);
+
+ return rv;
+}
+
+int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder) {
+ int rv;
+ nghttp3_qpack_entry *ent;
+ nghttp3_qpack_nv qnv;
+
+ DEBUGF("qpack::decode: Insert duplicate absidx=%" PRIu64 "\n",
+ decoder->rstate.absidx);
+
+ ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx);
+
+ if (table_space(ent->nv.name->len, ent->nv.value->len) >
+ decoder->ctx.max_dtable_capacity) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+
+ qnv = ent->nv;
+ nghttp3_rcbuf_incref(qnv.name);
+ nghttp3_rcbuf_incref(qnv.value);
+
+ rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0);
+
+ nghttp3_rcbuf_decref(qnv.value);
+ nghttp3_rcbuf_decref(qnv.name);
+
+ return rv;
+}
+
+int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder) {
+ nghttp3_qpack_nv qnv;
+ int rv;
+
+ DEBUGF("qpack::decode: Insert With Literal Name: name=%*s value=%*s\n",
+ (int)decoder->rstate.name->len, decoder->rstate.name->base,
+ (int)decoder->rstate.value->len, decoder->rstate.value->base);
+
+ if (table_space(decoder->rstate.name->len, decoder->rstate.value->len) >
+ decoder->ctx.max_dtable_capacity) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+
+ qnv.name = decoder->rstate.name;
+ qnv.value = decoder->rstate.value;
+ qnv.token = qpack_lookup_token(qnv.name->base, qnv.name->len);
+ qnv.flags = NGHTTP3_NV_FLAG_NONE;
+
+ rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0);
+
+ nghttp3_rcbuf_decref(qnv.value);
+ nghttp3_rcbuf_decref(qnv.name);
+
+ return rv;
+}
+
+void nghttp3_qpack_decoder_set_max_concurrent_streams(
+ nghttp3_qpack_decoder *decoder, size_t max_concurrent_streams) {
+ decoder->max_concurrent_streams =
+ nghttp3_max(decoder->max_concurrent_streams, max_concurrent_streams);
+}
+
+void nghttp3_qpack_stream_context_init(nghttp3_qpack_stream_context *sctx,
+ int64_t stream_id,
+ const nghttp3_mem *mem) {
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+
+ sctx->mem = mem;
+ sctx->rstate.prefix = 8;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_RICNT;
+ sctx->opcode = 0;
+ sctx->stream_id = stream_id;
+ sctx->ricnt = 0;
+ sctx->dbase_sign = 0;
+ sctx->base = 0;
+}
+
+void nghttp3_qpack_stream_context_free(nghttp3_qpack_stream_context *sctx) {
+ nghttp3_qpack_read_state_free(&sctx->rstate);
+}
+
+void nghttp3_qpack_stream_context_reset(nghttp3_qpack_stream_context *sctx) {
+ nghttp3_qpack_stream_context_init(sctx, sctx->stream_id, sctx->mem);
+}
+
+uint64_t
+nghttp3_qpack_stream_context_get_ricnt(nghttp3_qpack_stream_context *sctx) {
+ return sctx->ricnt;
+}
+
+nghttp3_ssize
+nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv, uint8_t *pflags,
+ const uint8_t *src, size_t srclen, int fin) {
+ const uint8_t *p = src, *end = src ? src + srclen : src;
+ int rv;
+ int busy = 0;
+ nghttp3_ssize nread;
+ int rfin;
+ const nghttp3_mem *mem = decoder->ctx.mem;
+
+ if (decoder->ctx.bad) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ *pflags = NGHTTP3_QPACK_DECODE_FLAG_NONE;
+
+ for (; p != end || busy;) {
+ busy = 0;
+ switch (sctx->state) {
+ case NGHTTP3_QPACK_RS_STATE_RICNT:
+ nread = qpack_read_varint(&rfin, &sctx->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ goto almost_ok;
+ }
+
+ rv = nghttp3_qpack_decoder_reconstruct_ricnt(decoder, &sctx->ricnt,
+ sctx->rstate.left);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_DBASE_SIGN;
+ break;
+ case NGHTTP3_QPACK_RS_STATE_DBASE_SIGN:
+ if ((*p) & 0x80) {
+ sctx->dbase_sign = 1;
+ }
+ sctx->state = NGHTTP3_QPACK_RS_STATE_DBASE;
+ sctx->rstate.left = 0;
+ sctx->rstate.prefix = 7;
+ sctx->rstate.shift = 0;
+ /* Fall through */
+ case NGHTTP3_QPACK_RS_STATE_DBASE:
+ nread = qpack_read_varint(&rfin, &sctx->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ goto almost_ok;
+ }
+
+ if (sctx->dbase_sign) {
+ if (sctx->ricnt <= sctx->rstate.left) {
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+ sctx->base = sctx->ricnt - sctx->rstate.left - 1;
+ } else {
+ sctx->base = sctx->ricnt + sctx->rstate.left;
+ }
+
+ DEBUGF("qpack::decode: ricnt=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64
+ "\n",
+ sctx->ricnt, sctx->base, decoder->ctx.next_absidx);
+
+ if (sctx->ricnt > decoder->ctx.next_absidx) {
+ DEBUGF("qpack::decode: stream blocked\n");
+ sctx->state = NGHTTP3_QPACK_RS_STATE_BLOCKED;
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_BLOCKED;
+ return p - src;
+ }
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ sctx->rstate.left = 0;
+ sctx->rstate.shift = 0;
+ break;
+ case NGHTTP3_QPACK_RS_STATE_OPCODE:
+ assert(sctx->rstate.left == 0);
+ assert(sctx->rstate.shift == 0);
+ if ((*p) & 0x80) {
+ DEBUGF("qpack::decode: OPCODE_INDEXED\n");
+ sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED;
+ sctx->rstate.dynamic = !((*p) & 0x40);
+ sctx->rstate.prefix = 6;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX;
+ } else if ((*p) & 0x40) {
+ DEBUGF("qpack::decode: OPCODE_INDEXED_NAME\n");
+ sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME;
+ sctx->rstate.never = (*p) & 0x20;
+ sctx->rstate.dynamic = !((*p) & 0x10);
+ sctx->rstate.prefix = 4;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX;
+ } else if ((*p) & 0x20) {
+ DEBUGF("qpack::decode: OPCODE_LITERAL\n");
+ sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_LITERAL;
+ sctx->rstate.never = (*p) & 0x10;
+ sctx->rstate.dynamic = 0;
+ sctx->rstate.prefix = 3;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN;
+ } else if ((*p) & 0x10) {
+ DEBUGF("qpack::decode: OPCODE_INDEXED_PB\n");
+ sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB;
+ sctx->rstate.dynamic = 1;
+ sctx->rstate.prefix = 4;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX;
+ } else {
+ DEBUGF("qpack::decode: OPCODE_INDEXED_NAME_PB\n");
+ sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB;
+ sctx->rstate.never = (*p) & 0x08;
+ sctx->rstate.dynamic = 1;
+ sctx->rstate.prefix = 3;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX;
+ }
+ break;
+ case NGHTTP3_QPACK_RS_STATE_READ_INDEX:
+ nread = qpack_read_varint(&rfin, &sctx->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ goto almost_ok;
+ }
+
+ switch (sctx->opcode) {
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED:
+ rv = nghttp3_qpack_decoder_brel2abs(decoder, sctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ nghttp3_qpack_decoder_emit_indexed(decoder, sctx, nv);
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT;
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+
+ return p - src;
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB:
+ rv = nghttp3_qpack_decoder_pbrel2abs(decoder, sctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ nghttp3_qpack_decoder_emit_indexed(decoder, sctx, nv);
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT;
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+
+ return p - src;
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME:
+ rv = nghttp3_qpack_decoder_brel2abs(decoder, sctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ sctx->rstate.prefix = 7;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN;
+ break;
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB:
+ rv = nghttp3_qpack_decoder_pbrel2abs(decoder, sctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ sctx->rstate.prefix = 7;
+ sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN;
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+ break;
+ case NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN:
+ qpack_read_state_check_huffman(&sctx->rstate, *p);
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAMELEN;
+ sctx->rstate.left = 0;
+ sctx->rstate.shift = 0;
+ /* Fall through */
+ case NGHTTP3_QPACK_RS_STATE_READ_NAMELEN:
+ nread = qpack_read_varint(&rfin, &sctx->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ goto almost_ok;
+ }
+
+ if (sctx->rstate.left > NGHTTP3_QPACK_MAX_NAMELEN) {
+ rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE;
+ goto fail;
+ }
+
+ if (sctx->rstate.huffman_encoded) {
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN;
+ nghttp3_qpack_huffman_decode_context_init(&sctx->rstate.huffman_ctx);
+ rv = nghttp3_rcbuf_new(&sctx->rstate.name,
+ (size_t)sctx->rstate.left * 2 + 1, mem);
+ } else {
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAME;
+ rv = nghttp3_rcbuf_new(&sctx->rstate.name,
+ (size_t)sctx->rstate.left + 1, mem);
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ nghttp3_buf_wrap_init(&sctx->rstate.namebuf, sctx->rstate.name->base,
+ sctx->rstate.name->len);
+ break;
+ case NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN:
+ nread = qpack_read_huffman_string(&sctx->rstate, &sctx->rstate.namebuf, p,
+ end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (sctx->rstate.left) {
+ goto almost_ok;
+ }
+
+ qpack_read_state_terminate_name(&sctx->rstate);
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN;
+ sctx->rstate.prefix = 7;
+ break;
+ case NGHTTP3_QPACK_RS_STATE_READ_NAME:
+ nread = qpack_read_string(&sctx->rstate, &sctx->rstate.namebuf, p, end);
+ if (nread < 0) {
+ rv = (int)nread;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (sctx->rstate.left) {
+ goto almost_ok;
+ }
+
+ qpack_read_state_terminate_name(&sctx->rstate);
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN;
+ sctx->rstate.prefix = 7;
+ break;
+ case NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN:
+ qpack_read_state_check_huffman(&sctx->rstate, *p);
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUELEN;
+ sctx->rstate.left = 0;
+ sctx->rstate.shift = 0;
+ /* Fall through */
+ case NGHTTP3_QPACK_RS_STATE_READ_VALUELEN:
+ nread = qpack_read_varint(&rfin, &sctx->rstate, p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (!rfin) {
+ goto almost_ok;
+ }
+
+ if (sctx->rstate.left > NGHTTP3_QPACK_MAX_VALUELEN) {
+ rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE;
+ goto fail;
+ }
+
+ if (sctx->rstate.huffman_encoded) {
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN;
+ nghttp3_qpack_huffman_decode_context_init(&sctx->rstate.huffman_ctx);
+ rv = nghttp3_rcbuf_new(&sctx->rstate.value,
+ (size_t)sctx->rstate.left * 2 + 1, mem);
+ } else {
+ sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUE;
+ rv = nghttp3_rcbuf_new(&sctx->rstate.value,
+ (size_t)sctx->rstate.left + 1, mem);
+ }
+ if (rv != 0) {
+ goto fail;
+ }
+
+ nghttp3_buf_wrap_init(&sctx->rstate.valuebuf, sctx->rstate.value->base,
+ sctx->rstate.value->len);
+
+ /* value might be 0 length */
+ busy = 1;
+ break;
+ case NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN:
+ nread = qpack_read_huffman_string(&sctx->rstate, &sctx->rstate.valuebuf,
+ p, end);
+ if (nread < 0) {
+ assert(NGHTTP3_ERR_QPACK_FATAL == nread);
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (sctx->rstate.left) {
+ goto almost_ok;
+ }
+
+ qpack_read_state_terminate_value(&sctx->rstate);
+
+ switch (sctx->opcode) {
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME:
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB:
+ rv = nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ break;
+ case NGHTTP3_QPACK_RS_OPCODE_LITERAL:
+ nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT;
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+
+ return p - src;
+ case NGHTTP3_QPACK_RS_STATE_READ_VALUE:
+ nread = qpack_read_string(&sctx->rstate, &sctx->rstate.valuebuf, p, end);
+ if (nread < 0) {
+ rv = (int)nread;
+ goto fail;
+ }
+
+ p += nread;
+
+ if (sctx->rstate.left) {
+ goto almost_ok;
+ }
+
+ qpack_read_state_terminate_value(&sctx->rstate);
+
+ switch (sctx->opcode) {
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME:
+ case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB:
+ rv = nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ break;
+ case NGHTTP3_QPACK_RS_OPCODE_LITERAL:
+ nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv);
+ break;
+ default:
+ nghttp3_unreachable();
+ }
+
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT;
+
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+
+ return p - src;
+ case NGHTTP3_QPACK_RS_STATE_BLOCKED:
+ if (sctx->ricnt > decoder->ctx.next_absidx) {
+ DEBUGF("qpack::decode: stream still blocked\n");
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_BLOCKED;
+ return p - src;
+ }
+ sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE;
+ nghttp3_qpack_read_state_reset(&sctx->rstate);
+ break;
+ }
+ }
+
+almost_ok:
+ if (fin) {
+ if (sctx->state != NGHTTP3_QPACK_RS_STATE_OPCODE) {
+ rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ goto fail;
+ }
+
+ *pflags |= NGHTTP3_QPACK_DECODE_FLAG_FINAL;
+
+ if (sctx->ricnt) {
+ rv = nghttp3_qpack_decoder_write_section_ack(decoder, sctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ }
+ }
+
+ return p - src;
+
+fail:
+ decoder->ctx.bad = 1;
+ return rv;
+}
+
+static int qpack_decoder_dbuf_overflow(nghttp3_qpack_decoder *decoder) {
+ size_t limit = nghttp3_max(decoder->max_concurrent_streams, 100);
+ /* 10 = nghttp3_qpack_put_varint_len((1ULL << 62) - 1, 2)) */
+ return nghttp3_buf_len(&decoder->dbuf) > limit * 2 * 10;
+}
+
+int nghttp3_qpack_decoder_write_section_ack(
+ nghttp3_qpack_decoder *decoder, const nghttp3_qpack_stream_context *sctx) {
+ nghttp3_buf *dbuf = &decoder->dbuf;
+ uint8_t *p;
+ int rv;
+
+ if (qpack_decoder_dbuf_overflow(decoder)) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ rv = reserve_buf_small(
+ dbuf, nghttp3_qpack_put_varint_len((uint64_t)sctx->stream_id, 7),
+ decoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = dbuf->last;
+ *p = 0x80;
+ dbuf->last = nghttp3_qpack_put_varint(p, (uint64_t)sctx->stream_id, 7);
+
+ if (decoder->written_icnt < sctx->ricnt) {
+ decoder->written_icnt = sctx->ricnt;
+ }
+
+ return 0;
+}
+
+size_t
+nghttp3_qpack_decoder_get_decoder_streamlen(nghttp3_qpack_decoder *decoder) {
+ uint64_t n;
+ size_t len = 0;
+
+ if (decoder->written_icnt < decoder->ctx.next_absidx) {
+ n = decoder->ctx.next_absidx - decoder->written_icnt;
+ len = nghttp3_qpack_put_varint_len(n, 6);
+ }
+
+ return nghttp3_buf_len(&decoder->dbuf) + len;
+}
+
+void nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder,
+ nghttp3_buf *dbuf) {
+ uint8_t *p;
+ uint64_t n = 0;
+ size_t len = 0;
+ (void)len;
+
+ if (decoder->written_icnt < decoder->ctx.next_absidx) {
+ n = decoder->ctx.next_absidx - decoder->written_icnt;
+ len = nghttp3_qpack_put_varint_len(n, 6);
+ }
+
+ assert(nghttp3_buf_left(dbuf) >= nghttp3_buf_len(&decoder->dbuf) + len);
+
+ if (nghttp3_buf_len(&decoder->dbuf)) {
+ dbuf->last = nghttp3_cpymem(dbuf->last, decoder->dbuf.pos,
+ nghttp3_buf_len(&decoder->dbuf));
+ }
+
+ if (n) {
+ p = dbuf->last;
+ *p = 0;
+ dbuf->last = nghttp3_qpack_put_varint(p, n, 6);
+
+ decoder->written_icnt = decoder->ctx.next_absidx;
+ }
+
+ nghttp3_buf_reset(&decoder->dbuf);
+}
+
+int nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder,
+ int64_t stream_id) {
+ uint8_t *p;
+ int rv;
+
+ if (qpack_decoder_dbuf_overflow(decoder)) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ rv = reserve_buf(&decoder->dbuf,
+ nghttp3_qpack_put_varint_len((uint64_t)stream_id, 6),
+ decoder->ctx.mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ p = decoder->dbuf.last;
+ *p = 0x40;
+ decoder->dbuf.last = nghttp3_qpack_put_varint(p, (uint64_t)stream_id, 6);
+
+ return 0;
+}
+
+int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder,
+ uint64_t *dest, uint64_t encricnt) {
+ uint64_t max_ents, full, max, max_wrapped, ricnt;
+
+ if (encricnt == 0) {
+ *dest = 0;
+ return 0;
+ }
+
+ max_ents =
+ decoder->ctx.hard_max_dtable_capacity / NGHTTP3_QPACK_ENTRY_OVERHEAD;
+ full = 2 * max_ents;
+
+ if (encricnt > full) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+
+ max = decoder->ctx.next_absidx + max_ents;
+ max_wrapped = max / full * full;
+ ricnt = max_wrapped + encricnt - 1;
+
+ if (ricnt > max) {
+ if (ricnt <= full) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+ ricnt -= full;
+ }
+
+ if (ricnt == 0) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+
+ *dest = ricnt;
+
+ return 0;
+}
+
+int nghttp3_qpack_decoder_rel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_read_state *rstate) {
+ DEBUGF("qpack::decode: dynamic=%d relidx=%" PRIu64 " icnt=%" PRIu64 "\n",
+ rstate->dynamic, rstate->left, decoder->ctx.next_absidx);
+
+ if (rstate->dynamic) {
+ if (decoder->ctx.next_absidx < rstate->left + 1) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+ rstate->absidx = decoder->ctx.next_absidx - rstate->left - 1;
+ } else {
+ rstate->absidx = rstate->left;
+ }
+ if (qpack_decoder_validate_index(decoder, rstate) != 0) {
+ return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR;
+ }
+ return 0;
+}
+
+int nghttp3_qpack_decoder_brel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx) {
+ nghttp3_qpack_read_state *rstate = &sctx->rstate;
+
+ DEBUGF("qpack::decode: dynamic=%d relidx=%" PRIu64 " base=%" PRIu64
+ " icnt=%" PRIu64 "\n",
+ rstate->dynamic, rstate->left, sctx->base, decoder->ctx.next_absidx);
+
+ if (rstate->dynamic) {
+ if (sctx->base < rstate->left + 1) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+ rstate->absidx = sctx->base - rstate->left - 1;
+
+ if (rstate->absidx >= sctx->ricnt) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+ } else {
+ rstate->absidx = rstate->left;
+ }
+
+ if (qpack_decoder_validate_index(decoder, rstate) != 0) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+ return 0;
+}
+
+int nghttp3_qpack_decoder_pbrel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx) {
+ nghttp3_qpack_read_state *rstate = &sctx->rstate;
+
+ DEBUGF("qpack::decode: pbidx=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64 "\n",
+ rstate->left, sctx->base, decoder->ctx.next_absidx);
+
+ assert(rstate->dynamic);
+
+ rstate->absidx = rstate->left + sctx->base;
+
+ if (rstate->absidx >= sctx->ricnt) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+
+ if (qpack_decoder_validate_index(decoder, rstate) != 0) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+ return 0;
+}
+
+static void
+qpack_decoder_emit_static_indexed(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ const nghttp3_qpack_static_header *shd = &stable[sctx->rstate.absidx];
+ (void)decoder;
+
+ nv->name = (nghttp3_rcbuf *)&shd->name;
+ nv->value = (nghttp3_rcbuf *)&shd->value;
+ nv->token = shd->token;
+ nv->flags = NGHTTP3_NV_FLAG_NONE;
+}
+
+static void
+qpack_decoder_emit_dynamic_indexed(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ nghttp3_qpack_entry *ent =
+ nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx);
+
+ *nv = ent->nv;
+
+ nghttp3_rcbuf_incref(nv->name);
+ nghttp3_rcbuf_incref(nv->value);
+}
+
+void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ DEBUGF("qpack::decode: Indexed (%s) absidx=%" PRIu64 "\n",
+ sctx->rstate.dynamic ? "dynamic" : "static", sctx->rstate.absidx);
+
+ if (sctx->rstate.dynamic) {
+ qpack_decoder_emit_dynamic_indexed(decoder, sctx, nv);
+ } else {
+ qpack_decoder_emit_static_indexed(decoder, sctx, nv);
+ }
+}
+
+static void
+qpack_decoder_emit_static_indexed_name(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ const nghttp3_qpack_static_header *shd = &stable[sctx->rstate.absidx];
+ (void)decoder;
+
+ nv->name = (nghttp3_rcbuf *)&shd->name;
+ nv->value = sctx->rstate.value;
+ nv->token = shd->token;
+ nv->flags =
+ sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE;
+
+ sctx->rstate.value = NULL;
+}
+
+static int
+qpack_decoder_emit_dynamic_indexed_name(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ nghttp3_qpack_entry *ent;
+
+ /* A broken encoder might change dtable capacity while processing
+ request stream instruction. Check the absidx again. */
+ if (qpack_decoder_validate_index(decoder, &sctx->rstate) != 0) {
+ return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED;
+ }
+
+ ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx);
+
+ nv->name = ent->nv.name;
+ nv->value = sctx->rstate.value;
+ nv->token = ent->nv.token;
+ nv->flags =
+ sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE;
+
+ nghttp3_rcbuf_incref(nv->name);
+
+ sctx->rstate.value = NULL;
+
+ return 0;
+}
+
+int nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ (void)decoder;
+
+ DEBUGF("qpack::decode: Indexed name (%s) absidx=%" PRIu64 " value=%*s\n",
+ sctx->rstate.dynamic ? "dynamic" : "static", sctx->rstate.absidx,
+ (int)sctx->rstate.value->len, sctx->rstate.value->base);
+
+ if (sctx->rstate.dynamic) {
+ return qpack_decoder_emit_dynamic_indexed_name(decoder, sctx, nv);
+ }
+
+ qpack_decoder_emit_static_indexed_name(decoder, sctx, nv);
+
+ return 0;
+}
+
+void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv) {
+ (void)decoder;
+
+ DEBUGF("qpack::decode: Emit literal name=%*s value=%*s\n",
+ (int)sctx->rstate.name->len, sctx->rstate.name->base,
+ (int)sctx->rstate.value->len, sctx->rstate.value->base);
+
+ nv->name = sctx->rstate.name;
+ nv->value = sctx->rstate.value;
+ nv->token = qpack_lookup_token(nv->name->base, nv->name->len);
+ nv->flags =
+ sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE;
+
+ sctx->rstate.name = NULL;
+ sctx->rstate.value = NULL;
+}
+
+int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder,
+ size_t hard_max_dtable_capacity,
+ const nghttp3_mem *mem) {
+ int rv;
+ nghttp3_qpack_encoder *p;
+
+ p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_encoder));
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ rv = nghttp3_qpack_encoder_init(p, hard_max_dtable_capacity, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ *pencoder = p;
+
+ return 0;
+}
+
+void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder) {
+ const nghttp3_mem *mem;
+
+ if (encoder == NULL) {
+ return;
+ }
+
+ mem = encoder->ctx.mem;
+
+ nghttp3_qpack_encoder_free(encoder);
+ nghttp3_mem_free(mem, encoder);
+}
+
+int nghttp3_qpack_stream_context_new(nghttp3_qpack_stream_context **psctx,
+ int64_t stream_id,
+ const nghttp3_mem *mem) {
+ nghttp3_qpack_stream_context *p;
+
+ p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream_context));
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ nghttp3_qpack_stream_context_init(p, stream_id, mem);
+
+ *psctx = p;
+
+ return 0;
+}
+
+void nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx) {
+ const nghttp3_mem *mem;
+
+ if (sctx == NULL) {
+ return;
+ }
+
+ mem = sctx->mem;
+
+ nghttp3_qpack_stream_context_free(sctx);
+ nghttp3_mem_free(mem, sctx);
+}
+
+int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder,
+ size_t hard_max_dtable_capacity,
+ size_t max_blocked_streams,
+ const nghttp3_mem *mem) {
+ int rv;
+ nghttp3_qpack_decoder *p;
+
+ p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_decoder));
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ rv = nghttp3_qpack_decoder_init(p, hard_max_dtable_capacity,
+ max_blocked_streams, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ *pdecoder = p;
+
+ return 0;
+}
+
+void nghttp3_qpack_decoder_del(nghttp3_qpack_decoder *decoder) {
+ const nghttp3_mem *mem;
+
+ if (decoder == NULL) {
+ return;
+ }
+
+ mem = decoder->ctx.mem;
+
+ nghttp3_qpack_decoder_free(decoder);
+ nghttp3_mem_free(mem, decoder);
+}
+
+uint64_t nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder) {
+ return decoder->ctx.next_absidx;
+}
diff --git a/lib/nghttp3_qpack.h b/lib/nghttp3_qpack.h
new file mode 100644
index 0000000..804969e
--- /dev/null
+++ b/lib/nghttp3_qpack.h
@@ -0,0 +1,996 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_QPACK_H
+#define NGHTTP3_QPACK_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_rcbuf.h"
+#include "nghttp3_map.h"
+#include "nghttp3_pq.h"
+#include "nghttp3_ringbuf.h"
+#include "nghttp3_buf.h"
+#include "nghttp3_ksl.h"
+#include "nghttp3_qpack_huffman.h"
+
+#define NGHTTP3_QPACK_INT_MAX ((1ull << 62) - 1)
+
+/* NGHTTP3_QPACK_MAX_NAMELEN is the maximum (compressed) length of
+ header name this library can decode. */
+#define NGHTTP3_QPACK_MAX_NAMELEN 256
+/* NGHTTP3_QPACK_MAX_VALUELEN is the maximum (compressed) length of
+ header value this library can decode. */
+#define NGHTTP3_QPACK_MAX_VALUELEN 65536
+
+/* nghttp3_qpack_indexing_mode is a indexing strategy. */
+typedef enum nghttp3_qpack_indexing_mode {
+ /* NGHTTP3_QPACK_INDEXING_MODE_LITERAL means that header field
+ should not be inserted into dynamic table. */
+ NGHTTP3_QPACK_INDEXING_MODE_LITERAL,
+ /* NGHTTP3_QPACK_INDEXING_MODE_STORE means that header field can be
+ inserted into dynamic table. */
+ NGHTTP3_QPACK_INDEXING_MODE_STORE,
+ /* NGHTTP3_QPACK_INDEXING_MODE_NEVER means that header field should
+ not be inserted into dynamic table and this must be true for all
+ forwarding paths. */
+ NGHTTP3_QPACK_INDEXING_MODE_NEVER,
+} nghttp3_qpack_indexing_mode;
+
+typedef struct nghttp3_qpack_entry nghttp3_qpack_entry;
+
+struct nghttp3_qpack_entry {
+ /* The header field name/value pair */
+ nghttp3_qpack_nv nv;
+ /* map_next points to the entry which shares same bucket in hash
+ table. */
+ nghttp3_qpack_entry *map_next;
+ /* sum is the sum of all entries inserted up to this entry. This
+ value does not contain the space required for this entry. */
+ size_t sum;
+ /* absidx is the absolute index of this entry. */
+ uint64_t absidx;
+ /* The hash value for header name (nv.name). */
+ uint32_t hash;
+};
+
+/* The entry used for static table. */
+typedef struct nghttp3_qpack_static_entry {
+ uint64_t absidx;
+ int32_t token;
+ uint32_t hash;
+} nghttp3_qpack_static_entry;
+
+typedef struct nghttp3_qpack_static_header {
+ nghttp3_rcbuf name;
+ nghttp3_rcbuf value;
+ int32_t token;
+} nghttp3_qpack_static_header;
+
+/*
+ * nghttp3_qpack_header_block_ref is created per encoded header block
+ * and includes the required insert count and the minimum insert count
+ * of dynamic table entry it refers to.
+ */
+typedef struct nghttp3_qpack_header_block_ref {
+ nghttp3_pq_entry max_cnts_pe;
+ nghttp3_pq_entry min_cnts_pe;
+ /* max_cnt is the required insert count. */
+ uint64_t max_cnt;
+ /* min_cnt is the minimum insert count of dynamic table entry it
+ refers to. In other words, this is the minimum absolute index of
+ dynamic header table entry this encoded block refers to plus
+ 1. */
+ uint64_t min_cnt;
+} nghttp3_qpack_header_block_ref;
+
+int nghttp3_qpack_header_block_ref_new(nghttp3_qpack_header_block_ref **pref,
+ uint64_t max_cnt, uint64_t min_cnt,
+ const nghttp3_mem *mem);
+
+void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref,
+ const nghttp3_mem *mem);
+
+typedef struct nghttp3_qpack_stream {
+ int64_t stream_id;
+ /* refs is an array of pointer to nghttp3_qpack_header_block_ref in
+ the order of the time they are encoded. HTTP/3 allows multiple
+ header blocks (e.g., non-final response headers, final response
+ headers, trailers, and push promises) per stream. */
+ nghttp3_ringbuf refs;
+ /* max_cnts is a priority queue sorted by descending order of
+ max_cnt of nghttp3_qpack_header_block_ref. */
+ nghttp3_pq max_cnts;
+} nghttp3_qpack_stream;
+
+int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id,
+ const nghttp3_mem *mem);
+
+void nghttp3_qpack_stream_del(nghttp3_qpack_stream *stream,
+ const nghttp3_mem *mem);
+
+uint64_t nghttp3_qpack_stream_get_max_cnt(const nghttp3_qpack_stream *stream);
+
+int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream,
+ nghttp3_qpack_header_block_ref *ref);
+
+void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream);
+
+#define NGHTTP3_QPACK_ENTRY_OVERHEAD 32
+
+typedef struct nghttp3_qpack_context {
+ /* dtable is a dynamic table */
+ nghttp3_ringbuf dtable;
+ /* mem is memory allocator */
+ const nghttp3_mem *mem;
+ /* dtable_size is abstracted buffer size of dtable as described in
+ the spec. This is the sum of length of name/value in dtable +
+ NGHTTP3_QPACK_ENTRY_OVERHEAD bytes overhead per each entry. */
+ size_t dtable_size;
+ size_t dtable_sum;
+ /* hard_max_dtable_capacity is the upper bound of
+ max_dtable_capacity. */
+ size_t hard_max_dtable_capacity;
+ /* max_dtable_capacity is the maximum capacity of the dynamic
+ table. */
+ size_t max_dtable_capacity;
+ /* max_blocked_streams is the maximum number of stream which can be
+ blocked. */
+ size_t max_blocked_streams;
+ /* next_absidx is the next absolute index for nghttp3_qpack_entry.
+ It is equivalent to insert count. */
+ uint64_t next_absidx;
+ /* If inflate/deflate error occurred, this value is set to 1 and
+ further invocation of inflate/deflate will fail with
+ NGHTTP3_ERR_QPACK_FATAL. */
+ uint8_t bad;
+} nghttp3_qpack_context;
+
+typedef struct nghttp3_qpack_read_state {
+ nghttp3_qpack_huffman_decode_context huffman_ctx;
+ nghttp3_buf namebuf;
+ nghttp3_buf valuebuf;
+ nghttp3_rcbuf *name;
+ nghttp3_rcbuf *value;
+ uint64_t left;
+ size_t prefix;
+ size_t shift;
+ uint64_t absidx;
+ int never;
+ int dynamic;
+ int huffman_encoded;
+} nghttp3_qpack_read_state;
+
+void nghttp3_qpack_read_state_free(nghttp3_qpack_read_state *rstate);
+
+void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate);
+
+#define NGHTTP3_QPACK_MAP_SIZE 64
+
+typedef struct nghttp3_qpack_map {
+ nghttp3_qpack_entry *table[NGHTTP3_QPACK_MAP_SIZE];
+} nghttp3_qpack_map;
+
+/* nghttp3_qpack_decoder_stream_state is a set of states when decoding
+ decoder stream. */
+typedef enum nghttp3_qpack_decoder_stream_state {
+ NGHTTP3_QPACK_DS_STATE_OPCODE,
+ NGHTTP3_QPACK_DS_STATE_READ_NUMBER,
+} nghttp3_qpack_decoder_stream_state;
+
+/* nghttp3_qpack_decoder_stream_opcode is opcode used in decoder
+ stream. */
+typedef enum nghttp3_qpack_decoder_stream_opcode {
+ NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT,
+ NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK,
+ NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL,
+} nghttp3_qpack_decoder_stream_opcode;
+
+/* QPACK encoder flags */
+
+/* NGHTTP3_QPACK_ENCODER_FLAG_NONE indicates that no flag is set. */
+#define NGHTTP3_QPACK_ENCODER_FLAG_NONE 0x00u
+/* NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP indicates that
+ Set Dynamic Table Capacity is required. */
+#define NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP 0x01u
+
+struct nghttp3_qpack_encoder {
+ nghttp3_qpack_context ctx;
+ /* dtable_map is a map of hash to nghttp3_qpack_entry to provide
+ fast access to an entry in dynamic table. */
+ nghttp3_qpack_map dtable_map;
+ /* streams is a map of stream ID to nghttp3_qpack_stream to keep
+ track of unacknowledged streams. */
+ nghttp3_map streams;
+ /* blocked_streams is an ordered list of nghttp3_qpack_stream, in
+ descending order of max_cnt, to search the unblocked streams by
+ received known count. */
+ nghttp3_ksl blocked_streams;
+ /* min_cnts is a priority queue of nghttp3_qpack_header_block_ref
+ sorted by ascending order of min_cnt to know that an entry can be
+ evicted from dynamic table. */
+ nghttp3_pq min_cnts;
+ /* krcnt is Known Received Count. */
+ uint64_t krcnt;
+ /* state is a current state of reading decoder stream. */
+ nghttp3_qpack_decoder_stream_state state;
+ /* opcode is a decoder stream opcode being processed. */
+ nghttp3_qpack_decoder_stream_opcode opcode;
+ /* rstate is a set of intermediate state which are used to process
+ decoder stream. */
+ nghttp3_qpack_read_state rstate;
+ /* min_dtable_update is the minimum dynamic table size required. */
+ size_t min_dtable_update;
+ /* last_max_dtable_update is the dynamic table size last
+ requested. */
+ size_t last_max_dtable_update;
+ /* flags is bitwise OR of zero or more of
+ NGHTTP3_QPACK_ENCODER_FLAG_*. */
+ uint8_t flags;
+};
+
+/*
+ * nghttp3_qpack_encoder_init initializes |encoder|.
+ * |hard_max_dtable_capacity| is the upper bound of the dynamic table
+ * capacity. |mem| is a memory allocator.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
+ size_t hard_max_dtable_capacity,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_qpack_encoder_free frees memory allocated for |encoder|.
+ * This function does not free memory pointed by |encoder|.
+ */
+void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder);
+
+/*
+ * nghttp3_qpack_encoder_encode_nv encodes |nv|. It writes request
+ * stream into |rbuf| and writes encoder stream into |ebuf|. |nv| is
+ * a header field to encode. |base| is base. |allow_blocking| is
+ * nonzero if this stream can be blocked (or it has been blocked
+ * already).
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder,
+ uint64_t *pmax_cnt, uint64_t *pmin_cnt,
+ nghttp3_buf *rbuf, nghttp3_buf *ebuf,
+ const nghttp3_nv *nv, uint64_t base,
+ int allow_blocking);
+
+/* nghttp3_qpack_lookup_result stores a result of table lookup. */
+typedef struct nghttp3_qpack_lookup_result {
+ /* index is an index of matched entry. -1 if no match is made. */
+ nghttp3_ssize index;
+ /* name_value_match is nonzero if both name and value are
+ matched. */
+ int name_value_match;
+ /* pb_index is the absolute index of matched post-based dynamic
+ table entry. -1 if no such entry exists. */
+ nghttp3_ssize pb_index;
+} nghttp3_qpack_lookup_result;
+
+/*
+ * nghttp3_qpack_lookup_stable searches |nv| in static table. |token|
+ * is a token of nv->name and it is -1 if there is no corresponding
+ * token defined. |indexing_mode| provides indexing strategy.
+ */
+nghttp3_qpack_lookup_result
+nghttp3_qpack_lookup_stable(const nghttp3_nv *nv, int32_t token,
+ nghttp3_qpack_indexing_mode indexing_mode);
+
+/*
+ * nghttp3_qpack_encoder_lookup_dtable searches |nv| in dynamic table.
+ * |token| is a token of nv->name and it is -1 if there is no
+ * corresponding token defined. |hash| is a hash of nv->name.
+ * |indexing_mode| provides indexing strategy. |krcnt| is Known
+ * Received Count. |allow_blocking| is nonzero if this stream can be
+ * blocked (or it has been blocked already).
+ */
+nghttp3_qpack_lookup_result nghttp3_qpack_encoder_lookup_dtable(
+ nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token,
+ uint32_t hash, nghttp3_qpack_indexing_mode indexing_mode, uint64_t krcnt,
+ int allow_blocking);
+
+/*
+ * nghttp3_qpack_encoder_write_field_section_prefix writes Encoded
+ * Field Section Prefix into |pbuf|. |ricnt| is Required Insert
+ * Count. |base| is Base.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_field_section_prefix(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, uint64_t ricnt,
+ uint64_t base);
+
+/*
+ * nghttp3_qpack_encoder_write_static_indexed writes Indexed Header
+ * Field to |rbuf|. |absidx| is an absolute index into static table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_static_indexed(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ uint64_t absidx);
+
+/*
+ * nghttp3_qpack_encoder_write_dynamic_indexed writes Indexed Header
+ * Field to |rbuf|. |absidx| is an absolute index into dynamic table.
+ * |base| is base.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_dynamic_indexed(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ uint64_t absidx, uint64_t base);
+
+/*
+ * nghttp3_qpack_encoder_write_static_indexed writes Literal Header
+ * Field With Name Reference to |rbuf|. |absidx| is an absolute index
+ * into static table to reference a name. |nv| is a header field to
+ * encode.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_static_indexed_name(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx,
+ const nghttp3_nv *nv);
+
+/*
+ * nghttp3_qpack_encoder_write_dynamic_indexed writes Literal Header
+ * Field With Name Reference to |rbuf|. |absidx| is an absolute index
+ * into dynamic table to reference a name. |base| is a base. |nv| is
+ * a header field to encode.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_dynamic_indexed_name(
+ nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx,
+ uint64_t base, const nghttp3_nv *nv);
+
+/*
+ * nghttp3_qpack_encoder_write_literal writes Literal Header Field
+ * With Literal Name to |rbuf|. |nv| is a header field to encode.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *rbuf,
+ const nghttp3_nv *nv);
+
+/*
+ * nghttp3_qpack_encoder_write_static_insert writes Insert With Name
+ * Reference to |ebuf|. |absidx| is an absolute index into static
+ * table to reference a name. |nv| is a header field to insert.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_static_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx,
+ const nghttp3_nv *nv);
+
+/*
+ * nghttp3_qpack_encoder_write_dynamic_insert writes Insert With Name
+ * Reference to |ebuf|. |absidx| is an absolute index into dynamic
+ * table to reference a name. |nv| is a header field to insert.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_dynamic_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx,
+ const nghttp3_nv *nv);
+
+/*
+ * nghttp3_qpack_encoder_write_duplicate_insert writes Duplicate to
+ * |ebuf|. |absidx| is an absolute index into dynamic table to
+ * reference an entry.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_duplicate_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ uint64_t absidx);
+
+/*
+ * nghttp3_qpack_encoder_write_literal_insert writes Insert With
+ * Literal Name to |ebuf|. |nv| is a header field to insert.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_literal_insert(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf,
+ const nghttp3_nv *nv);
+
+int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream);
+
+/*
+ * nghttp3_qpack_encoder_block_stream blocks |stream|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream);
+
+/*
+ * nghttp3_qpack_encoder_unblock_stream unblocks |stream|.
+ */
+void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder,
+ nghttp3_qpack_stream *stream);
+
+/*
+ * nghttp3_qpack_encoder_unblock unblocks stream whose max_cnt is less
+ * than or equal to |max_cnt|.
+ */
+void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder,
+ uint64_t max_cnt);
+
+/*
+ * nghttp3_qpack_encoder_find_stream returns stream whose stream ID is
+ * |stream_id|. This function returns NULL if there is no such
+ * stream.
+ */
+nghttp3_qpack_stream *
+nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id);
+
+uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder);
+
+/*
+ * nghttp3_qpack_encoder_shrink_dtable shrinks dynamic table so that
+ * the dynamic table size is less than or equal to maximum size.
+ */
+void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder);
+
+/*
+ * nghttp3_qpack_encoder_process_dtable_update processes pending
+ * dynamic table size update. It might write encoder stream into
+ * |ebuf|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf);
+
+/*
+ * nghttp3_qpack_encoder_write_set_dtable_cap writes Set Dynamic Table
+ * Capacity. to |ebuf|. |cap| is the capacity of dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder,
+ nghttp3_buf *ebuf, size_t cap);
+
+/*
+ * nghttp3_qpack_context_dtable_add adds |qnv| to dynamic table. If
+ * |ctx| is a part of encoder, |dtable_map| is not NULL. |hash| is a
+ * hash value of name.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx,
+ nghttp3_qpack_nv *qnv,
+ nghttp3_qpack_map *dtable_map,
+ uint32_t hash);
+
+/*
+ * nghttp3_qpack_encoder_dtable_static_add adds |nv| to dynamic table
+ * by referencing static table entry at an absolute index |absidx|.
+ * The hash of name is given as |hash|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_dtable_static_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx,
+ const nghttp3_nv *nv,
+ uint32_t hash);
+
+/*
+ * nghttp3_qpack_encoder_dtable_dynamic_add adds |nv| to dynamic table
+ * by referencing dynamic table entry at an absolute index |absidx|.
+ * The hash of name is given as |hash|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_dtable_dynamic_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx,
+ const nghttp3_nv *nv,
+ uint32_t hash);
+
+/*
+ * nghttp3_qpack_encoder_dtable_duplicate_add duplicates dynamic table
+ * entry at an absolute index |absidx|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_encoder_dtable_duplicate_add(nghttp3_qpack_encoder *encoder,
+ uint64_t absidx);
+
+/*
+ * nghttp3_qpack_encoder_dtable_literal_add adds |nv| to dynamic
+ * table. |token| is a token of name and is -1 if it has no token
+ * value defined. |hash| is a hash of name.
+ *
+ * NGHTTP3_ERR_NOMEM Out of memory.
+ */
+int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder,
+ const nghttp3_nv *nv,
+ int32_t token, uint32_t hash);
+
+/*
+ * `nghttp3_qpack_encoder_ack_header` tells |encoder| that header
+ * block for a stream denoted by |stream_id| was acknowledged by
+ * decoder.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR`
+ * Section Acknowledgement for a stream denoted by |stream_id| is
+ * unexpected.
+ */
+int nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id);
+
+/*
+ * `nghttp3_qpack_encoder_add_icnt` increments known received count of
+ * |encoder| by |n|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGHTTP3_ERR_NOMEM`
+ * Out of memory.
+ * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR`
+ * |n| is too large.
+ */
+int nghttp3_qpack_encoder_add_icnt(nghttp3_qpack_encoder *encoder, uint64_t n);
+
+/*
+ * `nghttp3_qpack_encoder_cancel_stream` tells |encoder| that stream
+ * denoted by |stream_id| is cancelled. This function is provided for
+ * debugging purpose only. In HTTP/3, |encoder| knows this by reading
+ * decoder stream with `nghttp3_qpack_encoder_read_decoder()`.
+ */
+void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder,
+ int64_t stream_id);
+
+/*
+ * nghttp3_qpack_context_dtable_get returns dynamic table entry whose
+ * absolute index is |absidx|. This function assumes that such entry
+ * exists.
+ */
+nghttp3_qpack_entry *
+nghttp3_qpack_context_dtable_get(nghttp3_qpack_context *ctx, uint64_t absidx);
+
+/*
+ * nghttp3_qpack_context_dtable_top returns latest dynamic table
+ * entry. This function assumes dynamic table is not empty.
+ */
+nghttp3_qpack_entry *
+nghttp3_qpack_context_dtable_top(nghttp3_qpack_context *ctx);
+
+/*
+ * nghttp3_qpack_entry_init initializes |ent|. |qnv| is a header
+ * field. |sum| is the sum of table space occupied by all entries
+ * inserted so far. It does not include this entry. |absidx| is an
+ * absolute index of this entry. |hash| is a hash of header field
+ * name. This function increases reference count of qnv->nv.name and
+ * qnv->nv.value.
+ */
+void nghttp3_qpack_entry_init(nghttp3_qpack_entry *ent, nghttp3_qpack_nv *qnv,
+ size_t sum, uint64_t absidx, uint32_t hash);
+
+/*
+ * nghttp3_qpack_entry_free frees memory allocated for |ent|.
+ */
+void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent);
+
+/*
+ * nghttp3_qpack_put_varint_len returns the required number of bytes
+ * to encode |n| with |prefix| bits.
+ */
+size_t nghttp3_qpack_put_varint_len(uint64_t n, size_t prefix);
+
+/*
+ * nghttp3_qpack_put_varint encodes |n| using variable integer
+ * encoding with |prefix| bits into |buf|. This function assumes the
+ * buffer pointed by |buf| has enough space. This function returns
+ * the one byte beyond the last write (buf +
+ * nghttp3_qpack_put_varint_len(n, prefix)).
+ */
+uint8_t *nghttp3_qpack_put_varint(uint8_t *buf, uint64_t n, size_t prefix);
+
+/* nghttp3_qpack_encoder_stream_state is a set of states for encoder
+ stream decoding. */
+typedef enum nghttp3_qpack_encoder_stream_state {
+ NGHTTP3_QPACK_ES_STATE_OPCODE,
+ NGHTTP3_QPACK_ES_STATE_READ_INDEX,
+ NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN,
+ NGHTTP3_QPACK_ES_STATE_READ_NAMELEN,
+ NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN,
+ NGHTTP3_QPACK_ES_STATE_READ_NAME,
+ NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN,
+ NGHTTP3_QPACK_ES_STATE_READ_VALUELEN,
+ NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN,
+ NGHTTP3_QPACK_ES_STATE_READ_VALUE,
+} nghttp3_qpack_encoder_stream_state;
+
+/* nghttp3_qpack_encoder_stream_opcode is a set of opcodes used in
+ encoder stream. */
+typedef enum nghttp3_qpack_encoder_stream_opcode {
+ NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED,
+ NGHTTP3_QPACK_ES_OPCODE_INSERT,
+ NGHTTP3_QPACK_ES_OPCODE_DUPLICATE,
+ NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP,
+} nghttp3_qpack_encoder_stream_opcode;
+
+/* nghttp3_qpack_request_stream_state is a set of states for request
+ stream decoding. */
+typedef enum nghttp3_qpack_request_stream_state {
+ NGHTTP3_QPACK_RS_STATE_RICNT,
+ NGHTTP3_QPACK_RS_STATE_DBASE_SIGN,
+ NGHTTP3_QPACK_RS_STATE_DBASE,
+ NGHTTP3_QPACK_RS_STATE_OPCODE,
+ NGHTTP3_QPACK_RS_STATE_READ_INDEX,
+ NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN,
+ NGHTTP3_QPACK_RS_STATE_READ_NAMELEN,
+ NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN,
+ NGHTTP3_QPACK_RS_STATE_READ_NAME,
+ NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN,
+ NGHTTP3_QPACK_RS_STATE_READ_VALUELEN,
+ NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN,
+ NGHTTP3_QPACK_RS_STATE_READ_VALUE,
+ NGHTTP3_QPACK_RS_STATE_BLOCKED,
+} nghttp3_qpack_request_stream_state;
+
+/* nghttp3_qpack_request_stream_opcode is a set of opcodes used in
+ request stream. */
+typedef enum nghttp3_qpack_request_stream_opcode {
+ NGHTTP3_QPACK_RS_OPCODE_INDEXED,
+ NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB,
+ NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME,
+ NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB,
+ NGHTTP3_QPACK_RS_OPCODE_LITERAL,
+} nghttp3_qpack_request_stream_opcode;
+
+struct nghttp3_qpack_decoder {
+ nghttp3_qpack_context ctx;
+ /* state is a current state of reading encoder stream. */
+ nghttp3_qpack_encoder_stream_state state;
+ /* opcode is an encoder stream opcode being processed. */
+ nghttp3_qpack_encoder_stream_opcode opcode;
+ /* rstate is a set of intermediate state which are used to process
+ encoder stream. */
+ nghttp3_qpack_read_state rstate;
+ /* dbuf is decoder stream. */
+ nghttp3_buf dbuf;
+ /* written_icnt is Insert Count written to decoder stream so far. */
+ uint64_t written_icnt;
+ /* max_concurrent_streams is the number of concurrent streams that a
+ remote endpoint can open, including both bidirectional and
+ unidirectional streams which potentially receives QPACK encoded
+ HEADER frame. */
+ size_t max_concurrent_streams;
+};
+
+/*
+ * nghttp3_qpack_decoder_init initializes |decoder|.
+ * |hard_max_dtable_capacity| is the upper bound of the dynamic table
+ * capacity. |max_blocked_streams| is the maximum number of stream
+ * which can be blocked. |mem| is a memory allocator.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder,
+ size_t hard_max_dtable_capacity,
+ size_t max_blocked_streams,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_qpack_decoder_free frees memory allocated for |decoder|.
+ * This function does not free memory pointed by |decoder|.
+ */
+void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder);
+
+/*
+ * nghttp3_qpack_decoder_dtable_indexed_add adds entry received in
+ * Insert With Name Reference to dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Space required for a decoded entry exceeds max dynamic table
+ * size.
+ */
+int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder);
+
+/*
+ * nghttp3_qpack_decoder_dtable_static_add adds entry received in
+ * Insert With Name Reference (static) to dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Space required for a decoded entry exceeds max dynamic table
+ * size.
+ */
+int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder);
+
+/*
+ * nghttp3_qpack_decoder_dtable_dynamic_add adds entry received in
+ * Insert With Name Reference (dynamic) to dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Space required for a decoded entry exceeds max dynamic table
+ * size.
+ */
+int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder);
+
+/*
+ * nghttp3_qpack_decoder_dtable_duplicate_add adds entry received in
+ * Duplicate to dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Space required for a decoded entry exceeds max dynamic table
+ * size.
+ */
+int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder);
+
+/*
+ * nghttp3_qpack_decoder_dtable_literal_add adds entry received in
+ * Insert With Literal Name to dynamic table.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Space required for a decoded entry exceeds max dynamic table
+ * size.
+ */
+int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder);
+
+struct nghttp3_qpack_stream_context {
+ /* state is a current state of reading request stream. */
+ nghttp3_qpack_request_stream_state state;
+ /* rstate is a set of intermediate state which are used to process
+ request stream. */
+ nghttp3_qpack_read_state rstate;
+ const nghttp3_mem *mem;
+ /* opcode is a request stream opcode being processed. */
+ nghttp3_qpack_request_stream_opcode opcode;
+ int64_t stream_id;
+ /* ricnt is Required Insert Count to decode this header block. */
+ uint64_t ricnt;
+ /* base is Base in Header Block Prefix. */
+ uint64_t base;
+ /* dbase_sign is the delta base sign in Header Block Prefix. */
+ int dbase_sign;
+};
+
+/*
+ * nghttp3_qpack_stream_context_init initializes |sctx|.
+ */
+void nghttp3_qpack_stream_context_init(nghttp3_qpack_stream_context *sctx,
+ int64_t stream_id,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_qpack_stream_context_free frees memory allocated for
+ * |sctx|. This function does not free memory pointed by |sctx|.
+ */
+void nghttp3_qpack_stream_context_free(nghttp3_qpack_stream_context *sctx);
+
+/*
+ * nghttp3_qpack_decoder_reconstruct_ricnt reconstructs Required
+ * Insert Count from the encoded form |encricnt| and stores Required
+ * Insert Count in |*dest|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED
+ * Unable to reconstruct Required Insert Count.
+ */
+int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder,
+ uint64_t *dest, uint64_t encricnt);
+
+/*
+ * nghttp3_qpack_decoder_rel2abs converts relative index rstate->left
+ * received in encoder stream to absolute index and stores it in
+ * rstate->absidx.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_ENCODER_STREAM
+ * Relative index is invalid.
+ */
+int nghttp3_qpack_decoder_rel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_read_state *rstate);
+
+/*
+ * nghttp3_qpack_decoder_brel2abs converts Base relative index
+ * rstate->left received in request stream to absolute index and
+ * stores it in rstate->absidx.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED
+ * Base relative index is invalid.
+ */
+int nghttp3_qpack_decoder_brel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx);
+
+/*
+ * nghttp3_qpack_decoder_pbrel2abs converts Post-Base relative index
+ * rstate->left received in request stream to absolute index and
+ * stores it in rstate->absidx.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED
+ * Post-Base relative index is invalid.
+ */
+int nghttp3_qpack_decoder_pbrel2abs(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx);
+
+void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv);
+
+int nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv);
+
+void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder,
+ nghttp3_qpack_stream_context *sctx,
+ nghttp3_qpack_nv *nv);
+
+/*
+ * nghttp3_qpack_decoder_write_section_ack writes Section
+ * Acknowledgement to decoder stream.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ * NGHTTP3_ERR_QPACK_FATAL
+ * Decoder stream overflow.
+ */
+int nghttp3_qpack_decoder_write_section_ack(
+ nghttp3_qpack_decoder *decoder, const nghttp3_qpack_stream_context *sctx);
+
+#endif /* NGHTTP3_QPACK_H */
diff --git a/lib/nghttp3_qpack_huffman.c b/lib/nghttp3_qpack_huffman.c
new file mode 100644
index 0000000..c36a68e
--- /dev/null
+++ b/lib/nghttp3_qpack_huffman.c
@@ -0,0 +1,122 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_qpack_huffman.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "nghttp3_conv.h"
+
+size_t nghttp3_qpack_huffman_encode_count(const uint8_t *src, size_t len) {
+ size_t i;
+ size_t nbits = 0;
+
+ for (i = 0; i < len; ++i) {
+ nbits += huffman_sym_table[src[i]].nbits;
+ }
+ /* pad the prefix of EOS (256) */
+ return (nbits + 7) / 8;
+}
+
+uint8_t *nghttp3_qpack_huffman_encode(uint8_t *dest, const uint8_t *src,
+ size_t srclen) {
+ const nghttp3_qpack_huffman_sym *sym;
+ const uint8_t *end = src + srclen;
+ uint64_t code = 0;
+ size_t nbits = 0;
+ uint32_t x;
+
+ for (; src != end;) {
+ sym = &huffman_sym_table[*src++];
+ code |= (uint64_t)sym->code << (32 - nbits);
+ nbits += sym->nbits;
+ if (nbits < 32) {
+ continue;
+ }
+ x = htonl((uint32_t)(code >> 32));
+ memcpy(dest, &x, 4);
+ dest += 4;
+ code <<= 32;
+ nbits -= 32;
+ }
+
+ for (; nbits >= 8;) {
+ *dest++ = (uint8_t)(code >> 56);
+ code <<= 8;
+ nbits -= 8;
+ }
+
+ if (nbits) {
+ *dest++ = (uint8_t)((uint8_t)(code >> 56) | ((1 << (8 - nbits)) - 1));
+ }
+
+ return dest;
+}
+
+void nghttp3_qpack_huffman_decode_context_init(
+ nghttp3_qpack_huffman_decode_context *ctx) {
+ ctx->fstate = NGHTTP3_QPACK_HUFFMAN_ACCEPTED;
+}
+
+nghttp3_ssize
+nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx,
+ uint8_t *dest, const uint8_t *src, size_t srclen,
+ int fin) {
+ uint8_t *p = dest;
+ const uint8_t *end = src + srclen;
+ nghttp3_qpack_huffman_decode_node node = {ctx->fstate, 0};
+ const nghttp3_qpack_huffman_decode_node *t = &node;
+ uint8_t c;
+
+ /* We use the decoding algorithm described in
+ http://graphics.ics.uci.edu/pub/Prefix.pdf */
+ for (; src != end;) {
+ c = *src++;
+ t = &qpack_huffman_decode_table[t->fstate & 0x1ff][c >> 4];
+ if (t->fstate & NGHTTP3_QPACK_HUFFMAN_SYM) {
+ *p++ = t->sym;
+ }
+
+ t = &qpack_huffman_decode_table[t->fstate & 0x1ff][c & 0xf];
+ if (t->fstate & NGHTTP3_QPACK_HUFFMAN_SYM) {
+ *p++ = t->sym;
+ }
+ }
+
+ ctx->fstate = t->fstate;
+
+ if (fin && !(ctx->fstate & NGHTTP3_QPACK_HUFFMAN_ACCEPTED)) {
+ return NGHTTP3_ERR_QPACK_FATAL;
+ }
+
+ return p - dest;
+}
+
+int nghttp3_qpack_huffman_decode_failure_state(
+ nghttp3_qpack_huffman_decode_context *ctx) {
+ return ctx->fstate == 0x100;
+}
diff --git a/lib/nghttp3_qpack_huffman.h b/lib/nghttp3_qpack_huffman.h
new file mode 100644
index 0000000..fc3bc7b
--- /dev/null
+++ b/lib/nghttp3_qpack_huffman.h
@@ -0,0 +1,108 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_QPACK_HUFFMAN_H
+#define NGHTTP3_QPACK_HUFFMAN_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+typedef struct nghttp3_qpack_huffman_sym {
+ /* The number of bits in this code */
+ uint32_t nbits;
+ /* Huffman code aligned to LSB */
+ uint32_t code;
+} nghttp3_qpack_huffman_sym;
+
+extern const nghttp3_qpack_huffman_sym huffman_sym_table[];
+
+size_t nghttp3_qpack_huffman_encode_count(const uint8_t *src, size_t len);
+
+uint8_t *nghttp3_qpack_huffman_encode(uint8_t *dest, const uint8_t *src,
+ size_t srclen);
+
+typedef enum nghttp3_qpack_huffman_decode_flag {
+ /* FSA accepts this state as the end of huffman encoding
+ sequence. */
+ NGHTTP3_QPACK_HUFFMAN_ACCEPTED = 1 << 14,
+ /* This state emits symbol */
+ NGHTTP3_QPACK_HUFFMAN_SYM = 1 << 15,
+} nghttp3_qpack_huffman_decode_flag;
+
+typedef struct nghttp3_qpack_huffman_decode_node {
+ /* fstate is the current huffman decoding state, which is actually
+ the node ID of internal huffman tree with
+ nghttp3_qpack_huffman_decode_flag OR-ed. We have 257 leaf nodes,
+ but they are identical to root node other than emitting a symbol,
+ so we have 256 internal nodes [1..256], inclusive. The node ID
+ 256 is a special node and it is a terminal state that means
+ decoding failed. */
+ uint16_t fstate;
+ /* symbol if NGHTTP3_QPACK_HUFFMAN_SYM flag set */
+ uint8_t sym;
+} nghttp3_qpack_huffman_decode_node;
+
+typedef struct nghttp3_qpack_huffman_decode_context {
+ /* fstate is the current huffman decoding state. */
+ uint16_t fstate;
+} nghttp3_qpack_huffman_decode_context;
+
+extern const nghttp3_qpack_huffman_decode_node qpack_huffman_decode_table[][16];
+
+void nghttp3_qpack_huffman_decode_context_init(
+ nghttp3_qpack_huffman_decode_context *ctx);
+
+/*
+ * nghttp3_qpack_huffman_decode decodes huffman encoded byte string
+ * stored in |src| of length |srclen|. |ctx| is a decoding context.
+ * |ctx| remembers the decoding state, and caller can call this
+ * function multiple times to feed each chunk of huffman encoded
+ * substring. |fin| must be nonzero if |src| contains the last chunk
+ * of huffman string. The decoded string is written to the buffer
+ * pointed by |dest|. This function assumes that the buffer pointed
+ * by |dest| contains enough memory to store decoded byte string.
+ *
+ * This function returns the number of bytes written to |dest|, or one
+ * of the following negative error codes:
+ *
+ * NGHTTP3_ERR_QPACK_FATAL
+ * Could not decode huffman string.
+ */
+nghttp3_ssize
+nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx,
+ uint8_t *dest, const uint8_t *src, size_t srclen,
+ int fin);
+
+/*
+ * nghttp3_qpack_huffman_decode_failure_state returns nonzero if |ctx|
+ * indicates that huffman decoding context is in failure state.
+ */
+int nghttp3_qpack_huffman_decode_failure_state(
+ nghttp3_qpack_huffman_decode_context *ctx);
+
+#endif /* NGHTTP3_QPACK_HUFFMAN_H */
diff --git a/lib/nghttp3_qpack_huffman_data.c b/lib/nghttp3_qpack_huffman_data.c
new file mode 100644
index 0000000..0c104db
--- /dev/null
+++ b/lib/nghttp3_qpack_huffman_data.c
@@ -0,0 +1,4981 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2013 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_qpack_huffman.h"
+
+/* Generated by mkhufftbl.py */
+
+const nghttp3_qpack_huffman_sym huffman_sym_table[] = {
+ {13, 0xffc00000u}, {23, 0xffffb000u}, {28, 0xfffffe20u}, {28, 0xfffffe30u},
+ {28, 0xfffffe40u}, {28, 0xfffffe50u}, {28, 0xfffffe60u}, {28, 0xfffffe70u},
+ {28, 0xfffffe80u}, {24, 0xffffea00u}, {30, 0xfffffff0u}, {28, 0xfffffe90u},
+ {28, 0xfffffea0u}, {30, 0xfffffff4u}, {28, 0xfffffeb0u}, {28, 0xfffffec0u},
+ {28, 0xfffffed0u}, {28, 0xfffffee0u}, {28, 0xfffffef0u}, {28, 0xffffff00u},
+ {28, 0xffffff10u}, {28, 0xffffff20u}, {30, 0xfffffff8u}, {28, 0xffffff30u},
+ {28, 0xffffff40u}, {28, 0xffffff50u}, {28, 0xffffff60u}, {28, 0xffffff70u},
+ {28, 0xffffff80u}, {28, 0xffffff90u}, {28, 0xffffffa0u}, {28, 0xffffffb0u},
+ {6, 0x50000000u}, {10, 0xfe000000u}, {10, 0xfe400000u}, {12, 0xffa00000u},
+ {13, 0xffc80000u}, {6, 0x54000000u}, {8, 0xf8000000u}, {11, 0xff400000u},
+ {10, 0xfe800000u}, {10, 0xfec00000u}, {8, 0xf9000000u}, {11, 0xff600000u},
+ {8, 0xfa000000u}, {6, 0x58000000u}, {6, 0x5c000000u}, {6, 0x60000000u},
+ {5, 0x0u}, {5, 0x8000000u}, {5, 0x10000000u}, {6, 0x64000000u},
+ {6, 0x68000000u}, {6, 0x6c000000u}, {6, 0x70000000u}, {6, 0x74000000u},
+ {6, 0x78000000u}, {6, 0x7c000000u}, {7, 0xb8000000u}, {8, 0xfb000000u},
+ {15, 0xfff80000u}, {6, 0x80000000u}, {12, 0xffb00000u}, {10, 0xff000000u},
+ {13, 0xffd00000u}, {6, 0x84000000u}, {7, 0xba000000u}, {7, 0xbc000000u},
+ {7, 0xbe000000u}, {7, 0xc0000000u}, {7, 0xc2000000u}, {7, 0xc4000000u},
+ {7, 0xc6000000u}, {7, 0xc8000000u}, {7, 0xca000000u}, {7, 0xcc000000u},
+ {7, 0xce000000u}, {7, 0xd0000000u}, {7, 0xd2000000u}, {7, 0xd4000000u},
+ {7, 0xd6000000u}, {7, 0xd8000000u}, {7, 0xda000000u}, {7, 0xdc000000u},
+ {7, 0xde000000u}, {7, 0xe0000000u}, {7, 0xe2000000u}, {7, 0xe4000000u},
+ {8, 0xfc000000u}, {7, 0xe6000000u}, {8, 0xfd000000u}, {13, 0xffd80000u},
+ {19, 0xfffe0000u}, {13, 0xffe00000u}, {14, 0xfff00000u}, {6, 0x88000000u},
+ {15, 0xfffa0000u}, {5, 0x18000000u}, {6, 0x8c000000u}, {5, 0x20000000u},
+ {6, 0x90000000u}, {5, 0x28000000u}, {6, 0x94000000u}, {6, 0x98000000u},
+ {6, 0x9c000000u}, {5, 0x30000000u}, {7, 0xe8000000u}, {7, 0xea000000u},
+ {6, 0xa0000000u}, {6, 0xa4000000u}, {6, 0xa8000000u}, {5, 0x38000000u},
+ {6, 0xac000000u}, {7, 0xec000000u}, {6, 0xb0000000u}, {5, 0x40000000u},
+ {5, 0x48000000u}, {6, 0xb4000000u}, {7, 0xee000000u}, {7, 0xf0000000u},
+ {7, 0xf2000000u}, {7, 0xf4000000u}, {7, 0xf6000000u}, {15, 0xfffc0000u},
+ {11, 0xff800000u}, {14, 0xfff40000u}, {13, 0xffe80000u}, {28, 0xffffffc0u},
+ {20, 0xfffe6000u}, {22, 0xffff4800u}, {20, 0xfffe7000u}, {20, 0xfffe8000u},
+ {22, 0xffff4c00u}, {22, 0xffff5000u}, {22, 0xffff5400u}, {23, 0xffffb200u},
+ {22, 0xffff5800u}, {23, 0xffffb400u}, {23, 0xffffb600u}, {23, 0xffffb800u},
+ {23, 0xffffba00u}, {23, 0xffffbc00u}, {24, 0xffffeb00u}, {23, 0xffffbe00u},
+ {24, 0xffffec00u}, {24, 0xffffed00u}, {22, 0xffff5c00u}, {23, 0xffffc000u},
+ {24, 0xffffee00u}, {23, 0xffffc200u}, {23, 0xffffc400u}, {23, 0xffffc600u},
+ {23, 0xffffc800u}, {21, 0xfffee000u}, {22, 0xffff6000u}, {23, 0xffffca00u},
+ {22, 0xffff6400u}, {23, 0xffffcc00u}, {23, 0xffffce00u}, {24, 0xffffef00u},
+ {22, 0xffff6800u}, {21, 0xfffee800u}, {20, 0xfffe9000u}, {22, 0xffff6c00u},
+ {22, 0xffff7000u}, {23, 0xffffd000u}, {23, 0xffffd200u}, {21, 0xfffef000u},
+ {23, 0xffffd400u}, {22, 0xffff7400u}, {22, 0xffff7800u}, {24, 0xfffff000u},
+ {21, 0xfffef800u}, {22, 0xffff7c00u}, {23, 0xffffd600u}, {23, 0xffffd800u},
+ {21, 0xffff0000u}, {21, 0xffff0800u}, {22, 0xffff8000u}, {21, 0xffff1000u},
+ {23, 0xffffda00u}, {22, 0xffff8400u}, {23, 0xffffdc00u}, {23, 0xffffde00u},
+ {20, 0xfffea000u}, {22, 0xffff8800u}, {22, 0xffff8c00u}, {22, 0xffff9000u},
+ {23, 0xffffe000u}, {22, 0xffff9400u}, {22, 0xffff9800u}, {23, 0xffffe200u},
+ {26, 0xfffff800u}, {26, 0xfffff840u}, {20, 0xfffeb000u}, {19, 0xfffe2000u},
+ {22, 0xffff9c00u}, {23, 0xffffe400u}, {22, 0xffffa000u}, {25, 0xfffff600u},
+ {26, 0xfffff880u}, {26, 0xfffff8c0u}, {26, 0xfffff900u}, {27, 0xfffffbc0u},
+ {27, 0xfffffbe0u}, {26, 0xfffff940u}, {24, 0xfffff100u}, {25, 0xfffff680u},
+ {19, 0xfffe4000u}, {21, 0xffff1800u}, {26, 0xfffff980u}, {27, 0xfffffc00u},
+ {27, 0xfffffc20u}, {26, 0xfffff9c0u}, {27, 0xfffffc40u}, {24, 0xfffff200u},
+ {21, 0xffff2000u}, {21, 0xffff2800u}, {26, 0xfffffa00u}, {26, 0xfffffa40u},
+ {28, 0xffffffd0u}, {27, 0xfffffc60u}, {27, 0xfffffc80u}, {27, 0xfffffca0u},
+ {20, 0xfffec000u}, {24, 0xfffff300u}, {20, 0xfffed000u}, {21, 0xffff3000u},
+ {22, 0xffffa400u}, {21, 0xffff3800u}, {21, 0xffff4000u}, {23, 0xffffe600u},
+ {22, 0xffffa800u}, {22, 0xffffac00u}, {25, 0xfffff700u}, {25, 0xfffff780u},
+ {24, 0xfffff400u}, {24, 0xfffff500u}, {26, 0xfffffa80u}, {23, 0xffffe800u},
+ {26, 0xfffffac0u}, {27, 0xfffffcc0u}, {26, 0xfffffb00u}, {26, 0xfffffb40u},
+ {27, 0xfffffce0u}, {27, 0xfffffd00u}, {27, 0xfffffd20u}, {27, 0xfffffd40u},
+ {27, 0xfffffd60u}, {28, 0xffffffe0u}, {27, 0xfffffd80u}, {27, 0xfffffda0u},
+ {27, 0xfffffdc0u}, {27, 0xfffffde0u}, {27, 0xfffffe00u}, {26, 0xfffffb80u},
+ {30, 0xfffffffcu}};
+
+const nghttp3_qpack_huffman_decode_node qpack_huffman_decode_table[][16] = {
+ /* 0 */
+ {
+ {0x04, 0},
+ {0x05, 0},
+ {0x07, 0},
+ {0x08, 0},
+ {0x0b, 0},
+ {0x0c, 0},
+ {0x10, 0},
+ {0x13, 0},
+ {0x19, 0},
+ {0x1c, 0},
+ {0x20, 0},
+ {0x23, 0},
+ {0x2a, 0},
+ {0x31, 0},
+ {0x39, 0},
+ {0x4040, 0},
+ },
+ /* 1 */
+ {
+ {0xc000, 48},
+ {0xc000, 49},
+ {0xc000, 50},
+ {0xc000, 97},
+ {0xc000, 99},
+ {0xc000, 101},
+ {0xc000, 105},
+ {0xc000, 111},
+ {0xc000, 115},
+ {0xc000, 116},
+ {0x0d, 0},
+ {0x0e, 0},
+ {0x11, 0},
+ {0x12, 0},
+ {0x14, 0},
+ {0x15, 0},
+ },
+ /* 2 */
+ {
+ {0x8001, 48},
+ {0xc016, 48},
+ {0x8001, 49},
+ {0xc016, 49},
+ {0x8001, 50},
+ {0xc016, 50},
+ {0x8001, 97},
+ {0xc016, 97},
+ {0x8001, 99},
+ {0xc016, 99},
+ {0x8001, 101},
+ {0xc016, 101},
+ {0x8001, 105},
+ {0xc016, 105},
+ {0x8001, 111},
+ {0xc016, 111},
+ },
+ /* 3 */
+ {
+ {0x8002, 48},
+ {0x8009, 48},
+ {0x8017, 48},
+ {0xc028, 48},
+ {0x8002, 49},
+ {0x8009, 49},
+ {0x8017, 49},
+ {0xc028, 49},
+ {0x8002, 50},
+ {0x8009, 50},
+ {0x8017, 50},
+ {0xc028, 50},
+ {0x8002, 97},
+ {0x8009, 97},
+ {0x8017, 97},
+ {0xc028, 97},
+ },
+ /* 4 */
+ {
+ {0x8003, 48},
+ {0x8006, 48},
+ {0x800a, 48},
+ {0x800f, 48},
+ {0x8018, 48},
+ {0x801f, 48},
+ {0x8029, 48},
+ {0xc038, 48},
+ {0x8003, 49},
+ {0x8006, 49},
+ {0x800a, 49},
+ {0x800f, 49},
+ {0x8018, 49},
+ {0x801f, 49},
+ {0x8029, 49},
+ {0xc038, 49},
+ },
+ /* 5 */
+ {
+ {0x8003, 50},
+ {0x8006, 50},
+ {0x800a, 50},
+ {0x800f, 50},
+ {0x8018, 50},
+ {0x801f, 50},
+ {0x8029, 50},
+ {0xc038, 50},
+ {0x8003, 97},
+ {0x8006, 97},
+ {0x800a, 97},
+ {0x800f, 97},
+ {0x8018, 97},
+ {0x801f, 97},
+ {0x8029, 97},
+ {0xc038, 97},
+ },
+ /* 6 */
+ {
+ {0x8002, 99},
+ {0x8009, 99},
+ {0x8017, 99},
+ {0xc028, 99},
+ {0x8002, 101},
+ {0x8009, 101},
+ {0x8017, 101},
+ {0xc028, 101},
+ {0x8002, 105},
+ {0x8009, 105},
+ {0x8017, 105},
+ {0xc028, 105},
+ {0x8002, 111},
+ {0x8009, 111},
+ {0x8017, 111},
+ {0xc028, 111},
+ },
+ /* 7 */
+ {
+ {0x8003, 99},
+ {0x8006, 99},
+ {0x800a, 99},
+ {0x800f, 99},
+ {0x8018, 99},
+ {0x801f, 99},
+ {0x8029, 99},
+ {0xc038, 99},
+ {0x8003, 101},
+ {0x8006, 101},
+ {0x800a, 101},
+ {0x800f, 101},
+ {0x8018, 101},
+ {0x801f, 101},
+ {0x8029, 101},
+ {0xc038, 101},
+ },
+ /* 8 */
+ {
+ {0x8003, 105},
+ {0x8006, 105},
+ {0x800a, 105},
+ {0x800f, 105},
+ {0x8018, 105},
+ {0x801f, 105},
+ {0x8029, 105},
+ {0xc038, 105},
+ {0x8003, 111},
+ {0x8006, 111},
+ {0x800a, 111},
+ {0x800f, 111},
+ {0x8018, 111},
+ {0x801f, 111},
+ {0x8029, 111},
+ {0xc038, 111},
+ },
+ /* 9 */
+ {
+ {0x8001, 115},
+ {0xc016, 115},
+ {0x8001, 116},
+ {0xc016, 116},
+ {0xc000, 32},
+ {0xc000, 37},
+ {0xc000, 45},
+ {0xc000, 46},
+ {0xc000, 47},
+ {0xc000, 51},
+ {0xc000, 52},
+ {0xc000, 53},
+ {0xc000, 54},
+ {0xc000, 55},
+ {0xc000, 56},
+ {0xc000, 57},
+ },
+ /* 10 */
+ {
+ {0x8002, 115},
+ {0x8009, 115},
+ {0x8017, 115},
+ {0xc028, 115},
+ {0x8002, 116},
+ {0x8009, 116},
+ {0x8017, 116},
+ {0xc028, 116},
+ {0x8001, 32},
+ {0xc016, 32},
+ {0x8001, 37},
+ {0xc016, 37},
+ {0x8001, 45},
+ {0xc016, 45},
+ {0x8001, 46},
+ {0xc016, 46},
+ },
+ /* 11 */
+ {
+ {0x8003, 115},
+ {0x8006, 115},
+ {0x800a, 115},
+ {0x800f, 115},
+ {0x8018, 115},
+ {0x801f, 115},
+ {0x8029, 115},
+ {0xc038, 115},
+ {0x8003, 116},
+ {0x8006, 116},
+ {0x800a, 116},
+ {0x800f, 116},
+ {0x8018, 116},
+ {0x801f, 116},
+ {0x8029, 116},
+ {0xc038, 116},
+ },
+ /* 12 */
+ {
+ {0x8002, 32},
+ {0x8009, 32},
+ {0x8017, 32},
+ {0xc028, 32},
+ {0x8002, 37},
+ {0x8009, 37},
+ {0x8017, 37},
+ {0xc028, 37},
+ {0x8002, 45},
+ {0x8009, 45},
+ {0x8017, 45},
+ {0xc028, 45},
+ {0x8002, 46},
+ {0x8009, 46},
+ {0x8017, 46},
+ {0xc028, 46},
+ },
+ /* 13 */
+ {
+ {0x8003, 32},
+ {0x8006, 32},
+ {0x800a, 32},
+ {0x800f, 32},
+ {0x8018, 32},
+ {0x801f, 32},
+ {0x8029, 32},
+ {0xc038, 32},
+ {0x8003, 37},
+ {0x8006, 37},
+ {0x800a, 37},
+ {0x800f, 37},
+ {0x8018, 37},
+ {0x801f, 37},
+ {0x8029, 37},
+ {0xc038, 37},
+ },
+ /* 14 */
+ {
+ {0x8003, 45},
+ {0x8006, 45},
+ {0x800a, 45},
+ {0x800f, 45},
+ {0x8018, 45},
+ {0x801f, 45},
+ {0x8029, 45},
+ {0xc038, 45},
+ {0x8003, 46},
+ {0x8006, 46},
+ {0x800a, 46},
+ {0x800f, 46},
+ {0x8018, 46},
+ {0x801f, 46},
+ {0x8029, 46},
+ {0xc038, 46},
+ },
+ /* 15 */
+ {
+ {0x8001, 47},
+ {0xc016, 47},
+ {0x8001, 51},
+ {0xc016, 51},
+ {0x8001, 52},
+ {0xc016, 52},
+ {0x8001, 53},
+ {0xc016, 53},
+ {0x8001, 54},
+ {0xc016, 54},
+ {0x8001, 55},
+ {0xc016, 55},
+ {0x8001, 56},
+ {0xc016, 56},
+ {0x8001, 57},
+ {0xc016, 57},
+ },
+ /* 16 */
+ {
+ {0x8002, 47},
+ {0x8009, 47},
+ {0x8017, 47},
+ {0xc028, 47},
+ {0x8002, 51},
+ {0x8009, 51},
+ {0x8017, 51},
+ {0xc028, 51},
+ {0x8002, 52},
+ {0x8009, 52},
+ {0x8017, 52},
+ {0xc028, 52},
+ {0x8002, 53},
+ {0x8009, 53},
+ {0x8017, 53},
+ {0xc028, 53},
+ },
+ /* 17 */
+ {
+ {0x8003, 47},
+ {0x8006, 47},
+ {0x800a, 47},
+ {0x800f, 47},
+ {0x8018, 47},
+ {0x801f, 47},
+ {0x8029, 47},
+ {0xc038, 47},
+ {0x8003, 51},
+ {0x8006, 51},
+ {0x800a, 51},
+ {0x800f, 51},
+ {0x8018, 51},
+ {0x801f, 51},
+ {0x8029, 51},
+ {0xc038, 51},
+ },
+ /* 18 */
+ {
+ {0x8003, 52},
+ {0x8006, 52},
+ {0x800a, 52},
+ {0x800f, 52},
+ {0x8018, 52},
+ {0x801f, 52},
+ {0x8029, 52},
+ {0xc038, 52},
+ {0x8003, 53},
+ {0x8006, 53},
+ {0x800a, 53},
+ {0x800f, 53},
+ {0x8018, 53},
+ {0x801f, 53},
+ {0x8029, 53},
+ {0xc038, 53},
+ },
+ /* 19 */
+ {
+ {0x8002, 54},
+ {0x8009, 54},
+ {0x8017, 54},
+ {0xc028, 54},
+ {0x8002, 55},
+ {0x8009, 55},
+ {0x8017, 55},
+ {0xc028, 55},
+ {0x8002, 56},
+ {0x8009, 56},
+ {0x8017, 56},
+ {0xc028, 56},
+ {0x8002, 57},
+ {0x8009, 57},
+ {0x8017, 57},
+ {0xc028, 57},
+ },
+ /* 20 */
+ {
+ {0x8003, 54},
+ {0x8006, 54},
+ {0x800a, 54},
+ {0x800f, 54},
+ {0x8018, 54},
+ {0x801f, 54},
+ {0x8029, 54},
+ {0xc038, 54},
+ {0x8003, 55},
+ {0x8006, 55},
+ {0x800a, 55},
+ {0x800f, 55},
+ {0x8018, 55},
+ {0x801f, 55},
+ {0x8029, 55},
+ {0xc038, 55},
+ },
+ /* 21 */
+ {
+ {0x8003, 56},
+ {0x8006, 56},
+ {0x800a, 56},
+ {0x800f, 56},
+ {0x8018, 56},
+ {0x801f, 56},
+ {0x8029, 56},
+ {0xc038, 56},
+ {0x8003, 57},
+ {0x8006, 57},
+ {0x800a, 57},
+ {0x800f, 57},
+ {0x8018, 57},
+ {0x801f, 57},
+ {0x8029, 57},
+ {0xc038, 57},
+ },
+ /* 22 */
+ {
+ {0x1a, 0},
+ {0x1b, 0},
+ {0x1d, 0},
+ {0x1e, 0},
+ {0x21, 0},
+ {0x22, 0},
+ {0x24, 0},
+ {0x25, 0},
+ {0x2b, 0},
+ {0x2e, 0},
+ {0x32, 0},
+ {0x35, 0},
+ {0x3a, 0},
+ {0x3d, 0},
+ {0x41, 0},
+ {0x4044, 0},
+ },
+ /* 23 */
+ {
+ {0xc000, 61},
+ {0xc000, 65},
+ {0xc000, 95},
+ {0xc000, 98},
+ {0xc000, 100},
+ {0xc000, 102},
+ {0xc000, 103},
+ {0xc000, 104},
+ {0xc000, 108},
+ {0xc000, 109},
+ {0xc000, 110},
+ {0xc000, 112},
+ {0xc000, 114},
+ {0xc000, 117},
+ {0x26, 0},
+ {0x27, 0},
+ },
+ /* 24 */
+ {
+ {0x8001, 61},
+ {0xc016, 61},
+ {0x8001, 65},
+ {0xc016, 65},
+ {0x8001, 95},
+ {0xc016, 95},
+ {0x8001, 98},
+ {0xc016, 98},
+ {0x8001, 100},
+ {0xc016, 100},
+ {0x8001, 102},
+ {0xc016, 102},
+ {0x8001, 103},
+ {0xc016, 103},
+ {0x8001, 104},
+ {0xc016, 104},
+ },
+ /* 25 */
+ {
+ {0x8002, 61},
+ {0x8009, 61},
+ {0x8017, 61},
+ {0xc028, 61},
+ {0x8002, 65},
+ {0x8009, 65},
+ {0x8017, 65},
+ {0xc028, 65},
+ {0x8002, 95},
+ {0x8009, 95},
+ {0x8017, 95},
+ {0xc028, 95},
+ {0x8002, 98},
+ {0x8009, 98},
+ {0x8017, 98},
+ {0xc028, 98},
+ },
+ /* 26 */
+ {
+ {0x8003, 61},
+ {0x8006, 61},
+ {0x800a, 61},
+ {0x800f, 61},
+ {0x8018, 61},
+ {0x801f, 61},
+ {0x8029, 61},
+ {0xc038, 61},
+ {0x8003, 65},
+ {0x8006, 65},
+ {0x800a, 65},
+ {0x800f, 65},
+ {0x8018, 65},
+ {0x801f, 65},
+ {0x8029, 65},
+ {0xc038, 65},
+ },
+ /* 27 */
+ {
+ {0x8003, 95},
+ {0x8006, 95},
+ {0x800a, 95},
+ {0x800f, 95},
+ {0x8018, 95},
+ {0x801f, 95},
+ {0x8029, 95},
+ {0xc038, 95},
+ {0x8003, 98},
+ {0x8006, 98},
+ {0x800a, 98},
+ {0x800f, 98},
+ {0x8018, 98},
+ {0x801f, 98},
+ {0x8029, 98},
+ {0xc038, 98},
+ },
+ /* 28 */
+ {
+ {0x8002, 100},
+ {0x8009, 100},
+ {0x8017, 100},
+ {0xc028, 100},
+ {0x8002, 102},
+ {0x8009, 102},
+ {0x8017, 102},
+ {0xc028, 102},
+ {0x8002, 103},
+ {0x8009, 103},
+ {0x8017, 103},
+ {0xc028, 103},
+ {0x8002, 104},
+ {0x8009, 104},
+ {0x8017, 104},
+ {0xc028, 104},
+ },
+ /* 29 */
+ {
+ {0x8003, 100},
+ {0x8006, 100},
+ {0x800a, 100},
+ {0x800f, 100},
+ {0x8018, 100},
+ {0x801f, 100},
+ {0x8029, 100},
+ {0xc038, 100},
+ {0x8003, 102},
+ {0x8006, 102},
+ {0x800a, 102},
+ {0x800f, 102},
+ {0x8018, 102},
+ {0x801f, 102},
+ {0x8029, 102},
+ {0xc038, 102},
+ },
+ /* 30 */
+ {
+ {0x8003, 103},
+ {0x8006, 103},
+ {0x800a, 103},
+ {0x800f, 103},
+ {0x8018, 103},
+ {0x801f, 103},
+ {0x8029, 103},
+ {0xc038, 103},
+ {0x8003, 104},
+ {0x8006, 104},
+ {0x800a, 104},
+ {0x800f, 104},
+ {0x8018, 104},
+ {0x801f, 104},
+ {0x8029, 104},
+ {0xc038, 104},
+ },
+ /* 31 */
+ {
+ {0x8001, 108},
+ {0xc016, 108},
+ {0x8001, 109},
+ {0xc016, 109},
+ {0x8001, 110},
+ {0xc016, 110},
+ {0x8001, 112},
+ {0xc016, 112},
+ {0x8001, 114},
+ {0xc016, 114},
+ {0x8001, 117},
+ {0xc016, 117},
+ {0xc000, 58},
+ {0xc000, 66},
+ {0xc000, 67},
+ {0xc000, 68},
+ },
+ /* 32 */
+ {
+ {0x8002, 108},
+ {0x8009, 108},
+ {0x8017, 108},
+ {0xc028, 108},
+ {0x8002, 109},
+ {0x8009, 109},
+ {0x8017, 109},
+ {0xc028, 109},
+ {0x8002, 110},
+ {0x8009, 110},
+ {0x8017, 110},
+ {0xc028, 110},
+ {0x8002, 112},
+ {0x8009, 112},
+ {0x8017, 112},
+ {0xc028, 112},
+ },
+ /* 33 */
+ {
+ {0x8003, 108},
+ {0x8006, 108},
+ {0x800a, 108},
+ {0x800f, 108},
+ {0x8018, 108},
+ {0x801f, 108},
+ {0x8029, 108},
+ {0xc038, 108},
+ {0x8003, 109},
+ {0x8006, 109},
+ {0x800a, 109},
+ {0x800f, 109},
+ {0x8018, 109},
+ {0x801f, 109},
+ {0x8029, 109},
+ {0xc038, 109},
+ },
+ /* 34 */
+ {
+ {0x8003, 110},
+ {0x8006, 110},
+ {0x800a, 110},
+ {0x800f, 110},
+ {0x8018, 110},
+ {0x801f, 110},
+ {0x8029, 110},
+ {0xc038, 110},
+ {0x8003, 112},
+ {0x8006, 112},
+ {0x800a, 112},
+ {0x800f, 112},
+ {0x8018, 112},
+ {0x801f, 112},
+ {0x8029, 112},
+ {0xc038, 112},
+ },
+ /* 35 */
+ {
+ {0x8002, 114},
+ {0x8009, 114},
+ {0x8017, 114},
+ {0xc028, 114},
+ {0x8002, 117},
+ {0x8009, 117},
+ {0x8017, 117},
+ {0xc028, 117},
+ {0x8001, 58},
+ {0xc016, 58},
+ {0x8001, 66},
+ {0xc016, 66},
+ {0x8001, 67},
+ {0xc016, 67},
+ {0x8001, 68},
+ {0xc016, 68},
+ },
+ /* 36 */
+ {
+ {0x8003, 114},
+ {0x8006, 114},
+ {0x800a, 114},
+ {0x800f, 114},
+ {0x8018, 114},
+ {0x801f, 114},
+ {0x8029, 114},
+ {0xc038, 114},
+ {0x8003, 117},
+ {0x8006, 117},
+ {0x800a, 117},
+ {0x800f, 117},
+ {0x8018, 117},
+ {0x801f, 117},
+ {0x8029, 117},
+ {0xc038, 117},
+ },
+ /* 37 */
+ {
+ {0x8002, 58},
+ {0x8009, 58},
+ {0x8017, 58},
+ {0xc028, 58},
+ {0x8002, 66},
+ {0x8009, 66},
+ {0x8017, 66},
+ {0xc028, 66},
+ {0x8002, 67},
+ {0x8009, 67},
+ {0x8017, 67},
+ {0xc028, 67},
+ {0x8002, 68},
+ {0x8009, 68},
+ {0x8017, 68},
+ {0xc028, 68},
+ },
+ /* 38 */
+ {
+ {0x8003, 58},
+ {0x8006, 58},
+ {0x800a, 58},
+ {0x800f, 58},
+ {0x8018, 58},
+ {0x801f, 58},
+ {0x8029, 58},
+ {0xc038, 58},
+ {0x8003, 66},
+ {0x8006, 66},
+ {0x800a, 66},
+ {0x800f, 66},
+ {0x8018, 66},
+ {0x801f, 66},
+ {0x8029, 66},
+ {0xc038, 66},
+ },
+ /* 39 */
+ {
+ {0x8003, 67},
+ {0x8006, 67},
+ {0x800a, 67},
+ {0x800f, 67},
+ {0x8018, 67},
+ {0x801f, 67},
+ {0x8029, 67},
+ {0xc038, 67},
+ {0x8003, 68},
+ {0x8006, 68},
+ {0x800a, 68},
+ {0x800f, 68},
+ {0x8018, 68},
+ {0x801f, 68},
+ {0x8029, 68},
+ {0xc038, 68},
+ },
+ /* 40 */
+ {
+ {0x2c, 0},
+ {0x2d, 0},
+ {0x2f, 0},
+ {0x30, 0},
+ {0x33, 0},
+ {0x34, 0},
+ {0x36, 0},
+ {0x37, 0},
+ {0x3b, 0},
+ {0x3c, 0},
+ {0x3e, 0},
+ {0x3f, 0},
+ {0x42, 0},
+ {0x43, 0},
+ {0x45, 0},
+ {0x4048, 0},
+ },
+ /* 41 */
+ {
+ {0xc000, 69},
+ {0xc000, 70},
+ {0xc000, 71},
+ {0xc000, 72},
+ {0xc000, 73},
+ {0xc000, 74},
+ {0xc000, 75},
+ {0xc000, 76},
+ {0xc000, 77},
+ {0xc000, 78},
+ {0xc000, 79},
+ {0xc000, 80},
+ {0xc000, 81},
+ {0xc000, 82},
+ {0xc000, 83},
+ {0xc000, 84},
+ },
+ /* 42 */
+ {
+ {0x8001, 69},
+ {0xc016, 69},
+ {0x8001, 70},
+ {0xc016, 70},
+ {0x8001, 71},
+ {0xc016, 71},
+ {0x8001, 72},
+ {0xc016, 72},
+ {0x8001, 73},
+ {0xc016, 73},
+ {0x8001, 74},
+ {0xc016, 74},
+ {0x8001, 75},
+ {0xc016, 75},
+ {0x8001, 76},
+ {0xc016, 76},
+ },
+ /* 43 */
+ {
+ {0x8002, 69},
+ {0x8009, 69},
+ {0x8017, 69},
+ {0xc028, 69},
+ {0x8002, 70},
+ {0x8009, 70},
+ {0x8017, 70},
+ {0xc028, 70},
+ {0x8002, 71},
+ {0x8009, 71},
+ {0x8017, 71},
+ {0xc028, 71},
+ {0x8002, 72},
+ {0x8009, 72},
+ {0x8017, 72},
+ {0xc028, 72},
+ },
+ /* 44 */
+ {
+ {0x8003, 69},
+ {0x8006, 69},
+ {0x800a, 69},
+ {0x800f, 69},
+ {0x8018, 69},
+ {0x801f, 69},
+ {0x8029, 69},
+ {0xc038, 69},
+ {0x8003, 70},
+ {0x8006, 70},
+ {0x800a, 70},
+ {0x800f, 70},
+ {0x8018, 70},
+ {0x801f, 70},
+ {0x8029, 70},
+ {0xc038, 70},
+ },
+ /* 45 */
+ {
+ {0x8003, 71},
+ {0x8006, 71},
+ {0x800a, 71},
+ {0x800f, 71},
+ {0x8018, 71},
+ {0x801f, 71},
+ {0x8029, 71},
+ {0xc038, 71},
+ {0x8003, 72},
+ {0x8006, 72},
+ {0x800a, 72},
+ {0x800f, 72},
+ {0x8018, 72},
+ {0x801f, 72},
+ {0x8029, 72},
+ {0xc038, 72},
+ },
+ /* 46 */
+ {
+ {0x8002, 73},
+ {0x8009, 73},
+ {0x8017, 73},
+ {0xc028, 73},
+ {0x8002, 74},
+ {0x8009, 74},
+ {0x8017, 74},
+ {0xc028, 74},
+ {0x8002, 75},
+ {0x8009, 75},
+ {0x8017, 75},
+ {0xc028, 75},
+ {0x8002, 76},
+ {0x8009, 76},
+ {0x8017, 76},
+ {0xc028, 76},
+ },
+ /* 47 */
+ {
+ {0x8003, 73},
+ {0x8006, 73},
+ {0x800a, 73},
+ {0x800f, 73},
+ {0x8018, 73},
+ {0x801f, 73},
+ {0x8029, 73},
+ {0xc038, 73},
+ {0x8003, 74},
+ {0x8006, 74},
+ {0x800a, 74},
+ {0x800f, 74},
+ {0x8018, 74},
+ {0x801f, 74},
+ {0x8029, 74},
+ {0xc038, 74},
+ },
+ /* 48 */
+ {
+ {0x8003, 75},
+ {0x8006, 75},
+ {0x800a, 75},
+ {0x800f, 75},
+ {0x8018, 75},
+ {0x801f, 75},
+ {0x8029, 75},
+ {0xc038, 75},
+ {0x8003, 76},
+ {0x8006, 76},
+ {0x800a, 76},
+ {0x800f, 76},
+ {0x8018, 76},
+ {0x801f, 76},
+ {0x8029, 76},
+ {0xc038, 76},
+ },
+ /* 49 */
+ {
+ {0x8001, 77},
+ {0xc016, 77},
+ {0x8001, 78},
+ {0xc016, 78},
+ {0x8001, 79},
+ {0xc016, 79},
+ {0x8001, 80},
+ {0xc016, 80},
+ {0x8001, 81},
+ {0xc016, 81},
+ {0x8001, 82},
+ {0xc016, 82},
+ {0x8001, 83},
+ {0xc016, 83},
+ {0x8001, 84},
+ {0xc016, 84},
+ },
+ /* 50 */
+ {
+ {0x8002, 77},
+ {0x8009, 77},
+ {0x8017, 77},
+ {0xc028, 77},
+ {0x8002, 78},
+ {0x8009, 78},
+ {0x8017, 78},
+ {0xc028, 78},
+ {0x8002, 79},
+ {0x8009, 79},
+ {0x8017, 79},
+ {0xc028, 79},
+ {0x8002, 80},
+ {0x8009, 80},
+ {0x8017, 80},
+ {0xc028, 80},
+ },
+ /* 51 */
+ {
+ {0x8003, 77},
+ {0x8006, 77},
+ {0x800a, 77},
+ {0x800f, 77},
+ {0x8018, 77},
+ {0x801f, 77},
+ {0x8029, 77},
+ {0xc038, 77},
+ {0x8003, 78},
+ {0x8006, 78},
+ {0x800a, 78},
+ {0x800f, 78},
+ {0x8018, 78},
+ {0x801f, 78},
+ {0x8029, 78},
+ {0xc038, 78},
+ },
+ /* 52 */
+ {
+ {0x8003, 79},
+ {0x8006, 79},
+ {0x800a, 79},
+ {0x800f, 79},
+ {0x8018, 79},
+ {0x801f, 79},
+ {0x8029, 79},
+ {0xc038, 79},
+ {0x8003, 80},
+ {0x8006, 80},
+ {0x800a, 80},
+ {0x800f, 80},
+ {0x8018, 80},
+ {0x801f, 80},
+ {0x8029, 80},
+ {0xc038, 80},
+ },
+ /* 53 */
+ {
+ {0x8002, 81},
+ {0x8009, 81},
+ {0x8017, 81},
+ {0xc028, 81},
+ {0x8002, 82},
+ {0x8009, 82},
+ {0x8017, 82},
+ {0xc028, 82},
+ {0x8002, 83},
+ {0x8009, 83},
+ {0x8017, 83},
+ {0xc028, 83},
+ {0x8002, 84},
+ {0x8009, 84},
+ {0x8017, 84},
+ {0xc028, 84},
+ },
+ /* 54 */
+ {
+ {0x8003, 81},
+ {0x8006, 81},
+ {0x800a, 81},
+ {0x800f, 81},
+ {0x8018, 81},
+ {0x801f, 81},
+ {0x8029, 81},
+ {0xc038, 81},
+ {0x8003, 82},
+ {0x8006, 82},
+ {0x800a, 82},
+ {0x800f, 82},
+ {0x8018, 82},
+ {0x801f, 82},
+ {0x8029, 82},
+ {0xc038, 82},
+ },
+ /* 55 */
+ {
+ {0x8003, 83},
+ {0x8006, 83},
+ {0x800a, 83},
+ {0x800f, 83},
+ {0x8018, 83},
+ {0x801f, 83},
+ {0x8029, 83},
+ {0xc038, 83},
+ {0x8003, 84},
+ {0x8006, 84},
+ {0x800a, 84},
+ {0x800f, 84},
+ {0x8018, 84},
+ {0x801f, 84},
+ {0x8029, 84},
+ {0xc038, 84},
+ },
+ /* 56 */
+ {
+ {0xc000, 85},
+ {0xc000, 86},
+ {0xc000, 87},
+ {0xc000, 89},
+ {0xc000, 106},
+ {0xc000, 107},
+ {0xc000, 113},
+ {0xc000, 118},
+ {0xc000, 119},
+ {0xc000, 120},
+ {0xc000, 121},
+ {0xc000, 122},
+ {0x46, 0},
+ {0x47, 0},
+ {0x49, 0},
+ {0x404a, 0},
+ },
+ /* 57 */
+ {
+ {0x8001, 85},
+ {0xc016, 85},
+ {0x8001, 86},
+ {0xc016, 86},
+ {0x8001, 87},
+ {0xc016, 87},
+ {0x8001, 89},
+ {0xc016, 89},
+ {0x8001, 106},
+ {0xc016, 106},
+ {0x8001, 107},
+ {0xc016, 107},
+ {0x8001, 113},
+ {0xc016, 113},
+ {0x8001, 118},
+ {0xc016, 118},
+ },
+ /* 58 */
+ {
+ {0x8002, 85},
+ {0x8009, 85},
+ {0x8017, 85},
+ {0xc028, 85},
+ {0x8002, 86},
+ {0x8009, 86},
+ {0x8017, 86},
+ {0xc028, 86},
+ {0x8002, 87},
+ {0x8009, 87},
+ {0x8017, 87},
+ {0xc028, 87},
+ {0x8002, 89},
+ {0x8009, 89},
+ {0x8017, 89},
+ {0xc028, 89},
+ },
+ /* 59 */
+ {
+ {0x8003, 85},
+ {0x8006, 85},
+ {0x800a, 85},
+ {0x800f, 85},
+ {0x8018, 85},
+ {0x801f, 85},
+ {0x8029, 85},
+ {0xc038, 85},
+ {0x8003, 86},
+ {0x8006, 86},
+ {0x800a, 86},
+ {0x800f, 86},
+ {0x8018, 86},
+ {0x801f, 86},
+ {0x8029, 86},
+ {0xc038, 86},
+ },
+ /* 60 */
+ {
+ {0x8003, 87},
+ {0x8006, 87},
+ {0x800a, 87},
+ {0x800f, 87},
+ {0x8018, 87},
+ {0x801f, 87},
+ {0x8029, 87},
+ {0xc038, 87},
+ {0x8003, 89},
+ {0x8006, 89},
+ {0x800a, 89},
+ {0x800f, 89},
+ {0x8018, 89},
+ {0x801f, 89},
+ {0x8029, 89},
+ {0xc038, 89},
+ },
+ /* 61 */
+ {
+ {0x8002, 106},
+ {0x8009, 106},
+ {0x8017, 106},
+ {0xc028, 106},
+ {0x8002, 107},
+ {0x8009, 107},
+ {0x8017, 107},
+ {0xc028, 107},
+ {0x8002, 113},
+ {0x8009, 113},
+ {0x8017, 113},
+ {0xc028, 113},
+ {0x8002, 118},
+ {0x8009, 118},
+ {0x8017, 118},
+ {0xc028, 118},
+ },
+ /* 62 */
+ {
+ {0x8003, 106},
+ {0x8006, 106},
+ {0x800a, 106},
+ {0x800f, 106},
+ {0x8018, 106},
+ {0x801f, 106},
+ {0x8029, 106},
+ {0xc038, 106},
+ {0x8003, 107},
+ {0x8006, 107},
+ {0x800a, 107},
+ {0x800f, 107},
+ {0x8018, 107},
+ {0x801f, 107},
+ {0x8029, 107},
+ {0xc038, 107},
+ },
+ /* 63 */
+ {
+ {0x8003, 113},
+ {0x8006, 113},
+ {0x800a, 113},
+ {0x800f, 113},
+ {0x8018, 113},
+ {0x801f, 113},
+ {0x8029, 113},
+ {0xc038, 113},
+ {0x8003, 118},
+ {0x8006, 118},
+ {0x800a, 118},
+ {0x800f, 118},
+ {0x8018, 118},
+ {0x801f, 118},
+ {0x8029, 118},
+ {0xc038, 118},
+ },
+ /* 64 */
+ {
+ {0x8001, 119},
+ {0xc016, 119},
+ {0x8001, 120},
+ {0xc016, 120},
+ {0x8001, 121},
+ {0xc016, 121},
+ {0x8001, 122},
+ {0xc016, 122},
+ {0xc000, 38},
+ {0xc000, 42},
+ {0xc000, 44},
+ {0xc000, 59},
+ {0xc000, 88},
+ {0xc000, 90},
+ {0x4b, 0},
+ {0x4e, 0},
+ },
+ /* 65 */
+ {
+ {0x8002, 119},
+ {0x8009, 119},
+ {0x8017, 119},
+ {0xc028, 119},
+ {0x8002, 120},
+ {0x8009, 120},
+ {0x8017, 120},
+ {0xc028, 120},
+ {0x8002, 121},
+ {0x8009, 121},
+ {0x8017, 121},
+ {0xc028, 121},
+ {0x8002, 122},
+ {0x8009, 122},
+ {0x8017, 122},
+ {0xc028, 122},
+ },
+ /* 66 */
+ {
+ {0x8003, 119},
+ {0x8006, 119},
+ {0x800a, 119},
+ {0x800f, 119},
+ {0x8018, 119},
+ {0x801f, 119},
+ {0x8029, 119},
+ {0xc038, 119},
+ {0x8003, 120},
+ {0x8006, 120},
+ {0x800a, 120},
+ {0x800f, 120},
+ {0x8018, 120},
+ {0x801f, 120},
+ {0x8029, 120},
+ {0xc038, 120},
+ },
+ /* 67 */
+ {
+ {0x8003, 121},
+ {0x8006, 121},
+ {0x800a, 121},
+ {0x800f, 121},
+ {0x8018, 121},
+ {0x801f, 121},
+ {0x8029, 121},
+ {0xc038, 121},
+ {0x8003, 122},
+ {0x8006, 122},
+ {0x800a, 122},
+ {0x800f, 122},
+ {0x8018, 122},
+ {0x801f, 122},
+ {0x8029, 122},
+ {0xc038, 122},
+ },
+ /* 68 */
+ {
+ {0x8001, 38},
+ {0xc016, 38},
+ {0x8001, 42},
+ {0xc016, 42},
+ {0x8001, 44},
+ {0xc016, 44},
+ {0x8001, 59},
+ {0xc016, 59},
+ {0x8001, 88},
+ {0xc016, 88},
+ {0x8001, 90},
+ {0xc016, 90},
+ {0x4c, 0},
+ {0x4d, 0},
+ {0x4f, 0},
+ {0x51, 0},
+ },
+ /* 69 */
+ {
+ {0x8002, 38},
+ {0x8009, 38},
+ {0x8017, 38},
+ {0xc028, 38},
+ {0x8002, 42},
+ {0x8009, 42},
+ {0x8017, 42},
+ {0xc028, 42},
+ {0x8002, 44},
+ {0x8009, 44},
+ {0x8017, 44},
+ {0xc028, 44},
+ {0x8002, 59},
+ {0x8009, 59},
+ {0x8017, 59},
+ {0xc028, 59},
+ },
+ /* 70 */
+ {
+ {0x8003, 38},
+ {0x8006, 38},
+ {0x800a, 38},
+ {0x800f, 38},
+ {0x8018, 38},
+ {0x801f, 38},
+ {0x8029, 38},
+ {0xc038, 38},
+ {0x8003, 42},
+ {0x8006, 42},
+ {0x800a, 42},
+ {0x800f, 42},
+ {0x8018, 42},
+ {0x801f, 42},
+ {0x8029, 42},
+ {0xc038, 42},
+ },
+ /* 71 */
+ {
+ {0x8003, 44},
+ {0x8006, 44},
+ {0x800a, 44},
+ {0x800f, 44},
+ {0x8018, 44},
+ {0x801f, 44},
+ {0x8029, 44},
+ {0xc038, 44},
+ {0x8003, 59},
+ {0x8006, 59},
+ {0x800a, 59},
+ {0x800f, 59},
+ {0x8018, 59},
+ {0x801f, 59},
+ {0x8029, 59},
+ {0xc038, 59},
+ },
+ /* 72 */
+ {
+ {0x8002, 88},
+ {0x8009, 88},
+ {0x8017, 88},
+ {0xc028, 88},
+ {0x8002, 90},
+ {0x8009, 90},
+ {0x8017, 90},
+ {0xc028, 90},
+ {0xc000, 33},
+ {0xc000, 34},
+ {0xc000, 40},
+ {0xc000, 41},
+ {0xc000, 63},
+ {0x50, 0},
+ {0x52, 0},
+ {0x54, 0},
+ },
+ /* 73 */
+ {
+ {0x8003, 88},
+ {0x8006, 88},
+ {0x800a, 88},
+ {0x800f, 88},
+ {0x8018, 88},
+ {0x801f, 88},
+ {0x8029, 88},
+ {0xc038, 88},
+ {0x8003, 90},
+ {0x8006, 90},
+ {0x800a, 90},
+ {0x800f, 90},
+ {0x8018, 90},
+ {0x801f, 90},
+ {0x8029, 90},
+ {0xc038, 90},
+ },
+ /* 74 */
+ {
+ {0x8001, 33},
+ {0xc016, 33},
+ {0x8001, 34},
+ {0xc016, 34},
+ {0x8001, 40},
+ {0xc016, 40},
+ {0x8001, 41},
+ {0xc016, 41},
+ {0x8001, 63},
+ {0xc016, 63},
+ {0xc000, 39},
+ {0xc000, 43},
+ {0xc000, 124},
+ {0x53, 0},
+ {0x55, 0},
+ {0x58, 0},
+ },
+ /* 75 */
+ {
+ {0x8002, 33},
+ {0x8009, 33},
+ {0x8017, 33},
+ {0xc028, 33},
+ {0x8002, 34},
+ {0x8009, 34},
+ {0x8017, 34},
+ {0xc028, 34},
+ {0x8002, 40},
+ {0x8009, 40},
+ {0x8017, 40},
+ {0xc028, 40},
+ {0x8002, 41},
+ {0x8009, 41},
+ {0x8017, 41},
+ {0xc028, 41},
+ },
+ /* 76 */
+ {
+ {0x8003, 33},
+ {0x8006, 33},
+ {0x800a, 33},
+ {0x800f, 33},
+ {0x8018, 33},
+ {0x801f, 33},
+ {0x8029, 33},
+ {0xc038, 33},
+ {0x8003, 34},
+ {0x8006, 34},
+ {0x800a, 34},
+ {0x800f, 34},
+ {0x8018, 34},
+ {0x801f, 34},
+ {0x8029, 34},
+ {0xc038, 34},
+ },
+ /* 77 */
+ {
+ {0x8003, 40},
+ {0x8006, 40},
+ {0x800a, 40},
+ {0x800f, 40},
+ {0x8018, 40},
+ {0x801f, 40},
+ {0x8029, 40},
+ {0xc038, 40},
+ {0x8003, 41},
+ {0x8006, 41},
+ {0x800a, 41},
+ {0x800f, 41},
+ {0x8018, 41},
+ {0x801f, 41},
+ {0x8029, 41},
+ {0xc038, 41},
+ },
+ /* 78 */
+ {
+ {0x8002, 63},
+ {0x8009, 63},
+ {0x8017, 63},
+ {0xc028, 63},
+ {0x8001, 39},
+ {0xc016, 39},
+ {0x8001, 43},
+ {0xc016, 43},
+ {0x8001, 124},
+ {0xc016, 124},
+ {0xc000, 35},
+ {0xc000, 62},
+ {0x56, 0},
+ {0x57, 0},
+ {0x59, 0},
+ {0x5a, 0},
+ },
+ /* 79 */
+ {
+ {0x8003, 63},
+ {0x8006, 63},
+ {0x800a, 63},
+ {0x800f, 63},
+ {0x8018, 63},
+ {0x801f, 63},
+ {0x8029, 63},
+ {0xc038, 63},
+ {0x8002, 39},
+ {0x8009, 39},
+ {0x8017, 39},
+ {0xc028, 39},
+ {0x8002, 43},
+ {0x8009, 43},
+ {0x8017, 43},
+ {0xc028, 43},
+ },
+ /* 80 */
+ {
+ {0x8003, 39},
+ {0x8006, 39},
+ {0x800a, 39},
+ {0x800f, 39},
+ {0x8018, 39},
+ {0x801f, 39},
+ {0x8029, 39},
+ {0xc038, 39},
+ {0x8003, 43},
+ {0x8006, 43},
+ {0x800a, 43},
+ {0x800f, 43},
+ {0x8018, 43},
+ {0x801f, 43},
+ {0x8029, 43},
+ {0xc038, 43},
+ },
+ /* 81 */
+ {
+ {0x8002, 124},
+ {0x8009, 124},
+ {0x8017, 124},
+ {0xc028, 124},
+ {0x8001, 35},
+ {0xc016, 35},
+ {0x8001, 62},
+ {0xc016, 62},
+ {0xc000, 0},
+ {0xc000, 36},
+ {0xc000, 64},
+ {0xc000, 91},
+ {0xc000, 93},
+ {0xc000, 126},
+ {0x5b, 0},
+ {0x5c, 0},
+ },
+ /* 82 */
+ {
+ {0x8003, 124},
+ {0x8006, 124},
+ {0x800a, 124},
+ {0x800f, 124},
+ {0x8018, 124},
+ {0x801f, 124},
+ {0x8029, 124},
+ {0xc038, 124},
+ {0x8002, 35},
+ {0x8009, 35},
+ {0x8017, 35},
+ {0xc028, 35},
+ {0x8002, 62},
+ {0x8009, 62},
+ {0x8017, 62},
+ {0xc028, 62},
+ },
+ /* 83 */
+ {
+ {0x8003, 35},
+ {0x8006, 35},
+ {0x800a, 35},
+ {0x800f, 35},
+ {0x8018, 35},
+ {0x801f, 35},
+ {0x8029, 35},
+ {0xc038, 35},
+ {0x8003, 62},
+ {0x8006, 62},
+ {0x800a, 62},
+ {0x800f, 62},
+ {0x8018, 62},
+ {0x801f, 62},
+ {0x8029, 62},
+ {0xc038, 62},
+ },
+ /* 84 */
+ {
+ {0x8001, 0},
+ {0xc016, 0},
+ {0x8001, 36},
+ {0xc016, 36},
+ {0x8001, 64},
+ {0xc016, 64},
+ {0x8001, 91},
+ {0xc016, 91},
+ {0x8001, 93},
+ {0xc016, 93},
+ {0x8001, 126},
+ {0xc016, 126},
+ {0xc000, 94},
+ {0xc000, 125},
+ {0x5d, 0},
+ {0x5e, 0},
+ },
+ /* 85 */
+ {
+ {0x8002, 0},
+ {0x8009, 0},
+ {0x8017, 0},
+ {0xc028, 0},
+ {0x8002, 36},
+ {0x8009, 36},
+ {0x8017, 36},
+ {0xc028, 36},
+ {0x8002, 64},
+ {0x8009, 64},
+ {0x8017, 64},
+ {0xc028, 64},
+ {0x8002, 91},
+ {0x8009, 91},
+ {0x8017, 91},
+ {0xc028, 91},
+ },
+ /* 86 */
+ {
+ {0x8003, 0},
+ {0x8006, 0},
+ {0x800a, 0},
+ {0x800f, 0},
+ {0x8018, 0},
+ {0x801f, 0},
+ {0x8029, 0},
+ {0xc038, 0},
+ {0x8003, 36},
+ {0x8006, 36},
+ {0x800a, 36},
+ {0x800f, 36},
+ {0x8018, 36},
+ {0x801f, 36},
+ {0x8029, 36},
+ {0xc038, 36},
+ },
+ /* 87 */
+ {
+ {0x8003, 64},
+ {0x8006, 64},
+ {0x800a, 64},
+ {0x800f, 64},
+ {0x8018, 64},
+ {0x801f, 64},
+ {0x8029, 64},
+ {0xc038, 64},
+ {0x8003, 91},
+ {0x8006, 91},
+ {0x800a, 91},
+ {0x800f, 91},
+ {0x8018, 91},
+ {0x801f, 91},
+ {0x8029, 91},
+ {0xc038, 91},
+ },
+ /* 88 */
+ {
+ {0x8002, 93},
+ {0x8009, 93},
+ {0x8017, 93},
+ {0xc028, 93},
+ {0x8002, 126},
+ {0x8009, 126},
+ {0x8017, 126},
+ {0xc028, 126},
+ {0x8001, 94},
+ {0xc016, 94},
+ {0x8001, 125},
+ {0xc016, 125},
+ {0xc000, 60},
+ {0xc000, 96},
+ {0xc000, 123},
+ {0x5f, 0},
+ },
+ /* 89 */
+ {
+ {0x8003, 93},
+ {0x8006, 93},
+ {0x800a, 93},
+ {0x800f, 93},
+ {0x8018, 93},
+ {0x801f, 93},
+ {0x8029, 93},
+ {0xc038, 93},
+ {0x8003, 126},
+ {0x8006, 126},
+ {0x800a, 126},
+ {0x800f, 126},
+ {0x8018, 126},
+ {0x801f, 126},
+ {0x8029, 126},
+ {0xc038, 126},
+ },
+ /* 90 */
+ {
+ {0x8002, 94},
+ {0x8009, 94},
+ {0x8017, 94},
+ {0xc028, 94},
+ {0x8002, 125},
+ {0x8009, 125},
+ {0x8017, 125},
+ {0xc028, 125},
+ {0x8001, 60},
+ {0xc016, 60},
+ {0x8001, 96},
+ {0xc016, 96},
+ {0x8001, 123},
+ {0xc016, 123},
+ {0x60, 0},
+ {0x6e, 0},
+ },
+ /* 91 */
+ {
+ {0x8003, 94},
+ {0x8006, 94},
+ {0x800a, 94},
+ {0x800f, 94},
+ {0x8018, 94},
+ {0x801f, 94},
+ {0x8029, 94},
+ {0xc038, 94},
+ {0x8003, 125},
+ {0x8006, 125},
+ {0x800a, 125},
+ {0x800f, 125},
+ {0x8018, 125},
+ {0x801f, 125},
+ {0x8029, 125},
+ {0xc038, 125},
+ },
+ /* 92 */
+ {
+ {0x8002, 60},
+ {0x8009, 60},
+ {0x8017, 60},
+ {0xc028, 60},
+ {0x8002, 96},
+ {0x8009, 96},
+ {0x8017, 96},
+ {0xc028, 96},
+ {0x8002, 123},
+ {0x8009, 123},
+ {0x8017, 123},
+ {0xc028, 123},
+ {0x61, 0},
+ {0x65, 0},
+ {0x6f, 0},
+ {0x85, 0},
+ },
+ /* 93 */
+ {
+ {0x8003, 60},
+ {0x8006, 60},
+ {0x800a, 60},
+ {0x800f, 60},
+ {0x8018, 60},
+ {0x801f, 60},
+ {0x8029, 60},
+ {0xc038, 60},
+ {0x8003, 96},
+ {0x8006, 96},
+ {0x800a, 96},
+ {0x800f, 96},
+ {0x8018, 96},
+ {0x801f, 96},
+ {0x8029, 96},
+ {0xc038, 96},
+ },
+ /* 94 */
+ {
+ {0x8003, 123},
+ {0x8006, 123},
+ {0x800a, 123},
+ {0x800f, 123},
+ {0x8018, 123},
+ {0x801f, 123},
+ {0x8029, 123},
+ {0xc038, 123},
+ {0x62, 0},
+ {0x63, 0},
+ {0x66, 0},
+ {0x69, 0},
+ {0x70, 0},
+ {0x77, 0},
+ {0x86, 0},
+ {0x99, 0},
+ },
+ /* 95 */
+ {
+ {0xc000, 92},
+ {0xc000, 195},
+ {0xc000, 208},
+ {0x64, 0},
+ {0x67, 0},
+ {0x68, 0},
+ {0x6a, 0},
+ {0x6b, 0},
+ {0x71, 0},
+ {0x74, 0},
+ {0x78, 0},
+ {0x7e, 0},
+ {0x87, 0},
+ {0x8e, 0},
+ {0x9a, 0},
+ {0xa9, 0},
+ },
+ /* 96 */
+ {
+ {0x8001, 92},
+ {0xc016, 92},
+ {0x8001, 195},
+ {0xc016, 195},
+ {0x8001, 208},
+ {0xc016, 208},
+ {0xc000, 128},
+ {0xc000, 130},
+ {0xc000, 131},
+ {0xc000, 162},
+ {0xc000, 184},
+ {0xc000, 194},
+ {0xc000, 224},
+ {0xc000, 226},
+ {0x6c, 0},
+ {0x6d, 0},
+ },
+ /* 97 */
+ {
+ {0x8002, 92},
+ {0x8009, 92},
+ {0x8017, 92},
+ {0xc028, 92},
+ {0x8002, 195},
+ {0x8009, 195},
+ {0x8017, 195},
+ {0xc028, 195},
+ {0x8002, 208},
+ {0x8009, 208},
+ {0x8017, 208},
+ {0xc028, 208},
+ {0x8001, 128},
+ {0xc016, 128},
+ {0x8001, 130},
+ {0xc016, 130},
+ },
+ /* 98 */
+ {
+ {0x8003, 92},
+ {0x8006, 92},
+ {0x800a, 92},
+ {0x800f, 92},
+ {0x8018, 92},
+ {0x801f, 92},
+ {0x8029, 92},
+ {0xc038, 92},
+ {0x8003, 195},
+ {0x8006, 195},
+ {0x800a, 195},
+ {0x800f, 195},
+ {0x8018, 195},
+ {0x801f, 195},
+ {0x8029, 195},
+ {0xc038, 195},
+ },
+ /* 99 */
+ {
+ {0x8003, 208},
+ {0x8006, 208},
+ {0x800a, 208},
+ {0x800f, 208},
+ {0x8018, 208},
+ {0x801f, 208},
+ {0x8029, 208},
+ {0xc038, 208},
+ {0x8002, 128},
+ {0x8009, 128},
+ {0x8017, 128},
+ {0xc028, 128},
+ {0x8002, 130},
+ {0x8009, 130},
+ {0x8017, 130},
+ {0xc028, 130},
+ },
+ /* 100 */
+ {
+ {0x8003, 128},
+ {0x8006, 128},
+ {0x800a, 128},
+ {0x800f, 128},
+ {0x8018, 128},
+ {0x801f, 128},
+ {0x8029, 128},
+ {0xc038, 128},
+ {0x8003, 130},
+ {0x8006, 130},
+ {0x800a, 130},
+ {0x800f, 130},
+ {0x8018, 130},
+ {0x801f, 130},
+ {0x8029, 130},
+ {0xc038, 130},
+ },
+ /* 101 */
+ {
+ {0x8001, 131},
+ {0xc016, 131},
+ {0x8001, 162},
+ {0xc016, 162},
+ {0x8001, 184},
+ {0xc016, 184},
+ {0x8001, 194},
+ {0xc016, 194},
+ {0x8001, 224},
+ {0xc016, 224},
+ {0x8001, 226},
+ {0xc016, 226},
+ {0xc000, 153},
+ {0xc000, 161},
+ {0xc000, 167},
+ {0xc000, 172},
+ },
+ /* 102 */
+ {
+ {0x8002, 131},
+ {0x8009, 131},
+ {0x8017, 131},
+ {0xc028, 131},
+ {0x8002, 162},
+ {0x8009, 162},
+ {0x8017, 162},
+ {0xc028, 162},
+ {0x8002, 184},
+ {0x8009, 184},
+ {0x8017, 184},
+ {0xc028, 184},
+ {0x8002, 194},
+ {0x8009, 194},
+ {0x8017, 194},
+ {0xc028, 194},
+ },
+ /* 103 */
+ {
+ {0x8003, 131},
+ {0x8006, 131},
+ {0x800a, 131},
+ {0x800f, 131},
+ {0x8018, 131},
+ {0x801f, 131},
+ {0x8029, 131},
+ {0xc038, 131},
+ {0x8003, 162},
+ {0x8006, 162},
+ {0x800a, 162},
+ {0x800f, 162},
+ {0x8018, 162},
+ {0x801f, 162},
+ {0x8029, 162},
+ {0xc038, 162},
+ },
+ /* 104 */
+ {
+ {0x8003, 184},
+ {0x8006, 184},
+ {0x800a, 184},
+ {0x800f, 184},
+ {0x8018, 184},
+ {0x801f, 184},
+ {0x8029, 184},
+ {0xc038, 184},
+ {0x8003, 194},
+ {0x8006, 194},
+ {0x800a, 194},
+ {0x800f, 194},
+ {0x8018, 194},
+ {0x801f, 194},
+ {0x8029, 194},
+ {0xc038, 194},
+ },
+ /* 105 */
+ {
+ {0x8002, 224},
+ {0x8009, 224},
+ {0x8017, 224},
+ {0xc028, 224},
+ {0x8002, 226},
+ {0x8009, 226},
+ {0x8017, 226},
+ {0xc028, 226},
+ {0x8001, 153},
+ {0xc016, 153},
+ {0x8001, 161},
+ {0xc016, 161},
+ {0x8001, 167},
+ {0xc016, 167},
+ {0x8001, 172},
+ {0xc016, 172},
+ },
+ /* 106 */
+ {
+ {0x8003, 224},
+ {0x8006, 224},
+ {0x800a, 224},
+ {0x800f, 224},
+ {0x8018, 224},
+ {0x801f, 224},
+ {0x8029, 224},
+ {0xc038, 224},
+ {0x8003, 226},
+ {0x8006, 226},
+ {0x800a, 226},
+ {0x800f, 226},
+ {0x8018, 226},
+ {0x801f, 226},
+ {0x8029, 226},
+ {0xc038, 226},
+ },
+ /* 107 */
+ {
+ {0x8002, 153},
+ {0x8009, 153},
+ {0x8017, 153},
+ {0xc028, 153},
+ {0x8002, 161},
+ {0x8009, 161},
+ {0x8017, 161},
+ {0xc028, 161},
+ {0x8002, 167},
+ {0x8009, 167},
+ {0x8017, 167},
+ {0xc028, 167},
+ {0x8002, 172},
+ {0x8009, 172},
+ {0x8017, 172},
+ {0xc028, 172},
+ },
+ /* 108 */
+ {
+ {0x8003, 153},
+ {0x8006, 153},
+ {0x800a, 153},
+ {0x800f, 153},
+ {0x8018, 153},
+ {0x801f, 153},
+ {0x8029, 153},
+ {0xc038, 153},
+ {0x8003, 161},
+ {0x8006, 161},
+ {0x800a, 161},
+ {0x800f, 161},
+ {0x8018, 161},
+ {0x801f, 161},
+ {0x8029, 161},
+ {0xc038, 161},
+ },
+ /* 109 */
+ {
+ {0x8003, 167},
+ {0x8006, 167},
+ {0x800a, 167},
+ {0x800f, 167},
+ {0x8018, 167},
+ {0x801f, 167},
+ {0x8029, 167},
+ {0xc038, 167},
+ {0x8003, 172},
+ {0x8006, 172},
+ {0x800a, 172},
+ {0x800f, 172},
+ {0x8018, 172},
+ {0x801f, 172},
+ {0x8029, 172},
+ {0xc038, 172},
+ },
+ /* 110 */
+ {
+ {0x72, 0},
+ {0x73, 0},
+ {0x75, 0},
+ {0x76, 0},
+ {0x79, 0},
+ {0x7b, 0},
+ {0x7f, 0},
+ {0x82, 0},
+ {0x88, 0},
+ {0x8b, 0},
+ {0x8f, 0},
+ {0x92, 0},
+ {0x9b, 0},
+ {0xa2, 0},
+ {0xaa, 0},
+ {0xb4, 0},
+ },
+ /* 111 */
+ {
+ {0xc000, 176},
+ {0xc000, 177},
+ {0xc000, 179},
+ {0xc000, 209},
+ {0xc000, 216},
+ {0xc000, 217},
+ {0xc000, 227},
+ {0xc000, 229},
+ {0xc000, 230},
+ {0x7a, 0},
+ {0x7c, 0},
+ {0x7d, 0},
+ {0x80, 0},
+ {0x81, 0},
+ {0x83, 0},
+ {0x84, 0},
+ },
+ /* 112 */
+ {
+ {0x8001, 176},
+ {0xc016, 176},
+ {0x8001, 177},
+ {0xc016, 177},
+ {0x8001, 179},
+ {0xc016, 179},
+ {0x8001, 209},
+ {0xc016, 209},
+ {0x8001, 216},
+ {0xc016, 216},
+ {0x8001, 217},
+ {0xc016, 217},
+ {0x8001, 227},
+ {0xc016, 227},
+ {0x8001, 229},
+ {0xc016, 229},
+ },
+ /* 113 */
+ {
+ {0x8002, 176},
+ {0x8009, 176},
+ {0x8017, 176},
+ {0xc028, 176},
+ {0x8002, 177},
+ {0x8009, 177},
+ {0x8017, 177},
+ {0xc028, 177},
+ {0x8002, 179},
+ {0x8009, 179},
+ {0x8017, 179},
+ {0xc028, 179},
+ {0x8002, 209},
+ {0x8009, 209},
+ {0x8017, 209},
+ {0xc028, 209},
+ },
+ /* 114 */
+ {
+ {0x8003, 176},
+ {0x8006, 176},
+ {0x800a, 176},
+ {0x800f, 176},
+ {0x8018, 176},
+ {0x801f, 176},
+ {0x8029, 176},
+ {0xc038, 176},
+ {0x8003, 177},
+ {0x8006, 177},
+ {0x800a, 177},
+ {0x800f, 177},
+ {0x8018, 177},
+ {0x801f, 177},
+ {0x8029, 177},
+ {0xc038, 177},
+ },
+ /* 115 */
+ {
+ {0x8003, 179},
+ {0x8006, 179},
+ {0x800a, 179},
+ {0x800f, 179},
+ {0x8018, 179},
+ {0x801f, 179},
+ {0x8029, 179},
+ {0xc038, 179},
+ {0x8003, 209},
+ {0x8006, 209},
+ {0x800a, 209},
+ {0x800f, 209},
+ {0x8018, 209},
+ {0x801f, 209},
+ {0x8029, 209},
+ {0xc038, 209},
+ },
+ /* 116 */
+ {
+ {0x8002, 216},
+ {0x8009, 216},
+ {0x8017, 216},
+ {0xc028, 216},
+ {0x8002, 217},
+ {0x8009, 217},
+ {0x8017, 217},
+ {0xc028, 217},
+ {0x8002, 227},
+ {0x8009, 227},
+ {0x8017, 227},
+ {0xc028, 227},
+ {0x8002, 229},
+ {0x8009, 229},
+ {0x8017, 229},
+ {0xc028, 229},
+ },
+ /* 117 */
+ {
+ {0x8003, 216},
+ {0x8006, 216},
+ {0x800a, 216},
+ {0x800f, 216},
+ {0x8018, 216},
+ {0x801f, 216},
+ {0x8029, 216},
+ {0xc038, 216},
+ {0x8003, 217},
+ {0x8006, 217},
+ {0x800a, 217},
+ {0x800f, 217},
+ {0x8018, 217},
+ {0x801f, 217},
+ {0x8029, 217},
+ {0xc038, 217},
+ },
+ /* 118 */
+ {
+ {0x8003, 227},
+ {0x8006, 227},
+ {0x800a, 227},
+ {0x800f, 227},
+ {0x8018, 227},
+ {0x801f, 227},
+ {0x8029, 227},
+ {0xc038, 227},
+ {0x8003, 229},
+ {0x8006, 229},
+ {0x800a, 229},
+ {0x800f, 229},
+ {0x8018, 229},
+ {0x801f, 229},
+ {0x8029, 229},
+ {0xc038, 229},
+ },
+ /* 119 */
+ {
+ {0x8001, 230},
+ {0xc016, 230},
+ {0xc000, 129},
+ {0xc000, 132},
+ {0xc000, 133},
+ {0xc000, 134},
+ {0xc000, 136},
+ {0xc000, 146},
+ {0xc000, 154},
+ {0xc000, 156},
+ {0xc000, 160},
+ {0xc000, 163},
+ {0xc000, 164},
+ {0xc000, 169},
+ {0xc000, 170},
+ {0xc000, 173},
+ },
+ /* 120 */
+ {
+ {0x8002, 230},
+ {0x8009, 230},
+ {0x8017, 230},
+ {0xc028, 230},
+ {0x8001, 129},
+ {0xc016, 129},
+ {0x8001, 132},
+ {0xc016, 132},
+ {0x8001, 133},
+ {0xc016, 133},
+ {0x8001, 134},
+ {0xc016, 134},
+ {0x8001, 136},
+ {0xc016, 136},
+ {0x8001, 146},
+ {0xc016, 146},
+ },
+ /* 121 */
+ {
+ {0x8003, 230},
+ {0x8006, 230},
+ {0x800a, 230},
+ {0x800f, 230},
+ {0x8018, 230},
+ {0x801f, 230},
+ {0x8029, 230},
+ {0xc038, 230},
+ {0x8002, 129},
+ {0x8009, 129},
+ {0x8017, 129},
+ {0xc028, 129},
+ {0x8002, 132},
+ {0x8009, 132},
+ {0x8017, 132},
+ {0xc028, 132},
+ },
+ /* 122 */
+ {
+ {0x8003, 129},
+ {0x8006, 129},
+ {0x800a, 129},
+ {0x800f, 129},
+ {0x8018, 129},
+ {0x801f, 129},
+ {0x8029, 129},
+ {0xc038, 129},
+ {0x8003, 132},
+ {0x8006, 132},
+ {0x800a, 132},
+ {0x800f, 132},
+ {0x8018, 132},
+ {0x801f, 132},
+ {0x8029, 132},
+ {0xc038, 132},
+ },
+ /* 123 */
+ {
+ {0x8002, 133},
+ {0x8009, 133},
+ {0x8017, 133},
+ {0xc028, 133},
+ {0x8002, 134},
+ {0x8009, 134},
+ {0x8017, 134},
+ {0xc028, 134},
+ {0x8002, 136},
+ {0x8009, 136},
+ {0x8017, 136},
+ {0xc028, 136},
+ {0x8002, 146},
+ {0x8009, 146},
+ {0x8017, 146},
+ {0xc028, 146},
+ },
+ /* 124 */
+ {
+ {0x8003, 133},
+ {0x8006, 133},
+ {0x800a, 133},
+ {0x800f, 133},
+ {0x8018, 133},
+ {0x801f, 133},
+ {0x8029, 133},
+ {0xc038, 133},
+ {0x8003, 134},
+ {0x8006, 134},
+ {0x800a, 134},
+ {0x800f, 134},
+ {0x8018, 134},
+ {0x801f, 134},
+ {0x8029, 134},
+ {0xc038, 134},
+ },
+ /* 125 */
+ {
+ {0x8003, 136},
+ {0x8006, 136},
+ {0x800a, 136},
+ {0x800f, 136},
+ {0x8018, 136},
+ {0x801f, 136},
+ {0x8029, 136},
+ {0xc038, 136},
+ {0x8003, 146},
+ {0x8006, 146},
+ {0x800a, 146},
+ {0x800f, 146},
+ {0x8018, 146},
+ {0x801f, 146},
+ {0x8029, 146},
+ {0xc038, 146},
+ },
+ /* 126 */
+ {
+ {0x8001, 154},
+ {0xc016, 154},
+ {0x8001, 156},
+ {0xc016, 156},
+ {0x8001, 160},
+ {0xc016, 160},
+ {0x8001, 163},
+ {0xc016, 163},
+ {0x8001, 164},
+ {0xc016, 164},
+ {0x8001, 169},
+ {0xc016, 169},
+ {0x8001, 170},
+ {0xc016, 170},
+ {0x8001, 173},
+ {0xc016, 173},
+ },
+ /* 127 */
+ {
+ {0x8002, 154},
+ {0x8009, 154},
+ {0x8017, 154},
+ {0xc028, 154},
+ {0x8002, 156},
+ {0x8009, 156},
+ {0x8017, 156},
+ {0xc028, 156},
+ {0x8002, 160},
+ {0x8009, 160},
+ {0x8017, 160},
+ {0xc028, 160},
+ {0x8002, 163},
+ {0x8009, 163},
+ {0x8017, 163},
+ {0xc028, 163},
+ },
+ /* 128 */
+ {
+ {0x8003, 154},
+ {0x8006, 154},
+ {0x800a, 154},
+ {0x800f, 154},
+ {0x8018, 154},
+ {0x801f, 154},
+ {0x8029, 154},
+ {0xc038, 154},
+ {0x8003, 156},
+ {0x8006, 156},
+ {0x800a, 156},
+ {0x800f, 156},
+ {0x8018, 156},
+ {0x801f, 156},
+ {0x8029, 156},
+ {0xc038, 156},
+ },
+ /* 129 */
+ {
+ {0x8003, 160},
+ {0x8006, 160},
+ {0x800a, 160},
+ {0x800f, 160},
+ {0x8018, 160},
+ {0x801f, 160},
+ {0x8029, 160},
+ {0xc038, 160},
+ {0x8003, 163},
+ {0x8006, 163},
+ {0x800a, 163},
+ {0x800f, 163},
+ {0x8018, 163},
+ {0x801f, 163},
+ {0x8029, 163},
+ {0xc038, 163},
+ },
+ /* 130 */
+ {
+ {0x8002, 164},
+ {0x8009, 164},
+ {0x8017, 164},
+ {0xc028, 164},
+ {0x8002, 169},
+ {0x8009, 169},
+ {0x8017, 169},
+ {0xc028, 169},
+ {0x8002, 170},
+ {0x8009, 170},
+ {0x8017, 170},
+ {0xc028, 170},
+ {0x8002, 173},
+ {0x8009, 173},
+ {0x8017, 173},
+ {0xc028, 173},
+ },
+ /* 131 */
+ {
+ {0x8003, 164},
+ {0x8006, 164},
+ {0x800a, 164},
+ {0x800f, 164},
+ {0x8018, 164},
+ {0x801f, 164},
+ {0x8029, 164},
+ {0xc038, 164},
+ {0x8003, 169},
+ {0x8006, 169},
+ {0x800a, 169},
+ {0x800f, 169},
+ {0x8018, 169},
+ {0x801f, 169},
+ {0x8029, 169},
+ {0xc038, 169},
+ },
+ /* 132 */
+ {
+ {0x8003, 170},
+ {0x8006, 170},
+ {0x800a, 170},
+ {0x800f, 170},
+ {0x8018, 170},
+ {0x801f, 170},
+ {0x8029, 170},
+ {0xc038, 170},
+ {0x8003, 173},
+ {0x8006, 173},
+ {0x800a, 173},
+ {0x800f, 173},
+ {0x8018, 173},
+ {0x801f, 173},
+ {0x8029, 173},
+ {0xc038, 173},
+ },
+ /* 133 */
+ {
+ {0x89, 0},
+ {0x8a, 0},
+ {0x8c, 0},
+ {0x8d, 0},
+ {0x90, 0},
+ {0x91, 0},
+ {0x93, 0},
+ {0x96, 0},
+ {0x9c, 0},
+ {0x9f, 0},
+ {0xa3, 0},
+ {0xa6, 0},
+ {0xab, 0},
+ {0xae, 0},
+ {0xb5, 0},
+ {0xbe, 0},
+ },
+ /* 134 */
+ {
+ {0xc000, 178},
+ {0xc000, 181},
+ {0xc000, 185},
+ {0xc000, 186},
+ {0xc000, 187},
+ {0xc000, 189},
+ {0xc000, 190},
+ {0xc000, 196},
+ {0xc000, 198},
+ {0xc000, 228},
+ {0xc000, 232},
+ {0xc000, 233},
+ {0x94, 0},
+ {0x95, 0},
+ {0x97, 0},
+ {0x98, 0},
+ },
+ /* 135 */
+ {
+ {0x8001, 178},
+ {0xc016, 178},
+ {0x8001, 181},
+ {0xc016, 181},
+ {0x8001, 185},
+ {0xc016, 185},
+ {0x8001, 186},
+ {0xc016, 186},
+ {0x8001, 187},
+ {0xc016, 187},
+ {0x8001, 189},
+ {0xc016, 189},
+ {0x8001, 190},
+ {0xc016, 190},
+ {0x8001, 196},
+ {0xc016, 196},
+ },
+ /* 136 */
+ {
+ {0x8002, 178},
+ {0x8009, 178},
+ {0x8017, 178},
+ {0xc028, 178},
+ {0x8002, 181},
+ {0x8009, 181},
+ {0x8017, 181},
+ {0xc028, 181},
+ {0x8002, 185},
+ {0x8009, 185},
+ {0x8017, 185},
+ {0xc028, 185},
+ {0x8002, 186},
+ {0x8009, 186},
+ {0x8017, 186},
+ {0xc028, 186},
+ },
+ /* 137 */
+ {
+ {0x8003, 178},
+ {0x8006, 178},
+ {0x800a, 178},
+ {0x800f, 178},
+ {0x8018, 178},
+ {0x801f, 178},
+ {0x8029, 178},
+ {0xc038, 178},
+ {0x8003, 181},
+ {0x8006, 181},
+ {0x800a, 181},
+ {0x800f, 181},
+ {0x8018, 181},
+ {0x801f, 181},
+ {0x8029, 181},
+ {0xc038, 181},
+ },
+ /* 138 */
+ {
+ {0x8003, 185},
+ {0x8006, 185},
+ {0x800a, 185},
+ {0x800f, 185},
+ {0x8018, 185},
+ {0x801f, 185},
+ {0x8029, 185},
+ {0xc038, 185},
+ {0x8003, 186},
+ {0x8006, 186},
+ {0x800a, 186},
+ {0x800f, 186},
+ {0x8018, 186},
+ {0x801f, 186},
+ {0x8029, 186},
+ {0xc038, 186},
+ },
+ /* 139 */
+ {
+ {0x8002, 187},
+ {0x8009, 187},
+ {0x8017, 187},
+ {0xc028, 187},
+ {0x8002, 189},
+ {0x8009, 189},
+ {0x8017, 189},
+ {0xc028, 189},
+ {0x8002, 190},
+ {0x8009, 190},
+ {0x8017, 190},
+ {0xc028, 190},
+ {0x8002, 196},
+ {0x8009, 196},
+ {0x8017, 196},
+ {0xc028, 196},
+ },
+ /* 140 */
+ {
+ {0x8003, 187},
+ {0x8006, 187},
+ {0x800a, 187},
+ {0x800f, 187},
+ {0x8018, 187},
+ {0x801f, 187},
+ {0x8029, 187},
+ {0xc038, 187},
+ {0x8003, 189},
+ {0x8006, 189},
+ {0x800a, 189},
+ {0x800f, 189},
+ {0x8018, 189},
+ {0x801f, 189},
+ {0x8029, 189},
+ {0xc038, 189},
+ },
+ /* 141 */
+ {
+ {0x8003, 190},
+ {0x8006, 190},
+ {0x800a, 190},
+ {0x800f, 190},
+ {0x8018, 190},
+ {0x801f, 190},
+ {0x8029, 190},
+ {0xc038, 190},
+ {0x8003, 196},
+ {0x8006, 196},
+ {0x800a, 196},
+ {0x800f, 196},
+ {0x8018, 196},
+ {0x801f, 196},
+ {0x8029, 196},
+ {0xc038, 196},
+ },
+ /* 142 */
+ {
+ {0x8001, 198},
+ {0xc016, 198},
+ {0x8001, 228},
+ {0xc016, 228},
+ {0x8001, 232},
+ {0xc016, 232},
+ {0x8001, 233},
+ {0xc016, 233},
+ {0xc000, 1},
+ {0xc000, 135},
+ {0xc000, 137},
+ {0xc000, 138},
+ {0xc000, 139},
+ {0xc000, 140},
+ {0xc000, 141},
+ {0xc000, 143},
+ },
+ /* 143 */
+ {
+ {0x8002, 198},
+ {0x8009, 198},
+ {0x8017, 198},
+ {0xc028, 198},
+ {0x8002, 228},
+ {0x8009, 228},
+ {0x8017, 228},
+ {0xc028, 228},
+ {0x8002, 232},
+ {0x8009, 232},
+ {0x8017, 232},
+ {0xc028, 232},
+ {0x8002, 233},
+ {0x8009, 233},
+ {0x8017, 233},
+ {0xc028, 233},
+ },
+ /* 144 */
+ {
+ {0x8003, 198},
+ {0x8006, 198},
+ {0x800a, 198},
+ {0x800f, 198},
+ {0x8018, 198},
+ {0x801f, 198},
+ {0x8029, 198},
+ {0xc038, 198},
+ {0x8003, 228},
+ {0x8006, 228},
+ {0x800a, 228},
+ {0x800f, 228},
+ {0x8018, 228},
+ {0x801f, 228},
+ {0x8029, 228},
+ {0xc038, 228},
+ },
+ /* 145 */
+ {
+ {0x8003, 232},
+ {0x8006, 232},
+ {0x800a, 232},
+ {0x800f, 232},
+ {0x8018, 232},
+ {0x801f, 232},
+ {0x8029, 232},
+ {0xc038, 232},
+ {0x8003, 233},
+ {0x8006, 233},
+ {0x800a, 233},
+ {0x800f, 233},
+ {0x8018, 233},
+ {0x801f, 233},
+ {0x8029, 233},
+ {0xc038, 233},
+ },
+ /* 146 */
+ {
+ {0x8001, 1},
+ {0xc016, 1},
+ {0x8001, 135},
+ {0xc016, 135},
+ {0x8001, 137},
+ {0xc016, 137},
+ {0x8001, 138},
+ {0xc016, 138},
+ {0x8001, 139},
+ {0xc016, 139},
+ {0x8001, 140},
+ {0xc016, 140},
+ {0x8001, 141},
+ {0xc016, 141},
+ {0x8001, 143},
+ {0xc016, 143},
+ },
+ /* 147 */
+ {
+ {0x8002, 1},
+ {0x8009, 1},
+ {0x8017, 1},
+ {0xc028, 1},
+ {0x8002, 135},
+ {0x8009, 135},
+ {0x8017, 135},
+ {0xc028, 135},
+ {0x8002, 137},
+ {0x8009, 137},
+ {0x8017, 137},
+ {0xc028, 137},
+ {0x8002, 138},
+ {0x8009, 138},
+ {0x8017, 138},
+ {0xc028, 138},
+ },
+ /* 148 */
+ {
+ {0x8003, 1},
+ {0x8006, 1},
+ {0x800a, 1},
+ {0x800f, 1},
+ {0x8018, 1},
+ {0x801f, 1},
+ {0x8029, 1},
+ {0xc038, 1},
+ {0x8003, 135},
+ {0x8006, 135},
+ {0x800a, 135},
+ {0x800f, 135},
+ {0x8018, 135},
+ {0x801f, 135},
+ {0x8029, 135},
+ {0xc038, 135},
+ },
+ /* 149 */
+ {
+ {0x8003, 137},
+ {0x8006, 137},
+ {0x800a, 137},
+ {0x800f, 137},
+ {0x8018, 137},
+ {0x801f, 137},
+ {0x8029, 137},
+ {0xc038, 137},
+ {0x8003, 138},
+ {0x8006, 138},
+ {0x800a, 138},
+ {0x800f, 138},
+ {0x8018, 138},
+ {0x801f, 138},
+ {0x8029, 138},
+ {0xc038, 138},
+ },
+ /* 150 */
+ {
+ {0x8002, 139},
+ {0x8009, 139},
+ {0x8017, 139},
+ {0xc028, 139},
+ {0x8002, 140},
+ {0x8009, 140},
+ {0x8017, 140},
+ {0xc028, 140},
+ {0x8002, 141},
+ {0x8009, 141},
+ {0x8017, 141},
+ {0xc028, 141},
+ {0x8002, 143},
+ {0x8009, 143},
+ {0x8017, 143},
+ {0xc028, 143},
+ },
+ /* 151 */
+ {
+ {0x8003, 139},
+ {0x8006, 139},
+ {0x800a, 139},
+ {0x800f, 139},
+ {0x8018, 139},
+ {0x801f, 139},
+ {0x8029, 139},
+ {0xc038, 139},
+ {0x8003, 140},
+ {0x8006, 140},
+ {0x800a, 140},
+ {0x800f, 140},
+ {0x8018, 140},
+ {0x801f, 140},
+ {0x8029, 140},
+ {0xc038, 140},
+ },
+ /* 152 */
+ {
+ {0x8003, 141},
+ {0x8006, 141},
+ {0x800a, 141},
+ {0x800f, 141},
+ {0x8018, 141},
+ {0x801f, 141},
+ {0x8029, 141},
+ {0xc038, 141},
+ {0x8003, 143},
+ {0x8006, 143},
+ {0x800a, 143},
+ {0x800f, 143},
+ {0x8018, 143},
+ {0x801f, 143},
+ {0x8029, 143},
+ {0xc038, 143},
+ },
+ /* 153 */
+ {
+ {0x9d, 0},
+ {0x9e, 0},
+ {0xa0, 0},
+ {0xa1, 0},
+ {0xa4, 0},
+ {0xa5, 0},
+ {0xa7, 0},
+ {0xa8, 0},
+ {0xac, 0},
+ {0xad, 0},
+ {0xaf, 0},
+ {0xb1, 0},
+ {0xb6, 0},
+ {0xb9, 0},
+ {0xbf, 0},
+ {0xcf, 0},
+ },
+ /* 154 */
+ {
+ {0xc000, 147},
+ {0xc000, 149},
+ {0xc000, 150},
+ {0xc000, 151},
+ {0xc000, 152},
+ {0xc000, 155},
+ {0xc000, 157},
+ {0xc000, 158},
+ {0xc000, 165},
+ {0xc000, 166},
+ {0xc000, 168},
+ {0xc000, 174},
+ {0xc000, 175},
+ {0xc000, 180},
+ {0xc000, 182},
+ {0xc000, 183},
+ },
+ /* 155 */
+ {
+ {0x8001, 147},
+ {0xc016, 147},
+ {0x8001, 149},
+ {0xc016, 149},
+ {0x8001, 150},
+ {0xc016, 150},
+ {0x8001, 151},
+ {0xc016, 151},
+ {0x8001, 152},
+ {0xc016, 152},
+ {0x8001, 155},
+ {0xc016, 155},
+ {0x8001, 157},
+ {0xc016, 157},
+ {0x8001, 158},
+ {0xc016, 158},
+ },
+ /* 156 */
+ {
+ {0x8002, 147},
+ {0x8009, 147},
+ {0x8017, 147},
+ {0xc028, 147},
+ {0x8002, 149},
+ {0x8009, 149},
+ {0x8017, 149},
+ {0xc028, 149},
+ {0x8002, 150},
+ {0x8009, 150},
+ {0x8017, 150},
+ {0xc028, 150},
+ {0x8002, 151},
+ {0x8009, 151},
+ {0x8017, 151},
+ {0xc028, 151},
+ },
+ /* 157 */
+ {
+ {0x8003, 147},
+ {0x8006, 147},
+ {0x800a, 147},
+ {0x800f, 147},
+ {0x8018, 147},
+ {0x801f, 147},
+ {0x8029, 147},
+ {0xc038, 147},
+ {0x8003, 149},
+ {0x8006, 149},
+ {0x800a, 149},
+ {0x800f, 149},
+ {0x8018, 149},
+ {0x801f, 149},
+ {0x8029, 149},
+ {0xc038, 149},
+ },
+ /* 158 */
+ {
+ {0x8003, 150},
+ {0x8006, 150},
+ {0x800a, 150},
+ {0x800f, 150},
+ {0x8018, 150},
+ {0x801f, 150},
+ {0x8029, 150},
+ {0xc038, 150},
+ {0x8003, 151},
+ {0x8006, 151},
+ {0x800a, 151},
+ {0x800f, 151},
+ {0x8018, 151},
+ {0x801f, 151},
+ {0x8029, 151},
+ {0xc038, 151},
+ },
+ /* 159 */
+ {
+ {0x8002, 152},
+ {0x8009, 152},
+ {0x8017, 152},
+ {0xc028, 152},
+ {0x8002, 155},
+ {0x8009, 155},
+ {0x8017, 155},
+ {0xc028, 155},
+ {0x8002, 157},
+ {0x8009, 157},
+ {0x8017, 157},
+ {0xc028, 157},
+ {0x8002, 158},
+ {0x8009, 158},
+ {0x8017, 158},
+ {0xc028, 158},
+ },
+ /* 160 */
+ {
+ {0x8003, 152},
+ {0x8006, 152},
+ {0x800a, 152},
+ {0x800f, 152},
+ {0x8018, 152},
+ {0x801f, 152},
+ {0x8029, 152},
+ {0xc038, 152},
+ {0x8003, 155},
+ {0x8006, 155},
+ {0x800a, 155},
+ {0x800f, 155},
+ {0x8018, 155},
+ {0x801f, 155},
+ {0x8029, 155},
+ {0xc038, 155},
+ },
+ /* 161 */
+ {
+ {0x8003, 157},
+ {0x8006, 157},
+ {0x800a, 157},
+ {0x800f, 157},
+ {0x8018, 157},
+ {0x801f, 157},
+ {0x8029, 157},
+ {0xc038, 157},
+ {0x8003, 158},
+ {0x8006, 158},
+ {0x800a, 158},
+ {0x800f, 158},
+ {0x8018, 158},
+ {0x801f, 158},
+ {0x8029, 158},
+ {0xc038, 158},
+ },
+ /* 162 */
+ {
+ {0x8001, 165},
+ {0xc016, 165},
+ {0x8001, 166},
+ {0xc016, 166},
+ {0x8001, 168},
+ {0xc016, 168},
+ {0x8001, 174},
+ {0xc016, 174},
+ {0x8001, 175},
+ {0xc016, 175},
+ {0x8001, 180},
+ {0xc016, 180},
+ {0x8001, 182},
+ {0xc016, 182},
+ {0x8001, 183},
+ {0xc016, 183},
+ },
+ /* 163 */
+ {
+ {0x8002, 165},
+ {0x8009, 165},
+ {0x8017, 165},
+ {0xc028, 165},
+ {0x8002, 166},
+ {0x8009, 166},
+ {0x8017, 166},
+ {0xc028, 166},
+ {0x8002, 168},
+ {0x8009, 168},
+ {0x8017, 168},
+ {0xc028, 168},
+ {0x8002, 174},
+ {0x8009, 174},
+ {0x8017, 174},
+ {0xc028, 174},
+ },
+ /* 164 */
+ {
+ {0x8003, 165},
+ {0x8006, 165},
+ {0x800a, 165},
+ {0x800f, 165},
+ {0x8018, 165},
+ {0x801f, 165},
+ {0x8029, 165},
+ {0xc038, 165},
+ {0x8003, 166},
+ {0x8006, 166},
+ {0x800a, 166},
+ {0x800f, 166},
+ {0x8018, 166},
+ {0x801f, 166},
+ {0x8029, 166},
+ {0xc038, 166},
+ },
+ /* 165 */
+ {
+ {0x8003, 168},
+ {0x8006, 168},
+ {0x800a, 168},
+ {0x800f, 168},
+ {0x8018, 168},
+ {0x801f, 168},
+ {0x8029, 168},
+ {0xc038, 168},
+ {0x8003, 174},
+ {0x8006, 174},
+ {0x800a, 174},
+ {0x800f, 174},
+ {0x8018, 174},
+ {0x801f, 174},
+ {0x8029, 174},
+ {0xc038, 174},
+ },
+ /* 166 */
+ {
+ {0x8002, 175},
+ {0x8009, 175},
+ {0x8017, 175},
+ {0xc028, 175},
+ {0x8002, 180},
+ {0x8009, 180},
+ {0x8017, 180},
+ {0xc028, 180},
+ {0x8002, 182},
+ {0x8009, 182},
+ {0x8017, 182},
+ {0xc028, 182},
+ {0x8002, 183},
+ {0x8009, 183},
+ {0x8017, 183},
+ {0xc028, 183},
+ },
+ /* 167 */
+ {
+ {0x8003, 175},
+ {0x8006, 175},
+ {0x800a, 175},
+ {0x800f, 175},
+ {0x8018, 175},
+ {0x801f, 175},
+ {0x8029, 175},
+ {0xc038, 175},
+ {0x8003, 180},
+ {0x8006, 180},
+ {0x800a, 180},
+ {0x800f, 180},
+ {0x8018, 180},
+ {0x801f, 180},
+ {0x8029, 180},
+ {0xc038, 180},
+ },
+ /* 168 */
+ {
+ {0x8003, 182},
+ {0x8006, 182},
+ {0x800a, 182},
+ {0x800f, 182},
+ {0x8018, 182},
+ {0x801f, 182},
+ {0x8029, 182},
+ {0xc038, 182},
+ {0x8003, 183},
+ {0x8006, 183},
+ {0x800a, 183},
+ {0x800f, 183},
+ {0x8018, 183},
+ {0x801f, 183},
+ {0x8029, 183},
+ {0xc038, 183},
+ },
+ /* 169 */
+ {
+ {0xc000, 188},
+ {0xc000, 191},
+ {0xc000, 197},
+ {0xc000, 231},
+ {0xc000, 239},
+ {0xb0, 0},
+ {0xb2, 0},
+ {0xb3, 0},
+ {0xb7, 0},
+ {0xb8, 0},
+ {0xba, 0},
+ {0xbb, 0},
+ {0xc0, 0},
+ {0xc7, 0},
+ {0xd0, 0},
+ {0xdf, 0},
+ },
+ /* 170 */
+ {
+ {0x8001, 188},
+ {0xc016, 188},
+ {0x8001, 191},
+ {0xc016, 191},
+ {0x8001, 197},
+ {0xc016, 197},
+ {0x8001, 231},
+ {0xc016, 231},
+ {0x8001, 239},
+ {0xc016, 239},
+ {0xc000, 9},
+ {0xc000, 142},
+ {0xc000, 144},
+ {0xc000, 145},
+ {0xc000, 148},
+ {0xc000, 159},
+ },
+ /* 171 */
+ {
+ {0x8002, 188},
+ {0x8009, 188},
+ {0x8017, 188},
+ {0xc028, 188},
+ {0x8002, 191},
+ {0x8009, 191},
+ {0x8017, 191},
+ {0xc028, 191},
+ {0x8002, 197},
+ {0x8009, 197},
+ {0x8017, 197},
+ {0xc028, 197},
+ {0x8002, 231},
+ {0x8009, 231},
+ {0x8017, 231},
+ {0xc028, 231},
+ },
+ /* 172 */
+ {
+ {0x8003, 188},
+ {0x8006, 188},
+ {0x800a, 188},
+ {0x800f, 188},
+ {0x8018, 188},
+ {0x801f, 188},
+ {0x8029, 188},
+ {0xc038, 188},
+ {0x8003, 191},
+ {0x8006, 191},
+ {0x800a, 191},
+ {0x800f, 191},
+ {0x8018, 191},
+ {0x801f, 191},
+ {0x8029, 191},
+ {0xc038, 191},
+ },
+ /* 173 */
+ {
+ {0x8003, 197},
+ {0x8006, 197},
+ {0x800a, 197},
+ {0x800f, 197},
+ {0x8018, 197},
+ {0x801f, 197},
+ {0x8029, 197},
+ {0xc038, 197},
+ {0x8003, 231},
+ {0x8006, 231},
+ {0x800a, 231},
+ {0x800f, 231},
+ {0x8018, 231},
+ {0x801f, 231},
+ {0x8029, 231},
+ {0xc038, 231},
+ },
+ /* 174 */
+ {
+ {0x8002, 239},
+ {0x8009, 239},
+ {0x8017, 239},
+ {0xc028, 239},
+ {0x8001, 9},
+ {0xc016, 9},
+ {0x8001, 142},
+ {0xc016, 142},
+ {0x8001, 144},
+ {0xc016, 144},
+ {0x8001, 145},
+ {0xc016, 145},
+ {0x8001, 148},
+ {0xc016, 148},
+ {0x8001, 159},
+ {0xc016, 159},
+ },
+ /* 175 */
+ {
+ {0x8003, 239},
+ {0x8006, 239},
+ {0x800a, 239},
+ {0x800f, 239},
+ {0x8018, 239},
+ {0x801f, 239},
+ {0x8029, 239},
+ {0xc038, 239},
+ {0x8002, 9},
+ {0x8009, 9},
+ {0x8017, 9},
+ {0xc028, 9},
+ {0x8002, 142},
+ {0x8009, 142},
+ {0x8017, 142},
+ {0xc028, 142},
+ },
+ /* 176 */
+ {
+ {0x8003, 9},
+ {0x8006, 9},
+ {0x800a, 9},
+ {0x800f, 9},
+ {0x8018, 9},
+ {0x801f, 9},
+ {0x8029, 9},
+ {0xc038, 9},
+ {0x8003, 142},
+ {0x8006, 142},
+ {0x800a, 142},
+ {0x800f, 142},
+ {0x8018, 142},
+ {0x801f, 142},
+ {0x8029, 142},
+ {0xc038, 142},
+ },
+ /* 177 */
+ {
+ {0x8002, 144},
+ {0x8009, 144},
+ {0x8017, 144},
+ {0xc028, 144},
+ {0x8002, 145},
+ {0x8009, 145},
+ {0x8017, 145},
+ {0xc028, 145},
+ {0x8002, 148},
+ {0x8009, 148},
+ {0x8017, 148},
+ {0xc028, 148},
+ {0x8002, 159},
+ {0x8009, 159},
+ {0x8017, 159},
+ {0xc028, 159},
+ },
+ /* 178 */
+ {
+ {0x8003, 144},
+ {0x8006, 144},
+ {0x800a, 144},
+ {0x800f, 144},
+ {0x8018, 144},
+ {0x801f, 144},
+ {0x8029, 144},
+ {0xc038, 144},
+ {0x8003, 145},
+ {0x8006, 145},
+ {0x800a, 145},
+ {0x800f, 145},
+ {0x8018, 145},
+ {0x801f, 145},
+ {0x8029, 145},
+ {0xc038, 145},
+ },
+ /* 179 */
+ {
+ {0x8003, 148},
+ {0x8006, 148},
+ {0x800a, 148},
+ {0x800f, 148},
+ {0x8018, 148},
+ {0x801f, 148},
+ {0x8029, 148},
+ {0xc038, 148},
+ {0x8003, 159},
+ {0x8006, 159},
+ {0x800a, 159},
+ {0x800f, 159},
+ {0x8018, 159},
+ {0x801f, 159},
+ {0x8029, 159},
+ {0xc038, 159},
+ },
+ /* 180 */
+ {
+ {0xc000, 171},
+ {0xc000, 206},
+ {0xc000, 215},
+ {0xc000, 225},
+ {0xc000, 236},
+ {0xc000, 237},
+ {0xbc, 0},
+ {0xbd, 0},
+ {0xc1, 0},
+ {0xc4, 0},
+ {0xc8, 0},
+ {0xcb, 0},
+ {0xd1, 0},
+ {0xd8, 0},
+ {0xe0, 0},
+ {0xee, 0},
+ },
+ /* 181 */
+ {
+ {0x8001, 171},
+ {0xc016, 171},
+ {0x8001, 206},
+ {0xc016, 206},
+ {0x8001, 215},
+ {0xc016, 215},
+ {0x8001, 225},
+ {0xc016, 225},
+ {0x8001, 236},
+ {0xc016, 236},
+ {0x8001, 237},
+ {0xc016, 237},
+ {0xc000, 199},
+ {0xc000, 207},
+ {0xc000, 234},
+ {0xc000, 235},
+ },
+ /* 182 */
+ {
+ {0x8002, 171},
+ {0x8009, 171},
+ {0x8017, 171},
+ {0xc028, 171},
+ {0x8002, 206},
+ {0x8009, 206},
+ {0x8017, 206},
+ {0xc028, 206},
+ {0x8002, 215},
+ {0x8009, 215},
+ {0x8017, 215},
+ {0xc028, 215},
+ {0x8002, 225},
+ {0x8009, 225},
+ {0x8017, 225},
+ {0xc028, 225},
+ },
+ /* 183 */
+ {
+ {0x8003, 171},
+ {0x8006, 171},
+ {0x800a, 171},
+ {0x800f, 171},
+ {0x8018, 171},
+ {0x801f, 171},
+ {0x8029, 171},
+ {0xc038, 171},
+ {0x8003, 206},
+ {0x8006, 206},
+ {0x800a, 206},
+ {0x800f, 206},
+ {0x8018, 206},
+ {0x801f, 206},
+ {0x8029, 206},
+ {0xc038, 206},
+ },
+ /* 184 */
+ {
+ {0x8003, 215},
+ {0x8006, 215},
+ {0x800a, 215},
+ {0x800f, 215},
+ {0x8018, 215},
+ {0x801f, 215},
+ {0x8029, 215},
+ {0xc038, 215},
+ {0x8003, 225},
+ {0x8006, 225},
+ {0x800a, 225},
+ {0x800f, 225},
+ {0x8018, 225},
+ {0x801f, 225},
+ {0x8029, 225},
+ {0xc038, 225},
+ },
+ /* 185 */
+ {
+ {0x8002, 236},
+ {0x8009, 236},
+ {0x8017, 236},
+ {0xc028, 236},
+ {0x8002, 237},
+ {0x8009, 237},
+ {0x8017, 237},
+ {0xc028, 237},
+ {0x8001, 199},
+ {0xc016, 199},
+ {0x8001, 207},
+ {0xc016, 207},
+ {0x8001, 234},
+ {0xc016, 234},
+ {0x8001, 235},
+ {0xc016, 235},
+ },
+ /* 186 */
+ {
+ {0x8003, 236},
+ {0x8006, 236},
+ {0x800a, 236},
+ {0x800f, 236},
+ {0x8018, 236},
+ {0x801f, 236},
+ {0x8029, 236},
+ {0xc038, 236},
+ {0x8003, 237},
+ {0x8006, 237},
+ {0x800a, 237},
+ {0x800f, 237},
+ {0x8018, 237},
+ {0x801f, 237},
+ {0x8029, 237},
+ {0xc038, 237},
+ },
+ /* 187 */
+ {
+ {0x8002, 199},
+ {0x8009, 199},
+ {0x8017, 199},
+ {0xc028, 199},
+ {0x8002, 207},
+ {0x8009, 207},
+ {0x8017, 207},
+ {0xc028, 207},
+ {0x8002, 234},
+ {0x8009, 234},
+ {0x8017, 234},
+ {0xc028, 234},
+ {0x8002, 235},
+ {0x8009, 235},
+ {0x8017, 235},
+ {0xc028, 235},
+ },
+ /* 188 */
+ {
+ {0x8003, 199},
+ {0x8006, 199},
+ {0x800a, 199},
+ {0x800f, 199},
+ {0x8018, 199},
+ {0x801f, 199},
+ {0x8029, 199},
+ {0xc038, 199},
+ {0x8003, 207},
+ {0x8006, 207},
+ {0x800a, 207},
+ {0x800f, 207},
+ {0x8018, 207},
+ {0x801f, 207},
+ {0x8029, 207},
+ {0xc038, 207},
+ },
+ /* 189 */
+ {
+ {0x8003, 234},
+ {0x8006, 234},
+ {0x800a, 234},
+ {0x800f, 234},
+ {0x8018, 234},
+ {0x801f, 234},
+ {0x8029, 234},
+ {0xc038, 234},
+ {0x8003, 235},
+ {0x8006, 235},
+ {0x800a, 235},
+ {0x800f, 235},
+ {0x8018, 235},
+ {0x801f, 235},
+ {0x8029, 235},
+ {0xc038, 235},
+ },
+ /* 190 */
+ {
+ {0xc2, 0},
+ {0xc3, 0},
+ {0xc5, 0},
+ {0xc6, 0},
+ {0xc9, 0},
+ {0xca, 0},
+ {0xcc, 0},
+ {0xcd, 0},
+ {0xd2, 0},
+ {0xd5, 0},
+ {0xd9, 0},
+ {0xdc, 0},
+ {0xe1, 0},
+ {0xe7, 0},
+ {0xef, 0},
+ {0xf6, 0},
+ },
+ /* 191 */
+ {
+ {0xc000, 192},
+ {0xc000, 193},
+ {0xc000, 200},
+ {0xc000, 201},
+ {0xc000, 202},
+ {0xc000, 205},
+ {0xc000, 210},
+ {0xc000, 213},
+ {0xc000, 218},
+ {0xc000, 219},
+ {0xc000, 238},
+ {0xc000, 240},
+ {0xc000, 242},
+ {0xc000, 243},
+ {0xc000, 255},
+ {0xce, 0},
+ },
+ /* 192 */
+ {
+ {0x8001, 192},
+ {0xc016, 192},
+ {0x8001, 193},
+ {0xc016, 193},
+ {0x8001, 200},
+ {0xc016, 200},
+ {0x8001, 201},
+ {0xc016, 201},
+ {0x8001, 202},
+ {0xc016, 202},
+ {0x8001, 205},
+ {0xc016, 205},
+ {0x8001, 210},
+ {0xc016, 210},
+ {0x8001, 213},
+ {0xc016, 213},
+ },
+ /* 193 */
+ {
+ {0x8002, 192},
+ {0x8009, 192},
+ {0x8017, 192},
+ {0xc028, 192},
+ {0x8002, 193},
+ {0x8009, 193},
+ {0x8017, 193},
+ {0xc028, 193},
+ {0x8002, 200},
+ {0x8009, 200},
+ {0x8017, 200},
+ {0xc028, 200},
+ {0x8002, 201},
+ {0x8009, 201},
+ {0x8017, 201},
+ {0xc028, 201},
+ },
+ /* 194 */
+ {
+ {0x8003, 192},
+ {0x8006, 192},
+ {0x800a, 192},
+ {0x800f, 192},
+ {0x8018, 192},
+ {0x801f, 192},
+ {0x8029, 192},
+ {0xc038, 192},
+ {0x8003, 193},
+ {0x8006, 193},
+ {0x800a, 193},
+ {0x800f, 193},
+ {0x8018, 193},
+ {0x801f, 193},
+ {0x8029, 193},
+ {0xc038, 193},
+ },
+ /* 195 */
+ {
+ {0x8003, 200},
+ {0x8006, 200},
+ {0x800a, 200},
+ {0x800f, 200},
+ {0x8018, 200},
+ {0x801f, 200},
+ {0x8029, 200},
+ {0xc038, 200},
+ {0x8003, 201},
+ {0x8006, 201},
+ {0x800a, 201},
+ {0x800f, 201},
+ {0x8018, 201},
+ {0x801f, 201},
+ {0x8029, 201},
+ {0xc038, 201},
+ },
+ /* 196 */
+ {
+ {0x8002, 202},
+ {0x8009, 202},
+ {0x8017, 202},
+ {0xc028, 202},
+ {0x8002, 205},
+ {0x8009, 205},
+ {0x8017, 205},
+ {0xc028, 205},
+ {0x8002, 210},
+ {0x8009, 210},
+ {0x8017, 210},
+ {0xc028, 210},
+ {0x8002, 213},
+ {0x8009, 213},
+ {0x8017, 213},
+ {0xc028, 213},
+ },
+ /* 197 */
+ {
+ {0x8003, 202},
+ {0x8006, 202},
+ {0x800a, 202},
+ {0x800f, 202},
+ {0x8018, 202},
+ {0x801f, 202},
+ {0x8029, 202},
+ {0xc038, 202},
+ {0x8003, 205},
+ {0x8006, 205},
+ {0x800a, 205},
+ {0x800f, 205},
+ {0x8018, 205},
+ {0x801f, 205},
+ {0x8029, 205},
+ {0xc038, 205},
+ },
+ /* 198 */
+ {
+ {0x8003, 210},
+ {0x8006, 210},
+ {0x800a, 210},
+ {0x800f, 210},
+ {0x8018, 210},
+ {0x801f, 210},
+ {0x8029, 210},
+ {0xc038, 210},
+ {0x8003, 213},
+ {0x8006, 213},
+ {0x800a, 213},
+ {0x800f, 213},
+ {0x8018, 213},
+ {0x801f, 213},
+ {0x8029, 213},
+ {0xc038, 213},
+ },
+ /* 199 */
+ {
+ {0x8001, 218},
+ {0xc016, 218},
+ {0x8001, 219},
+ {0xc016, 219},
+ {0x8001, 238},
+ {0xc016, 238},
+ {0x8001, 240},
+ {0xc016, 240},
+ {0x8001, 242},
+ {0xc016, 242},
+ {0x8001, 243},
+ {0xc016, 243},
+ {0x8001, 255},
+ {0xc016, 255},
+ {0xc000, 203},
+ {0xc000, 204},
+ },
+ /* 200 */
+ {
+ {0x8002, 218},
+ {0x8009, 218},
+ {0x8017, 218},
+ {0xc028, 218},
+ {0x8002, 219},
+ {0x8009, 219},
+ {0x8017, 219},
+ {0xc028, 219},
+ {0x8002, 238},
+ {0x8009, 238},
+ {0x8017, 238},
+ {0xc028, 238},
+ {0x8002, 240},
+ {0x8009, 240},
+ {0x8017, 240},
+ {0xc028, 240},
+ },
+ /* 201 */
+ {
+ {0x8003, 218},
+ {0x8006, 218},
+ {0x800a, 218},
+ {0x800f, 218},
+ {0x8018, 218},
+ {0x801f, 218},
+ {0x8029, 218},
+ {0xc038, 218},
+ {0x8003, 219},
+ {0x8006, 219},
+ {0x800a, 219},
+ {0x800f, 219},
+ {0x8018, 219},
+ {0x801f, 219},
+ {0x8029, 219},
+ {0xc038, 219},
+ },
+ /* 202 */
+ {
+ {0x8003, 238},
+ {0x8006, 238},
+ {0x800a, 238},
+ {0x800f, 238},
+ {0x8018, 238},
+ {0x801f, 238},
+ {0x8029, 238},
+ {0xc038, 238},
+ {0x8003, 240},
+ {0x8006, 240},
+ {0x800a, 240},
+ {0x800f, 240},
+ {0x8018, 240},
+ {0x801f, 240},
+ {0x8029, 240},
+ {0xc038, 240},
+ },
+ /* 203 */
+ {
+ {0x8002, 242},
+ {0x8009, 242},
+ {0x8017, 242},
+ {0xc028, 242},
+ {0x8002, 243},
+ {0x8009, 243},
+ {0x8017, 243},
+ {0xc028, 243},
+ {0x8002, 255},
+ {0x8009, 255},
+ {0x8017, 255},
+ {0xc028, 255},
+ {0x8001, 203},
+ {0xc016, 203},
+ {0x8001, 204},
+ {0xc016, 204},
+ },
+ /* 204 */
+ {
+ {0x8003, 242},
+ {0x8006, 242},
+ {0x800a, 242},
+ {0x800f, 242},
+ {0x8018, 242},
+ {0x801f, 242},
+ {0x8029, 242},
+ {0xc038, 242},
+ {0x8003, 243},
+ {0x8006, 243},
+ {0x800a, 243},
+ {0x800f, 243},
+ {0x8018, 243},
+ {0x801f, 243},
+ {0x8029, 243},
+ {0xc038, 243},
+ },
+ /* 205 */
+ {
+ {0x8003, 255},
+ {0x8006, 255},
+ {0x800a, 255},
+ {0x800f, 255},
+ {0x8018, 255},
+ {0x801f, 255},
+ {0x8029, 255},
+ {0xc038, 255},
+ {0x8002, 203},
+ {0x8009, 203},
+ {0x8017, 203},
+ {0xc028, 203},
+ {0x8002, 204},
+ {0x8009, 204},
+ {0x8017, 204},
+ {0xc028, 204},
+ },
+ /* 206 */
+ {
+ {0x8003, 203},
+ {0x8006, 203},
+ {0x800a, 203},
+ {0x800f, 203},
+ {0x8018, 203},
+ {0x801f, 203},
+ {0x8029, 203},
+ {0xc038, 203},
+ {0x8003, 204},
+ {0x8006, 204},
+ {0x800a, 204},
+ {0x800f, 204},
+ {0x8018, 204},
+ {0x801f, 204},
+ {0x8029, 204},
+ {0xc038, 204},
+ },
+ /* 207 */
+ {
+ {0xd3, 0},
+ {0xd4, 0},
+ {0xd6, 0},
+ {0xd7, 0},
+ {0xda, 0},
+ {0xdb, 0},
+ {0xdd, 0},
+ {0xde, 0},
+ {0xe2, 0},
+ {0xe4, 0},
+ {0xe8, 0},
+ {0xeb, 0},
+ {0xf0, 0},
+ {0xf3, 0},
+ {0xf7, 0},
+ {0xfa, 0},
+ },
+ /* 208 */
+ {
+ {0xc000, 211},
+ {0xc000, 212},
+ {0xc000, 214},
+ {0xc000, 221},
+ {0xc000, 222},
+ {0xc000, 223},
+ {0xc000, 241},
+ {0xc000, 244},
+ {0xc000, 245},
+ {0xc000, 246},
+ {0xc000, 247},
+ {0xc000, 248},
+ {0xc000, 250},
+ {0xc000, 251},
+ {0xc000, 252},
+ {0xc000, 253},
+ },
+ /* 209 */
+ {
+ {0x8001, 211},
+ {0xc016, 211},
+ {0x8001, 212},
+ {0xc016, 212},
+ {0x8001, 214},
+ {0xc016, 214},
+ {0x8001, 221},
+ {0xc016, 221},
+ {0x8001, 222},
+ {0xc016, 222},
+ {0x8001, 223},
+ {0xc016, 223},
+ {0x8001, 241},
+ {0xc016, 241},
+ {0x8001, 244},
+ {0xc016, 244},
+ },
+ /* 210 */
+ {
+ {0x8002, 211},
+ {0x8009, 211},
+ {0x8017, 211},
+ {0xc028, 211},
+ {0x8002, 212},
+ {0x8009, 212},
+ {0x8017, 212},
+ {0xc028, 212},
+ {0x8002, 214},
+ {0x8009, 214},
+ {0x8017, 214},
+ {0xc028, 214},
+ {0x8002, 221},
+ {0x8009, 221},
+ {0x8017, 221},
+ {0xc028, 221},
+ },
+ /* 211 */
+ {
+ {0x8003, 211},
+ {0x8006, 211},
+ {0x800a, 211},
+ {0x800f, 211},
+ {0x8018, 211},
+ {0x801f, 211},
+ {0x8029, 211},
+ {0xc038, 211},
+ {0x8003, 212},
+ {0x8006, 212},
+ {0x800a, 212},
+ {0x800f, 212},
+ {0x8018, 212},
+ {0x801f, 212},
+ {0x8029, 212},
+ {0xc038, 212},
+ },
+ /* 212 */
+ {
+ {0x8003, 214},
+ {0x8006, 214},
+ {0x800a, 214},
+ {0x800f, 214},
+ {0x8018, 214},
+ {0x801f, 214},
+ {0x8029, 214},
+ {0xc038, 214},
+ {0x8003, 221},
+ {0x8006, 221},
+ {0x800a, 221},
+ {0x800f, 221},
+ {0x8018, 221},
+ {0x801f, 221},
+ {0x8029, 221},
+ {0xc038, 221},
+ },
+ /* 213 */
+ {
+ {0x8002, 222},
+ {0x8009, 222},
+ {0x8017, 222},
+ {0xc028, 222},
+ {0x8002, 223},
+ {0x8009, 223},
+ {0x8017, 223},
+ {0xc028, 223},
+ {0x8002, 241},
+ {0x8009, 241},
+ {0x8017, 241},
+ {0xc028, 241},
+ {0x8002, 244},
+ {0x8009, 244},
+ {0x8017, 244},
+ {0xc028, 244},
+ },
+ /* 214 */
+ {
+ {0x8003, 222},
+ {0x8006, 222},
+ {0x800a, 222},
+ {0x800f, 222},
+ {0x8018, 222},
+ {0x801f, 222},
+ {0x8029, 222},
+ {0xc038, 222},
+ {0x8003, 223},
+ {0x8006, 223},
+ {0x800a, 223},
+ {0x800f, 223},
+ {0x8018, 223},
+ {0x801f, 223},
+ {0x8029, 223},
+ {0xc038, 223},
+ },
+ /* 215 */
+ {
+ {0x8003, 241},
+ {0x8006, 241},
+ {0x800a, 241},
+ {0x800f, 241},
+ {0x8018, 241},
+ {0x801f, 241},
+ {0x8029, 241},
+ {0xc038, 241},
+ {0x8003, 244},
+ {0x8006, 244},
+ {0x800a, 244},
+ {0x800f, 244},
+ {0x8018, 244},
+ {0x801f, 244},
+ {0x8029, 244},
+ {0xc038, 244},
+ },
+ /* 216 */
+ {
+ {0x8001, 245},
+ {0xc016, 245},
+ {0x8001, 246},
+ {0xc016, 246},
+ {0x8001, 247},
+ {0xc016, 247},
+ {0x8001, 248},
+ {0xc016, 248},
+ {0x8001, 250},
+ {0xc016, 250},
+ {0x8001, 251},
+ {0xc016, 251},
+ {0x8001, 252},
+ {0xc016, 252},
+ {0x8001, 253},
+ {0xc016, 253},
+ },
+ /* 217 */
+ {
+ {0x8002, 245},
+ {0x8009, 245},
+ {0x8017, 245},
+ {0xc028, 245},
+ {0x8002, 246},
+ {0x8009, 246},
+ {0x8017, 246},
+ {0xc028, 246},
+ {0x8002, 247},
+ {0x8009, 247},
+ {0x8017, 247},
+ {0xc028, 247},
+ {0x8002, 248},
+ {0x8009, 248},
+ {0x8017, 248},
+ {0xc028, 248},
+ },
+ /* 218 */
+ {
+ {0x8003, 245},
+ {0x8006, 245},
+ {0x800a, 245},
+ {0x800f, 245},
+ {0x8018, 245},
+ {0x801f, 245},
+ {0x8029, 245},
+ {0xc038, 245},
+ {0x8003, 246},
+ {0x8006, 246},
+ {0x800a, 246},
+ {0x800f, 246},
+ {0x8018, 246},
+ {0x801f, 246},
+ {0x8029, 246},
+ {0xc038, 246},
+ },
+ /* 219 */
+ {
+ {0x8003, 247},
+ {0x8006, 247},
+ {0x800a, 247},
+ {0x800f, 247},
+ {0x8018, 247},
+ {0x801f, 247},
+ {0x8029, 247},
+ {0xc038, 247},
+ {0x8003, 248},
+ {0x8006, 248},
+ {0x800a, 248},
+ {0x800f, 248},
+ {0x8018, 248},
+ {0x801f, 248},
+ {0x8029, 248},
+ {0xc038, 248},
+ },
+ /* 220 */
+ {
+ {0x8002, 250},
+ {0x8009, 250},
+ {0x8017, 250},
+ {0xc028, 250},
+ {0x8002, 251},
+ {0x8009, 251},
+ {0x8017, 251},
+ {0xc028, 251},
+ {0x8002, 252},
+ {0x8009, 252},
+ {0x8017, 252},
+ {0xc028, 252},
+ {0x8002, 253},
+ {0x8009, 253},
+ {0x8017, 253},
+ {0xc028, 253},
+ },
+ /* 221 */
+ {
+ {0x8003, 250},
+ {0x8006, 250},
+ {0x800a, 250},
+ {0x800f, 250},
+ {0x8018, 250},
+ {0x801f, 250},
+ {0x8029, 250},
+ {0xc038, 250},
+ {0x8003, 251},
+ {0x8006, 251},
+ {0x800a, 251},
+ {0x800f, 251},
+ {0x8018, 251},
+ {0x801f, 251},
+ {0x8029, 251},
+ {0xc038, 251},
+ },
+ /* 222 */
+ {
+ {0x8003, 252},
+ {0x8006, 252},
+ {0x800a, 252},
+ {0x800f, 252},
+ {0x8018, 252},
+ {0x801f, 252},
+ {0x8029, 252},
+ {0xc038, 252},
+ {0x8003, 253},
+ {0x8006, 253},
+ {0x800a, 253},
+ {0x800f, 253},
+ {0x8018, 253},
+ {0x801f, 253},
+ {0x8029, 253},
+ {0xc038, 253},
+ },
+ /* 223 */
+ {
+ {0xc000, 254},
+ {0xe3, 0},
+ {0xe5, 0},
+ {0xe6, 0},
+ {0xe9, 0},
+ {0xea, 0},
+ {0xec, 0},
+ {0xed, 0},
+ {0xf1, 0},
+ {0xf2, 0},
+ {0xf4, 0},
+ {0xf5, 0},
+ {0xf8, 0},
+ {0xf9, 0},
+ {0xfb, 0},
+ {0xfc, 0},
+ },
+ /* 224 */
+ {
+ {0x8001, 254},
+ {0xc016, 254},
+ {0xc000, 2},
+ {0xc000, 3},
+ {0xc000, 4},
+ {0xc000, 5},
+ {0xc000, 6},
+ {0xc000, 7},
+ {0xc000, 8},
+ {0xc000, 11},
+ {0xc000, 12},
+ {0xc000, 14},
+ {0xc000, 15},
+ {0xc000, 16},
+ {0xc000, 17},
+ {0xc000, 18},
+ },
+ /* 225 */
+ {
+ {0x8002, 254},
+ {0x8009, 254},
+ {0x8017, 254},
+ {0xc028, 254},
+ {0x8001, 2},
+ {0xc016, 2},
+ {0x8001, 3},
+ {0xc016, 3},
+ {0x8001, 4},
+ {0xc016, 4},
+ {0x8001, 5},
+ {0xc016, 5},
+ {0x8001, 6},
+ {0xc016, 6},
+ {0x8001, 7},
+ {0xc016, 7},
+ },
+ /* 226 */
+ {
+ {0x8003, 254},
+ {0x8006, 254},
+ {0x800a, 254},
+ {0x800f, 254},
+ {0x8018, 254},
+ {0x801f, 254},
+ {0x8029, 254},
+ {0xc038, 254},
+ {0x8002, 2},
+ {0x8009, 2},
+ {0x8017, 2},
+ {0xc028, 2},
+ {0x8002, 3},
+ {0x8009, 3},
+ {0x8017, 3},
+ {0xc028, 3},
+ },
+ /* 227 */
+ {
+ {0x8003, 2},
+ {0x8006, 2},
+ {0x800a, 2},
+ {0x800f, 2},
+ {0x8018, 2},
+ {0x801f, 2},
+ {0x8029, 2},
+ {0xc038, 2},
+ {0x8003, 3},
+ {0x8006, 3},
+ {0x800a, 3},
+ {0x800f, 3},
+ {0x8018, 3},
+ {0x801f, 3},
+ {0x8029, 3},
+ {0xc038, 3},
+ },
+ /* 228 */
+ {
+ {0x8002, 4},
+ {0x8009, 4},
+ {0x8017, 4},
+ {0xc028, 4},
+ {0x8002, 5},
+ {0x8009, 5},
+ {0x8017, 5},
+ {0xc028, 5},
+ {0x8002, 6},
+ {0x8009, 6},
+ {0x8017, 6},
+ {0xc028, 6},
+ {0x8002, 7},
+ {0x8009, 7},
+ {0x8017, 7},
+ {0xc028, 7},
+ },
+ /* 229 */
+ {
+ {0x8003, 4},
+ {0x8006, 4},
+ {0x800a, 4},
+ {0x800f, 4},
+ {0x8018, 4},
+ {0x801f, 4},
+ {0x8029, 4},
+ {0xc038, 4},
+ {0x8003, 5},
+ {0x8006, 5},
+ {0x800a, 5},
+ {0x800f, 5},
+ {0x8018, 5},
+ {0x801f, 5},
+ {0x8029, 5},
+ {0xc038, 5},
+ },
+ /* 230 */
+ {
+ {0x8003, 6},
+ {0x8006, 6},
+ {0x800a, 6},
+ {0x800f, 6},
+ {0x8018, 6},
+ {0x801f, 6},
+ {0x8029, 6},
+ {0xc038, 6},
+ {0x8003, 7},
+ {0x8006, 7},
+ {0x800a, 7},
+ {0x800f, 7},
+ {0x8018, 7},
+ {0x801f, 7},
+ {0x8029, 7},
+ {0xc038, 7},
+ },
+ /* 231 */
+ {
+ {0x8001, 8},
+ {0xc016, 8},
+ {0x8001, 11},
+ {0xc016, 11},
+ {0x8001, 12},
+ {0xc016, 12},
+ {0x8001, 14},
+ {0xc016, 14},
+ {0x8001, 15},
+ {0xc016, 15},
+ {0x8001, 16},
+ {0xc016, 16},
+ {0x8001, 17},
+ {0xc016, 17},
+ {0x8001, 18},
+ {0xc016, 18},
+ },
+ /* 232 */
+ {
+ {0x8002, 8},
+ {0x8009, 8},
+ {0x8017, 8},
+ {0xc028, 8},
+ {0x8002, 11},
+ {0x8009, 11},
+ {0x8017, 11},
+ {0xc028, 11},
+ {0x8002, 12},
+ {0x8009, 12},
+ {0x8017, 12},
+ {0xc028, 12},
+ {0x8002, 14},
+ {0x8009, 14},
+ {0x8017, 14},
+ {0xc028, 14},
+ },
+ /* 233 */
+ {
+ {0x8003, 8},
+ {0x8006, 8},
+ {0x800a, 8},
+ {0x800f, 8},
+ {0x8018, 8},
+ {0x801f, 8},
+ {0x8029, 8},
+ {0xc038, 8},
+ {0x8003, 11},
+ {0x8006, 11},
+ {0x800a, 11},
+ {0x800f, 11},
+ {0x8018, 11},
+ {0x801f, 11},
+ {0x8029, 11},
+ {0xc038, 11},
+ },
+ /* 234 */
+ {
+ {0x8003, 12},
+ {0x8006, 12},
+ {0x800a, 12},
+ {0x800f, 12},
+ {0x8018, 12},
+ {0x801f, 12},
+ {0x8029, 12},
+ {0xc038, 12},
+ {0x8003, 14},
+ {0x8006, 14},
+ {0x800a, 14},
+ {0x800f, 14},
+ {0x8018, 14},
+ {0x801f, 14},
+ {0x8029, 14},
+ {0xc038, 14},
+ },
+ /* 235 */
+ {
+ {0x8002, 15},
+ {0x8009, 15},
+ {0x8017, 15},
+ {0xc028, 15},
+ {0x8002, 16},
+ {0x8009, 16},
+ {0x8017, 16},
+ {0xc028, 16},
+ {0x8002, 17},
+ {0x8009, 17},
+ {0x8017, 17},
+ {0xc028, 17},
+ {0x8002, 18},
+ {0x8009, 18},
+ {0x8017, 18},
+ {0xc028, 18},
+ },
+ /* 236 */
+ {
+ {0x8003, 15},
+ {0x8006, 15},
+ {0x800a, 15},
+ {0x800f, 15},
+ {0x8018, 15},
+ {0x801f, 15},
+ {0x8029, 15},
+ {0xc038, 15},
+ {0x8003, 16},
+ {0x8006, 16},
+ {0x800a, 16},
+ {0x800f, 16},
+ {0x8018, 16},
+ {0x801f, 16},
+ {0x8029, 16},
+ {0xc038, 16},
+ },
+ /* 237 */
+ {
+ {0x8003, 17},
+ {0x8006, 17},
+ {0x800a, 17},
+ {0x800f, 17},
+ {0x8018, 17},
+ {0x801f, 17},
+ {0x8029, 17},
+ {0xc038, 17},
+ {0x8003, 18},
+ {0x8006, 18},
+ {0x800a, 18},
+ {0x800f, 18},
+ {0x8018, 18},
+ {0x801f, 18},
+ {0x8029, 18},
+ {0xc038, 18},
+ },
+ /* 238 */
+ {
+ {0xc000, 19},
+ {0xc000, 20},
+ {0xc000, 21},
+ {0xc000, 23},
+ {0xc000, 24},
+ {0xc000, 25},
+ {0xc000, 26},
+ {0xc000, 27},
+ {0xc000, 28},
+ {0xc000, 29},
+ {0xc000, 30},
+ {0xc000, 31},
+ {0xc000, 127},
+ {0xc000, 220},
+ {0xc000, 249},
+ {0xfd, 0},
+ },
+ /* 239 */
+ {
+ {0x8001, 19},
+ {0xc016, 19},
+ {0x8001, 20},
+ {0xc016, 20},
+ {0x8001, 21},
+ {0xc016, 21},
+ {0x8001, 23},
+ {0xc016, 23},
+ {0x8001, 24},
+ {0xc016, 24},
+ {0x8001, 25},
+ {0xc016, 25},
+ {0x8001, 26},
+ {0xc016, 26},
+ {0x8001, 27},
+ {0xc016, 27},
+ },
+ /* 240 */
+ {
+ {0x8002, 19},
+ {0x8009, 19},
+ {0x8017, 19},
+ {0xc028, 19},
+ {0x8002, 20},
+ {0x8009, 20},
+ {0x8017, 20},
+ {0xc028, 20},
+ {0x8002, 21},
+ {0x8009, 21},
+ {0x8017, 21},
+ {0xc028, 21},
+ {0x8002, 23},
+ {0x8009, 23},
+ {0x8017, 23},
+ {0xc028, 23},
+ },
+ /* 241 */
+ {
+ {0x8003, 19},
+ {0x8006, 19},
+ {0x800a, 19},
+ {0x800f, 19},
+ {0x8018, 19},
+ {0x801f, 19},
+ {0x8029, 19},
+ {0xc038, 19},
+ {0x8003, 20},
+ {0x8006, 20},
+ {0x800a, 20},
+ {0x800f, 20},
+ {0x8018, 20},
+ {0x801f, 20},
+ {0x8029, 20},
+ {0xc038, 20},
+ },
+ /* 242 */
+ {
+ {0x8003, 21},
+ {0x8006, 21},
+ {0x800a, 21},
+ {0x800f, 21},
+ {0x8018, 21},
+ {0x801f, 21},
+ {0x8029, 21},
+ {0xc038, 21},
+ {0x8003, 23},
+ {0x8006, 23},
+ {0x800a, 23},
+ {0x800f, 23},
+ {0x8018, 23},
+ {0x801f, 23},
+ {0x8029, 23},
+ {0xc038, 23},
+ },
+ /* 243 */
+ {
+ {0x8002, 24},
+ {0x8009, 24},
+ {0x8017, 24},
+ {0xc028, 24},
+ {0x8002, 25},
+ {0x8009, 25},
+ {0x8017, 25},
+ {0xc028, 25},
+ {0x8002, 26},
+ {0x8009, 26},
+ {0x8017, 26},
+ {0xc028, 26},
+ {0x8002, 27},
+ {0x8009, 27},
+ {0x8017, 27},
+ {0xc028, 27},
+ },
+ /* 244 */
+ {
+ {0x8003, 24},
+ {0x8006, 24},
+ {0x800a, 24},
+ {0x800f, 24},
+ {0x8018, 24},
+ {0x801f, 24},
+ {0x8029, 24},
+ {0xc038, 24},
+ {0x8003, 25},
+ {0x8006, 25},
+ {0x800a, 25},
+ {0x800f, 25},
+ {0x8018, 25},
+ {0x801f, 25},
+ {0x8029, 25},
+ {0xc038, 25},
+ },
+ /* 245 */
+ {
+ {0x8003, 26},
+ {0x8006, 26},
+ {0x800a, 26},
+ {0x800f, 26},
+ {0x8018, 26},
+ {0x801f, 26},
+ {0x8029, 26},
+ {0xc038, 26},
+ {0x8003, 27},
+ {0x8006, 27},
+ {0x800a, 27},
+ {0x800f, 27},
+ {0x8018, 27},
+ {0x801f, 27},
+ {0x8029, 27},
+ {0xc038, 27},
+ },
+ /* 246 */
+ {
+ {0x8001, 28},
+ {0xc016, 28},
+ {0x8001, 29},
+ {0xc016, 29},
+ {0x8001, 30},
+ {0xc016, 30},
+ {0x8001, 31},
+ {0xc016, 31},
+ {0x8001, 127},
+ {0xc016, 127},
+ {0x8001, 220},
+ {0xc016, 220},
+ {0x8001, 249},
+ {0xc016, 249},
+ {0xfe, 0},
+ {0xff, 0},
+ },
+ /* 247 */
+ {
+ {0x8002, 28},
+ {0x8009, 28},
+ {0x8017, 28},
+ {0xc028, 28},
+ {0x8002, 29},
+ {0x8009, 29},
+ {0x8017, 29},
+ {0xc028, 29},
+ {0x8002, 30},
+ {0x8009, 30},
+ {0x8017, 30},
+ {0xc028, 30},
+ {0x8002, 31},
+ {0x8009, 31},
+ {0x8017, 31},
+ {0xc028, 31},
+ },
+ /* 248 */
+ {
+ {0x8003, 28},
+ {0x8006, 28},
+ {0x800a, 28},
+ {0x800f, 28},
+ {0x8018, 28},
+ {0x801f, 28},
+ {0x8029, 28},
+ {0xc038, 28},
+ {0x8003, 29},
+ {0x8006, 29},
+ {0x800a, 29},
+ {0x800f, 29},
+ {0x8018, 29},
+ {0x801f, 29},
+ {0x8029, 29},
+ {0xc038, 29},
+ },
+ /* 249 */
+ {
+ {0x8003, 30},
+ {0x8006, 30},
+ {0x800a, 30},
+ {0x800f, 30},
+ {0x8018, 30},
+ {0x801f, 30},
+ {0x8029, 30},
+ {0xc038, 30},
+ {0x8003, 31},
+ {0x8006, 31},
+ {0x800a, 31},
+ {0x800f, 31},
+ {0x8018, 31},
+ {0x801f, 31},
+ {0x8029, 31},
+ {0xc038, 31},
+ },
+ /* 250 */
+ {
+ {0x8002, 127},
+ {0x8009, 127},
+ {0x8017, 127},
+ {0xc028, 127},
+ {0x8002, 220},
+ {0x8009, 220},
+ {0x8017, 220},
+ {0xc028, 220},
+ {0x8002, 249},
+ {0x8009, 249},
+ {0x8017, 249},
+ {0xc028, 249},
+ {0xc000, 10},
+ {0xc000, 13},
+ {0xc000, 22},
+ {0x100, 0},
+ },
+ /* 251 */
+ {
+ {0x8003, 127},
+ {0x8006, 127},
+ {0x800a, 127},
+ {0x800f, 127},
+ {0x8018, 127},
+ {0x801f, 127},
+ {0x8029, 127},
+ {0xc038, 127},
+ {0x8003, 220},
+ {0x8006, 220},
+ {0x800a, 220},
+ {0x800f, 220},
+ {0x8018, 220},
+ {0x801f, 220},
+ {0x8029, 220},
+ {0xc038, 220},
+ },
+ /* 252 */
+ {
+ {0x8003, 249},
+ {0x8006, 249},
+ {0x800a, 249},
+ {0x800f, 249},
+ {0x8018, 249},
+ {0x801f, 249},
+ {0x8029, 249},
+ {0xc038, 249},
+ {0x8001, 10},
+ {0xc016, 10},
+ {0x8001, 13},
+ {0xc016, 13},
+ {0x8001, 22},
+ {0xc016, 22},
+ {0x100, 0},
+ {0x100, 0},
+ },
+ /* 253 */
+ {
+ {0x8002, 10},
+ {0x8009, 10},
+ {0x8017, 10},
+ {0xc028, 10},
+ {0x8002, 13},
+ {0x8009, 13},
+ {0x8017, 13},
+ {0xc028, 13},
+ {0x8002, 22},
+ {0x8009, 22},
+ {0x8017, 22},
+ {0xc028, 22},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ },
+ /* 254 */
+ {
+ {0x8003, 10},
+ {0x8006, 10},
+ {0x800a, 10},
+ {0x800f, 10},
+ {0x8018, 10},
+ {0x801f, 10},
+ {0x8029, 10},
+ {0xc038, 10},
+ {0x8003, 13},
+ {0x8006, 13},
+ {0x800a, 13},
+ {0x800f, 13},
+ {0x8018, 13},
+ {0x801f, 13},
+ {0x8029, 13},
+ {0xc038, 13},
+ },
+ /* 255 */
+ {
+ {0x8003, 22},
+ {0x8006, 22},
+ {0x800a, 22},
+ {0x800f, 22},
+ {0x8018, 22},
+ {0x801f, 22},
+ {0x8029, 22},
+ {0xc038, 22},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ },
+ /* 256 */
+ {
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ {0x100, 0},
+ },
+};
diff --git a/lib/nghttp3_range.c b/lib/nghttp3_range.c
new file mode 100644
index 0000000..0ce7148
--- /dev/null
+++ b/lib/nghttp3_range.c
@@ -0,0 +1,62 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_range.h"
+#include "nghttp3_macro.h"
+
+void nghttp3_range_init(nghttp3_range *r, uint64_t begin, uint64_t end) {
+ r->begin = begin;
+ r->end = end;
+}
+
+nghttp3_range nghttp3_range_intersect(const nghttp3_range *a,
+ const nghttp3_range *b) {
+ nghttp3_range r = {0, 0};
+ uint64_t begin = nghttp3_max(a->begin, b->begin);
+ uint64_t end = nghttp3_min(a->end, b->end);
+ if (begin < end) {
+ nghttp3_range_init(&r, begin, end);
+ }
+ return r;
+}
+
+uint64_t nghttp3_range_len(const nghttp3_range *r) { return r->end - r->begin; }
+
+int nghttp3_range_eq(const nghttp3_range *a, const nghttp3_range *b) {
+ return a->begin == b->begin && a->end == b->end;
+}
+
+void nghttp3_range_cut(nghttp3_range *left, nghttp3_range *right,
+ const nghttp3_range *a, const nghttp3_range *b) {
+ /* Assume that b is included in a */
+ left->begin = a->begin;
+ left->end = b->begin;
+ right->begin = b->end;
+ right->end = a->end;
+}
+
+int nghttp3_range_not_after(const nghttp3_range *a, const nghttp3_range *b) {
+ return a->end <= b->end;
+}
diff --git a/lib/nghttp3_range.h b/lib/nghttp3_range.h
new file mode 100644
index 0000000..20dab69
--- /dev/null
+++ b/lib/nghttp3_range.h
@@ -0,0 +1,81 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_RANGE_H
+#define NGHTTP3_RANGE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+/*
+ * nghttp3_range represents half-closed range [begin, end).
+ */
+typedef struct nghttp3_range {
+ uint64_t begin;
+ uint64_t end;
+} nghttp3_range;
+
+/*
+ * nghttp3_range_init initializes |r| with the range [|begin|, |end|).
+ */
+void nghttp3_range_init(nghttp3_range *r, uint64_t begin, uint64_t end);
+
+/*
+ * nghttp3_range_intersect returns the intersection of |a| and |b|.
+ * If they do not overlap, it returns empty range.
+ */
+nghttp3_range nghttp3_range_intersect(const nghttp3_range *a,
+ const nghttp3_range *b);
+
+/*
+ * nghttp3_range_len returns the length of |r|.
+ */
+uint64_t nghttp3_range_len(const nghttp3_range *r);
+
+/*
+ * nghttp3_range_eq returns nonzero if |a| equals |b|, such that
+ * a->begin == b->begin, and a->end == b->end hold.
+ */
+int nghttp3_range_eq(const nghttp3_range *a, const nghttp3_range *b);
+
+/*
+ * nghttp3_range_cut returns the left and right range after removing
+ * |b| from |a|. This function assumes that |a| completely includes
+ * |b|. In other words, a->begin <= b->begin and b->end <= a->end
+ * hold.
+ */
+void nghttp3_range_cut(nghttp3_range *left, nghttp3_range *right,
+ const nghttp3_range *a, const nghttp3_range *b);
+
+/*
+ * nghttp3_range_not_after returns nonzero if the right edge of |a|
+ * does not go beyond of the right edge of |b|.
+ */
+int nghttp3_range_not_after(const nghttp3_range *a, const nghttp3_range *b);
+
+#endif /* NGHTTP3_RANGE_H */
diff --git a/lib/nghttp3_rcbuf.c b/lib/nghttp3_rcbuf.c
new file mode 100644
index 0000000..9e9dab5
--- /dev/null
+++ b/lib/nghttp3_rcbuf.c
@@ -0,0 +1,108 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2016 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_rcbuf.h"
+
+#include <assert.h>
+
+#include "nghttp3_mem.h"
+#include "nghttp3_str.h"
+
+int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size,
+ const nghttp3_mem *mem) {
+ uint8_t *p;
+
+ p = nghttp3_mem_malloc(mem, sizeof(nghttp3_rcbuf) + size);
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ *rcbuf_ptr = (void *)p;
+
+ (*rcbuf_ptr)->mem = mem;
+ (*rcbuf_ptr)->base = p + sizeof(nghttp3_rcbuf);
+ (*rcbuf_ptr)->len = size;
+ (*rcbuf_ptr)->ref = 1;
+
+ return 0;
+}
+
+int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src,
+ size_t srclen, const nghttp3_mem *mem) {
+ int rv;
+ uint8_t *p;
+
+ rv = nghttp3_rcbuf_new(rcbuf_ptr, srclen + 1, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ (*rcbuf_ptr)->len = srclen;
+ p = (*rcbuf_ptr)->base;
+
+ if (srclen) {
+ p = nghttp3_cpymem(p, src, srclen);
+ }
+
+ *p = '\0';
+
+ return 0;
+}
+
+/*
+ * Frees |rcbuf| itself, regardless of its reference cout.
+ */
+void nghttp3_rcbuf_del(nghttp3_rcbuf *rcbuf) {
+ nghttp3_mem_free(rcbuf->mem, rcbuf);
+}
+
+void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf) {
+ if (rcbuf->ref == -1) {
+ return;
+ }
+
+ ++rcbuf->ref;
+}
+
+void nghttp3_rcbuf_decref(nghttp3_rcbuf *rcbuf) {
+ if (rcbuf == NULL || rcbuf->ref == -1) {
+ return;
+ }
+
+ assert(rcbuf->ref > 0);
+
+ if (--rcbuf->ref == 0) {
+ nghttp3_rcbuf_del(rcbuf);
+ }
+}
+
+nghttp3_vec nghttp3_rcbuf_get_buf(const nghttp3_rcbuf *rcbuf) {
+ nghttp3_vec res = {rcbuf->base, rcbuf->len};
+ return res;
+}
+
+int nghttp3_rcbuf_is_static(const nghttp3_rcbuf *rcbuf) {
+ return rcbuf->ref == -1;
+}
diff --git a/lib/nghttp3_rcbuf.h b/lib/nghttp3_rcbuf.h
new file mode 100644
index 0000000..f589c37
--- /dev/null
+++ b/lib/nghttp3_rcbuf.h
@@ -0,0 +1,81 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2016 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_RCBUF_H
+#define NGHTTP3_RCBUF_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+struct nghttp3_rcbuf {
+ /* mem is the memory allocator that allocates memory for this
+ object. */
+ const nghttp3_mem *mem;
+ /* The pointer to the underlying buffer */
+ uint8_t *base;
+ /* Size of buffer pointed by |base|. */
+ size_t len;
+ /* Reference count */
+ int32_t ref;
+};
+
+/*
+ * Allocates nghttp3_rcbuf object with |size| as initial buffer size.
+ * When the function succeeds, the reference count becomes 1.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM:
+ * Out of memory.
+ */
+int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size,
+ const nghttp3_mem *mem);
+
+/*
+ * Like nghttp3_rcbuf_new(), but initializes the buffer with |src| of
+ * length |srclen|. This function allocates additional byte at the
+ * end and puts '\0' into it, so that the resulting buffer could be
+ * used as NULL-terminated string. Still (*rcbuf_ptr)->len equals to
+ * |srclen|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM:
+ * Out of memory.
+ */
+int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src,
+ size_t srclen, const nghttp3_mem *mem);
+
+/*
+ * Frees |rcbuf| itself, regardless of its reference cout.
+ */
+void nghttp3_rcbuf_del(nghttp3_rcbuf *rcbuf);
+
+#endif /* NGHTTP3_RCBUF_H */
diff --git a/lib/nghttp3_ringbuf.c b/lib/nghttp3_ringbuf.c
new file mode 100644
index 0000000..75ef7ad
--- /dev/null
+++ b/lib/nghttp3_ringbuf.c
@@ -0,0 +1,159 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_ringbuf.h"
+
+#include <assert.h>
+#include <string.h>
+#ifdef WIN32
+# include <intrin.h>
+#endif
+
+#include "nghttp3_macro.h"
+
+#if defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
+unsigned int __popcnt(unsigned int x) {
+ unsigned int c = 0;
+ for (; x; ++c) {
+ x &= x - 1;
+ }
+ return c;
+}
+#endif
+
+int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size,
+ const nghttp3_mem *mem) {
+ if (nmemb) {
+#ifdef WIN32
+ assert(1 == __popcnt((unsigned int)nmemb));
+#else
+ assert(1 == __builtin_popcount((unsigned int)nmemb));
+#endif
+
+ rb->buf = nghttp3_mem_malloc(mem, nmemb * size);
+ if (rb->buf == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+ } else {
+ rb->buf = NULL;
+ }
+
+ rb->mem = mem;
+ rb->nmemb = nmemb;
+ rb->size = size;
+ rb->first = 0;
+ rb->len = 0;
+
+ return 0;
+}
+
+void nghttp3_ringbuf_free(nghttp3_ringbuf *rb) {
+ if (rb == NULL) {
+ return;
+ }
+
+ nghttp3_mem_free(rb->mem, rb->buf);
+}
+
+void *nghttp3_ringbuf_push_front(nghttp3_ringbuf *rb) {
+ rb->first = (rb->first - 1) & (rb->nmemb - 1);
+ rb->len = nghttp3_min(rb->nmemb, rb->len + 1);
+
+ return (void *)&rb->buf[rb->first * rb->size];
+}
+
+void *nghttp3_ringbuf_push_back(nghttp3_ringbuf *rb) {
+ size_t offset = (rb->first + rb->len) & (rb->nmemb - 1);
+
+ if (rb->len == rb->nmemb) {
+ rb->first = (rb->first + 1) & (rb->nmemb - 1);
+ } else {
+ ++rb->len;
+ }
+
+ return (void *)&rb->buf[offset * rb->size];
+}
+
+void nghttp3_ringbuf_pop_front(nghttp3_ringbuf *rb) {
+ rb->first = (rb->first + 1) & (rb->nmemb - 1);
+ --rb->len;
+}
+
+void nghttp3_ringbuf_pop_back(nghttp3_ringbuf *rb) {
+ assert(rb->len);
+ --rb->len;
+}
+
+void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len) {
+ assert(len <= rb->nmemb);
+ rb->len = len;
+}
+
+void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset) {
+ assert(offset < rb->len);
+ offset = (rb->first + offset) & (rb->nmemb - 1);
+ return &rb->buf[offset * rb->size];
+}
+
+int nghttp3_ringbuf_full(nghttp3_ringbuf *rb) { return rb->len == rb->nmemb; }
+
+int nghttp3_ringbuf_reserve(nghttp3_ringbuf *rb, size_t nmemb) {
+ uint8_t *buf;
+
+ if (rb->nmemb >= nmemb) {
+ return 0;
+ }
+
+#ifdef WIN32
+ assert(1 == __popcnt((unsigned int)nmemb));
+#else
+ assert(1 == __builtin_popcount((unsigned int)nmemb));
+#endif
+
+ buf = nghttp3_mem_malloc(rb->mem, nmemb * rb->size);
+ if (buf == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ if (rb->buf != NULL) {
+ if (rb->first + rb->len <= rb->nmemb) {
+ memcpy(buf, rb->buf + rb->first * rb->size, rb->len * rb->size);
+ rb->first = 0;
+ } else {
+ memcpy(buf, rb->buf + rb->first * rb->size,
+ (rb->nmemb - rb->first) * rb->size);
+ memcpy(buf + (rb->nmemb - rb->first) * rb->size, rb->buf,
+ (rb->len - (rb->nmemb - rb->first)) * rb->size);
+ rb->first = 0;
+ }
+
+ nghttp3_mem_free(rb->mem, rb->buf);
+ }
+
+ rb->buf = buf;
+ rb->nmemb = nmemb;
+
+ return 0;
+}
diff --git a/lib/nghttp3_ringbuf.h b/lib/nghttp3_ringbuf.h
new file mode 100644
index 0000000..8e05ec5
--- /dev/null
+++ b/lib/nghttp3_ringbuf.h
@@ -0,0 +1,113 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_RINGBUF_H
+#define NGHTTP3_RINGBUF_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_mem.h"
+
+typedef struct nghttp3_ringbuf {
+ /* buf points to the underlying buffer. */
+ uint8_t *buf;
+ const nghttp3_mem *mem;
+ /* nmemb is the number of elements that can be stored in this ring
+ buffer. */
+ size_t nmemb;
+ /* size is the size of each element. */
+ size_t size;
+ /* first is the offset to the first element. */
+ size_t first;
+ /* len is the number of elements actually stored. */
+ size_t len;
+} nghttp3_ringbuf;
+
+/*
+ * nghttp3_ringbuf_init initializes |rb|. |nmemb| is the number of
+ * elements that can be stored in this buffer. |size| is the size of
+ * each element. |size| must be power of 2.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGHTTP3_ERR_NOMEM
+ * Out of memory.
+ */
+int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size,
+ const nghttp3_mem *mem);
+
+/*
+ * nghttp3_ringbuf_free frees resources allocated for |rb|. This
+ * function does not free the memory pointed by |rb|.
+ */
+void nghttp3_ringbuf_free(nghttp3_ringbuf *rb);
+
+/* nghttp3_ringbuf_push_front moves the offset to the first element in
+ the buffer backward, and returns the pointer to the element.
+ Caller can store data to the buffer pointed by the returned
+ pointer. If this action exceeds the capacity of the ring buffer,
+ the last element is silently overwritten, and rb->len remains
+ unchanged. */
+void *nghttp3_ringbuf_push_front(nghttp3_ringbuf *rb);
+
+/* nghttp3_ringbuf_push_back moves the offset to the last element in
+ the buffer forward, and returns the pointer to the element. Caller
+ can store data to the buffer pointed by the returned pointer. If
+ this action exceeds the capacity of the ring buffer, the first
+ element is silently overwritten, and rb->len remains unchanged. */
+void *nghttp3_ringbuf_push_back(nghttp3_ringbuf *rb);
+
+/*
+ * nghttp3_ringbuf_pop_front removes first element in |rb|.
+ */
+void nghttp3_ringbuf_pop_front(nghttp3_ringbuf *rb);
+
+/*
+ * nghttp3_ringbuf_pop_back removes the last element in |rb|.
+ */
+void nghttp3_ringbuf_pop_back(nghttp3_ringbuf *rb);
+
+/* nghttp3_ringbuf_resize changes the number of elements stored. This
+ does not change the capacity of the underlying buffer. */
+void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len);
+
+/* nghttp3_ringbuf_get returns the pointer to the element at
+ |offset|. */
+void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset);
+
+/* nghttp3_ringbuf_len returns the number of elements stored. */
+#define nghttp3_ringbuf_len(RB) ((RB)->len)
+
+/* nghttp3_ringbuf_full returns nonzero if |rb| is full. */
+int nghttp3_ringbuf_full(nghttp3_ringbuf *rb);
+
+int nghttp3_ringbuf_reserve(nghttp3_ringbuf *rb, size_t nmemb);
+
+#endif /* NGHTTP3_RINGBUF_H */
diff --git a/lib/nghttp3_str.c b/lib/nghttp3_str.c
new file mode 100644
index 0000000..3782aa7
--- /dev/null
+++ b/lib/nghttp3_str.c
@@ -0,0 +1,110 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_str.h"
+
+#include <string.h>
+#include <assert.h>
+
+uint8_t *nghttp3_cpymem(uint8_t *dest, const uint8_t *src, size_t n) {
+ memcpy(dest, src, n);
+ return dest + n;
+}
+
+/* Generated by gendowncasetbl.py */
+static const uint8_t DOWNCASE_TBL[] = {
+ 0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */,
+ 4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */,
+ 8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */,
+ 12 /* FF */, 13 /* CR */, 14 /* SO */, 15 /* SI */,
+ 16 /* DLE */, 17 /* DC1 */, 18 /* DC2 */, 19 /* DC3 */,
+ 20 /* DC4 */, 21 /* NAK */, 22 /* SYN */, 23 /* ETB */,
+ 24 /* CAN */, 25 /* EM */, 26 /* SUB */, 27 /* ESC */,
+ 28 /* FS */, 29 /* GS */, 30 /* RS */, 31 /* US */,
+ 32 /* SPC */, 33 /* ! */, 34 /* " */, 35 /* # */,
+ 36 /* $ */, 37 /* % */, 38 /* & */, 39 /* ' */,
+ 40 /* ( */, 41 /* ) */, 42 /* * */, 43 /* + */,
+ 44 /* , */, 45 /* - */, 46 /* . */, 47 /* / */,
+ 48 /* 0 */, 49 /* 1 */, 50 /* 2 */, 51 /* 3 */,
+ 52 /* 4 */, 53 /* 5 */, 54 /* 6 */, 55 /* 7 */,
+ 56 /* 8 */, 57 /* 9 */, 58 /* : */, 59 /* ; */,
+ 60 /* < */, 61 /* = */, 62 /* > */, 63 /* ? */,
+ 64 /* @ */, 97 /* A */, 98 /* B */, 99 /* C */,
+ 100 /* D */, 101 /* E */, 102 /* F */, 103 /* G */,
+ 104 /* H */, 105 /* I */, 106 /* J */, 107 /* K */,
+ 108 /* L */, 109 /* M */, 110 /* N */, 111 /* O */,
+ 112 /* P */, 113 /* Q */, 114 /* R */, 115 /* S */,
+ 116 /* T */, 117 /* U */, 118 /* V */, 119 /* W */,
+ 120 /* X */, 121 /* Y */, 122 /* Z */, 91 /* [ */,
+ 92 /* \ */, 93 /* ] */, 94 /* ^ */, 95 /* _ */,
+ 96 /* ` */, 97 /* a */, 98 /* b */, 99 /* c */,
+ 100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */,
+ 104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */,
+ 108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */,
+ 112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */,
+ 116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */,
+ 120 /* x */, 121 /* y */, 122 /* z */, 123 /* { */,
+ 124 /* | */, 125 /* } */, 126 /* ~ */, 127 /* DEL */,
+ 128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */,
+ 132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */,
+ 136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */,
+ 140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */,
+ 144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */,
+ 148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */,
+ 152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */,
+ 156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */,
+ 160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */,
+ 164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */,
+ 168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */,
+ 172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */,
+ 176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */,
+ 180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */,
+ 184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */,
+ 188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */,
+ 192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */,
+ 196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */,
+ 200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */,
+ 204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */,
+ 208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */,
+ 212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */,
+ 216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */,
+ 220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */,
+ 224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */,
+ 228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */,
+ 232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */,
+ 236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */,
+ 240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */,
+ 244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */,
+ 248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */,
+ 252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */,
+};
+
+void nghttp3_downcase(uint8_t *s, size_t len) {
+ size_t i;
+ for (i = 0; i < len; ++i) {
+ s[i] = DOWNCASE_TBL[s[i]];
+ }
+}
diff --git a/lib/nghttp3_str.h b/lib/nghttp3_str.h
new file mode 100644
index 0000000..19c1d2c
--- /dev/null
+++ b/lib/nghttp3_str.h
@@ -0,0 +1,40 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_STR_H
+#define NGHTTP3_STR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+uint8_t *nghttp3_cpymem(uint8_t *dest, const uint8_t *src, size_t n);
+
+void nghttp3_downcase(uint8_t *s, size_t len);
+
+#endif /* NGHTTP3_STR_H */
diff --git a/lib/nghttp3_stream.c b/lib/nghttp3_stream.c
new file mode 100644
index 0000000..457f5e4
--- /dev/null
+++ b/lib/nghttp3_stream.c
@@ -0,0 +1,1248 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_stream.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "nghttp3_conv.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_frame.h"
+#include "nghttp3_conn.h"
+#include "nghttp3_str.h"
+#include "nghttp3_http.h"
+#include "nghttp3_vec.h"
+#include "nghttp3_unreachable.h"
+
+/* NGHTTP3_STREAM_MAX_COPY_THRES is the maximum size of buffer which
+ makes a copy to outq. */
+#define NGHTTP3_STREAM_MAX_COPY_THRES 128
+
+/* NGHTTP3_MIN_RBLEN is the minimum length of nghttp3_ringbuf */
+#define NGHTTP3_MIN_RBLEN 4
+
+int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id,
+ const nghttp3_stream_callbacks *callbacks,
+ nghttp3_objalloc *out_chunk_objalloc,
+ nghttp3_objalloc *stream_objalloc,
+ const nghttp3_mem *mem) {
+ nghttp3_stream *stream = nghttp3_objalloc_stream_get(stream_objalloc);
+
+ if (stream == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ memset(stream, 0, sizeof(*stream));
+
+ stream->out_chunk_objalloc = out_chunk_objalloc;
+ stream->stream_objalloc = stream_objalloc;
+
+ nghttp3_tnode_init(&stream->node, stream_id, NGHTTP3_DEFAULT_URGENCY);
+
+ nghttp3_ringbuf_init(&stream->frq, 0, sizeof(nghttp3_frame_entry), mem);
+ nghttp3_ringbuf_init(&stream->chunks, 0, sizeof(nghttp3_buf), mem);
+ nghttp3_ringbuf_init(&stream->outq, 0, sizeof(nghttp3_typed_buf), mem);
+ nghttp3_ringbuf_init(&stream->inq, 0, sizeof(nghttp3_buf), mem);
+
+ nghttp3_qpack_stream_context_init(&stream->qpack_sctx, stream_id, mem);
+
+ stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX;
+ stream->mem = mem;
+ stream->tx.offset = 0;
+ stream->rx.http.status_code = -1;
+ stream->rx.http.content_length = -1;
+ stream->rx.http.pri = NGHTTP3_DEFAULT_URGENCY;
+ stream->error_code = NGHTTP3_H3_NO_ERROR;
+
+ if (callbacks) {
+ stream->callbacks = *callbacks;
+ }
+
+ *pstream = stream;
+
+ return 0;
+}
+
+static void delete_outq(nghttp3_ringbuf *outq, const nghttp3_mem *mem) {
+ nghttp3_typed_buf *tbuf;
+ size_t i, len = nghttp3_ringbuf_len(outq);
+
+ for (i = 0; i < len; ++i) {
+ tbuf = nghttp3_ringbuf_get(outq, i);
+ if (tbuf->type == NGHTTP3_BUF_TYPE_PRIVATE) {
+ nghttp3_buf_free(&tbuf->buf, mem);
+ }
+ }
+
+ nghttp3_ringbuf_free(outq);
+}
+
+static void delete_chunks(nghttp3_ringbuf *chunks, const nghttp3_mem *mem) {
+ nghttp3_buf *buf;
+ size_t i, len = nghttp3_ringbuf_len(chunks);
+
+ for (i = 0; i < len; ++i) {
+ buf = nghttp3_ringbuf_get(chunks, i);
+ nghttp3_buf_free(buf, mem);
+ }
+
+ nghttp3_ringbuf_free(chunks);
+}
+
+static void delete_out_chunks(nghttp3_ringbuf *chunks,
+ nghttp3_objalloc *out_chunk_objalloc,
+ const nghttp3_mem *mem) {
+ nghttp3_buf *buf;
+ size_t i, len = nghttp3_ringbuf_len(chunks);
+
+ for (i = 0; i < len; ++i) {
+ buf = nghttp3_ringbuf_get(chunks, i);
+
+ if (nghttp3_buf_cap(buf) == NGHTTP3_STREAM_MIN_CHUNK_SIZE) {
+ nghttp3_objalloc_chunk_release(out_chunk_objalloc,
+ (nghttp3_chunk *)(void *)buf->begin);
+ continue;
+ }
+
+ nghttp3_buf_free(buf, mem);
+ }
+
+ nghttp3_ringbuf_free(chunks);
+}
+
+static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) {
+ nghttp3_frame_entry *frent;
+ size_t i, len = nghttp3_ringbuf_len(frq);
+
+ for (i = 0; i < len; ++i) {
+ frent = nghttp3_ringbuf_get(frq, i);
+ switch (frent->fr.hd.type) {
+ case NGHTTP3_FRAME_HEADERS:
+ nghttp3_frame_headers_free(&frent->fr.headers, mem);
+ break;
+ default:
+ break;
+ }
+ }
+
+ nghttp3_ringbuf_free(frq);
+}
+
+void nghttp3_stream_del(nghttp3_stream *stream) {
+ if (stream == NULL) {
+ return;
+ }
+
+ nghttp3_qpack_stream_context_free(&stream->qpack_sctx);
+ delete_chunks(&stream->inq, stream->mem);
+ delete_outq(&stream->outq, stream->mem);
+ delete_out_chunks(&stream->chunks, stream->out_chunk_objalloc, stream->mem);
+ delete_frq(&stream->frq, stream->mem);
+ nghttp3_tnode_free(&stream->node);
+
+ nghttp3_objalloc_stream_release(stream->stream_objalloc, stream);
+}
+
+void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint) {
+ memset(rvint, 0, sizeof(*rvint));
+}
+
+void nghttp3_stream_read_state_reset(nghttp3_stream_read_state *rstate) {
+ memset(rstate, 0, sizeof(*rstate));
+}
+
+nghttp3_ssize nghttp3_read_varint(nghttp3_varint_read_state *rvint,
+ const uint8_t *src, size_t srclen, int fin) {
+ size_t nread = 0;
+ size_t n;
+ size_t i;
+
+ assert(srclen > 0);
+
+ if (rvint->left == 0) {
+ assert(rvint->acc == 0);
+
+ rvint->left = nghttp3_get_varintlen(src);
+ if (rvint->left <= srclen) {
+ rvint->acc = nghttp3_get_varint(&nread, src);
+ rvint->left = 0;
+ return (nghttp3_ssize)nread;
+ }
+
+ if (fin) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ rvint->acc = nghttp3_get_varint_fb(src);
+ nread = 1;
+ ++src;
+ --srclen;
+ --rvint->left;
+ }
+
+ n = nghttp3_min(rvint->left, srclen);
+
+ for (i = 0; i < n; ++i) {
+ rvint->acc = (rvint->acc << 8) + src[i];
+ }
+
+ rvint->left -= n;
+ nread += n;
+
+ if (fin && rvint->left) {
+ return NGHTTP3_ERR_INVALID_ARGUMENT;
+ }
+
+ return (nghttp3_ssize)nread;
+}
+
+int nghttp3_stream_frq_add(nghttp3_stream *stream,
+ const nghttp3_frame_entry *frent) {
+ nghttp3_ringbuf *frq = &stream->frq;
+ nghttp3_frame_entry *dest;
+ int rv;
+
+ if (nghttp3_ringbuf_full(frq)) {
+ size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, nghttp3_ringbuf_len(frq) * 2);
+ rv = nghttp3_ringbuf_reserve(frq, nlen);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ dest = nghttp3_ringbuf_push_back(frq);
+ *dest = *frent;
+
+ return 0;
+}
+
+int nghttp3_stream_fill_outq(nghttp3_stream *stream) {
+ nghttp3_ringbuf *frq = &stream->frq;
+ nghttp3_frame_entry *frent;
+ int data_eof;
+ int rv;
+
+ for (; nghttp3_ringbuf_len(frq) && !nghttp3_stream_outq_is_full(stream) &&
+ stream->unsent_bytes < NGHTTP3_MIN_UNSENT_BYTES;) {
+ frent = nghttp3_ringbuf_get(frq, 0);
+
+ switch (frent->fr.hd.type) {
+ case NGHTTP3_FRAME_SETTINGS:
+ rv = nghttp3_stream_write_settings(stream, frent);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGHTTP3_FRAME_HEADERS:
+ rv = nghttp3_stream_write_headers(stream, frent);
+ if (rv != 0) {
+ return rv;
+ }
+ nghttp3_frame_headers_free(&frent->fr.headers, stream->mem);
+ break;
+ case NGHTTP3_FRAME_DATA:
+ rv = nghttp3_stream_write_data(stream, &data_eof, frent);
+ if (rv != 0) {
+ return rv;
+ }
+ if (stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED) {
+ return 0;
+ }
+ if (!data_eof) {
+ return 0;
+ }
+ break;
+ case NGHTTP3_FRAME_GOAWAY:
+ rv = nghttp3_stream_write_goaway(stream, frent);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGHTTP3_FRAME_PRIORITY_UPDATE:
+ rv = nghttp3_stream_write_priority_update(stream, frent);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ default:
+ /* TODO Not implemented */
+ break;
+ }
+
+ nghttp3_ringbuf_pop_front(frq);
+ }
+
+ return 0;
+}
+
+static void typed_buf_shared_init(nghttp3_typed_buf *tbuf,
+ const nghttp3_buf *chunk) {
+ nghttp3_typed_buf_init(tbuf, chunk, NGHTTP3_BUF_TYPE_SHARED);
+ tbuf->buf.pos = tbuf->buf.last;
+}
+
+int nghttp3_stream_write_stream_type(nghttp3_stream *stream) {
+ size_t len = nghttp3_put_varintlen((int64_t)stream->type);
+ nghttp3_buf *chunk;
+ nghttp3_typed_buf tbuf;
+ int rv;
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ return rv;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ chunk->last = nghttp3_put_varint(chunk->last, (int64_t)stream->type);
+ tbuf.buf.last = chunk->last;
+
+ return nghttp3_stream_outq_add(stream, &tbuf);
+}
+
+int nghttp3_stream_write_settings(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent) {
+ size_t len;
+ int rv;
+ nghttp3_buf *chunk;
+ nghttp3_typed_buf tbuf;
+ struct {
+ nghttp3_frame_settings settings;
+ nghttp3_settings_entry iv[15];
+ } fr;
+ nghttp3_settings_entry *iv;
+ nghttp3_settings *local_settings = frent->aux.settings.local_settings;
+
+ fr.settings.hd.type = NGHTTP3_FRAME_SETTINGS;
+ fr.settings.niv = 3;
+ iv = &fr.settings.iv[0];
+
+ iv[0].id = NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE;
+ iv[0].value = local_settings->max_field_section_size;
+ iv[1].id = NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY;
+ iv[1].value = local_settings->qpack_max_dtable_capacity;
+ iv[2].id = NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS;
+ iv[2].value = local_settings->qpack_blocked_streams;
+
+ if (local_settings->enable_connect_protocol) {
+ ++fr.settings.niv;
+ iv[3].id = NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL;
+ iv[3].value = 1;
+ }
+
+ len = nghttp3_frame_write_settings_len(&fr.settings.hd.length, &fr.settings);
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ return rv;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ chunk->last = nghttp3_frame_write_settings(chunk->last, &fr.settings);
+
+ tbuf.buf.last = chunk->last;
+
+ return nghttp3_stream_outq_add(stream, &tbuf);
+}
+
+int nghttp3_stream_write_goaway(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent) {
+ nghttp3_frame_goaway *fr = &frent->fr.goaway;
+ size_t len;
+ int rv;
+ nghttp3_buf *chunk;
+ nghttp3_typed_buf tbuf;
+
+ len = nghttp3_frame_write_goaway_len(&fr->hd.length, fr);
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ return rv;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ chunk->last = nghttp3_frame_write_goaway(chunk->last, fr);
+
+ tbuf.buf.last = chunk->last;
+
+ return nghttp3_stream_outq_add(stream, &tbuf);
+}
+
+int nghttp3_stream_write_priority_update(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent) {
+ nghttp3_frame_priority_update *fr = &frent->fr.priority_update;
+ size_t len;
+ int rv;
+ nghttp3_buf *chunk;
+ nghttp3_typed_buf tbuf;
+
+ len = nghttp3_frame_write_priority_update_len(&fr->hd.length, fr);
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ return rv;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ chunk->last = nghttp3_frame_write_priority_update(chunk->last, fr);
+
+ tbuf.buf.last = chunk->last;
+
+ return nghttp3_stream_outq_add(stream, &tbuf);
+}
+
+int nghttp3_stream_write_headers(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent) {
+ nghttp3_frame_headers *fr = &frent->fr.headers;
+ nghttp3_conn *conn = stream->conn;
+
+ assert(conn);
+
+ return nghttp3_stream_write_header_block(
+ stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf,
+ &conn->tx.qpack.ebuf, NGHTTP3_FRAME_HEADERS, fr->nva, fr->nvlen);
+}
+
+int nghttp3_stream_write_header_block(nghttp3_stream *stream,
+ nghttp3_qpack_encoder *qenc,
+ nghttp3_stream *qenc_stream,
+ nghttp3_buf *rbuf, nghttp3_buf *ebuf,
+ int64_t frame_type, const nghttp3_nv *nva,
+ size_t nvlen) {
+ nghttp3_buf pbuf;
+ int rv;
+ size_t len;
+ nghttp3_buf *chunk;
+ nghttp3_typed_buf tbuf;
+ nghttp3_frame_hd hd;
+ uint8_t raw_pbuf[16];
+ size_t pbuflen, rbuflen, ebuflen;
+
+ nghttp3_buf_wrap_init(&pbuf, raw_pbuf, sizeof(raw_pbuf));
+
+ rv = nghttp3_qpack_encoder_encode(qenc, &pbuf, rbuf, ebuf, stream->node.id,
+ nva, nvlen);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ pbuflen = nghttp3_buf_len(&pbuf);
+ rbuflen = nghttp3_buf_len(rbuf);
+ ebuflen = nghttp3_buf_len(ebuf);
+
+ hd.type = frame_type;
+ hd.length = (int64_t)(pbuflen + rbuflen);
+
+ len = nghttp3_frame_write_hd_len(&hd) + pbuflen;
+
+ if (rbuflen <= NGHTTP3_STREAM_MAX_COPY_THRES) {
+ len += rbuflen;
+ }
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ chunk->last = nghttp3_frame_write_hd(chunk->last, &hd);
+
+ chunk->last = nghttp3_cpymem(chunk->last, pbuf.pos, pbuflen);
+ nghttp3_buf_init(&pbuf);
+
+ if (rbuflen > NGHTTP3_STREAM_MAX_COPY_THRES) {
+ tbuf.buf.last = chunk->last;
+
+ rv = nghttp3_stream_outq_add(stream, &tbuf);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ nghttp3_typed_buf_init(&tbuf, rbuf, NGHTTP3_BUF_TYPE_PRIVATE);
+ rv = nghttp3_stream_outq_add(stream, &tbuf);
+ if (rv != 0) {
+ goto fail;
+ }
+ nghttp3_buf_init(rbuf);
+ } else if (rbuflen) {
+ chunk->last = nghttp3_cpymem(chunk->last, rbuf->pos, rbuflen);
+ tbuf.buf.last = chunk->last;
+
+ rv = nghttp3_stream_outq_add(stream, &tbuf);
+ if (rv != 0) {
+ goto fail;
+ }
+ nghttp3_buf_reset(rbuf);
+ }
+
+ if (ebuflen > NGHTTP3_STREAM_MAX_COPY_THRES) {
+ assert(qenc_stream);
+
+ nghttp3_typed_buf_init(&tbuf, ebuf, NGHTTP3_BUF_TYPE_PRIVATE);
+ rv = nghttp3_stream_outq_add(qenc_stream, &tbuf);
+ if (rv != 0) {
+ return rv;
+ }
+ nghttp3_buf_init(ebuf);
+ } else if (ebuflen) {
+ assert(qenc_stream);
+
+ rv = nghttp3_stream_ensure_chunk(qenc_stream, ebuflen);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ chunk = nghttp3_stream_get_chunk(qenc_stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ chunk->last = nghttp3_cpymem(chunk->last, ebuf->pos, ebuflen);
+ tbuf.buf.last = chunk->last;
+
+ rv = nghttp3_stream_outq_add(qenc_stream, &tbuf);
+ if (rv != 0) {
+ goto fail;
+ }
+ nghttp3_buf_reset(ebuf);
+ }
+
+ assert(0 == nghttp3_buf_len(&pbuf));
+ assert(0 == nghttp3_buf_len(rbuf));
+ assert(0 == nghttp3_buf_len(ebuf));
+
+ return 0;
+
+fail:
+
+ return rv;
+}
+
+int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
+ nghttp3_frame_entry *frent) {
+ int rv;
+ size_t len;
+ nghttp3_typed_buf tbuf;
+ nghttp3_buf buf;
+ nghttp3_buf *chunk;
+ nghttp3_read_data_callback read_data = frent->aux.data.dr.read_data;
+ nghttp3_conn *conn = stream->conn;
+ int64_t datalen;
+ uint32_t flags = 0;
+ nghttp3_frame_hd hd;
+ nghttp3_vec vec[8];
+ nghttp3_vec *v;
+ nghttp3_ssize sveccnt;
+ size_t i;
+
+ assert(!(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED));
+ assert(read_data);
+ assert(conn);
+
+ *peof = 0;
+
+ sveccnt = read_data(conn, stream->node.id, vec, nghttp3_arraylen(vec), &flags,
+ conn->user_data, stream->user_data);
+ if (sveccnt < 0) {
+ if (sveccnt == NGHTTP3_ERR_WOULDBLOCK) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED;
+ return 0;
+ }
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+
+ datalen = nghttp3_vec_len_varint(vec, (size_t)sveccnt);
+ if (datalen == -1) {
+ return NGHTTP3_ERR_STREAM_DATA_OVERFLOW;
+ }
+
+ assert(datalen || flags & NGHTTP3_DATA_FLAG_EOF);
+
+ if (flags & NGHTTP3_DATA_FLAG_EOF) {
+ *peof = 1;
+ if (!(flags & NGHTTP3_DATA_FLAG_NO_END_STREAM)) {
+ stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM;
+ if (datalen == 0) {
+ if (nghttp3_stream_outq_write_done(stream)) {
+ /* If this is the last data and its is 0 length, we don't
+ need send DATA frame. We rely on the non-emptiness of
+ outq to schedule stream, so add empty tbuf to outq to
+ just send fin. */
+ nghttp3_buf_init(&buf);
+ nghttp3_typed_buf_init(&tbuf, &buf, NGHTTP3_BUF_TYPE_PRIVATE);
+ return nghttp3_stream_outq_add(stream, &tbuf);
+ }
+ return 0;
+ }
+ }
+
+ if (datalen == 0) {
+ /* We are going to send more frames, but no DATA frame this
+ time. */
+ return 0;
+ }
+ }
+
+ hd.type = NGHTTP3_FRAME_DATA;
+ hd.length = datalen;
+
+ len = nghttp3_frame_write_hd_len(&hd);
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ return rv;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ chunk->last = nghttp3_frame_write_hd(chunk->last, &hd);
+
+ tbuf.buf.last = chunk->last;
+
+ rv = nghttp3_stream_outq_add(stream, &tbuf);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (datalen) {
+ for (i = 0; i < (size_t)sveccnt; ++i) {
+ v = &vec[i];
+ if (v->len == 0) {
+ continue;
+ }
+ nghttp3_buf_wrap_init(&buf, v->base, v->len);
+ buf.last = buf.end;
+ nghttp3_typed_buf_init(&tbuf, &buf, NGHTTP3_BUF_TYPE_ALIEN);
+ rv = nghttp3_stream_outq_add(stream, &tbuf);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int nghttp3_stream_write_qpack_decoder_stream(nghttp3_stream *stream) {
+ nghttp3_qpack_decoder *qdec;
+ nghttp3_buf *chunk;
+ int rv;
+ nghttp3_typed_buf tbuf;
+ size_t len;
+
+ assert(stream->conn);
+ assert(stream->conn->tx.qdec == stream);
+
+ qdec = &stream->conn->qdec;
+
+ assert(qdec);
+
+ len = nghttp3_qpack_decoder_get_decoder_streamlen(qdec);
+ if (len == 0) {
+ return 0;
+ }
+
+ rv = nghttp3_stream_ensure_chunk(stream, len);
+ if (rv != 0) {
+ return rv;
+ }
+
+ chunk = nghttp3_stream_get_chunk(stream);
+ typed_buf_shared_init(&tbuf, chunk);
+
+ nghttp3_qpack_decoder_write_decoder(qdec, chunk);
+
+ tbuf.buf.last = chunk->last;
+
+ return nghttp3_stream_outq_add(stream, &tbuf);
+}
+
+int nghttp3_stream_outq_is_full(nghttp3_stream *stream) {
+ /* TODO Verify that the limit is reasonable. */
+ return nghttp3_ringbuf_len(&stream->outq) >= 1024;
+}
+
+int nghttp3_stream_outq_add(nghttp3_stream *stream,
+ const nghttp3_typed_buf *tbuf) {
+ nghttp3_ringbuf *outq = &stream->outq;
+ int rv;
+ nghttp3_typed_buf *dest;
+ size_t len = nghttp3_ringbuf_len(outq);
+ size_t buflen = nghttp3_buf_len(&tbuf->buf);
+
+ if (buflen > NGHTTP3_MAX_VARINT - stream->tx.offset) {
+ return NGHTTP3_ERR_STREAM_DATA_OVERFLOW;
+ }
+
+ stream->tx.offset += buflen;
+ stream->unsent_bytes += buflen;
+
+ if (len) {
+ dest = nghttp3_ringbuf_get(outq, len - 1);
+ if (dest->type == tbuf->type && dest->type == NGHTTP3_BUF_TYPE_SHARED &&
+ dest->buf.begin == tbuf->buf.begin && dest->buf.last == tbuf->buf.pos) {
+ /* If we have already written last entry, adjust outq_idx and
+ offset so that this entry is eligible to send. */
+ if (len == stream->outq_idx) {
+ --stream->outq_idx;
+ stream->outq_offset = nghttp3_buf_len(&dest->buf);
+ }
+
+ dest->buf.last = tbuf->buf.last;
+ /* TODO Is this required? */
+ dest->buf.end = tbuf->buf.end;
+
+ return 0;
+ }
+ }
+
+ if (nghttp3_ringbuf_full(outq)) {
+ size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, len * 2);
+ rv = nghttp3_ringbuf_reserve(outq, nlen);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ dest = nghttp3_ringbuf_push_back(outq);
+ *dest = *tbuf;
+
+ return 0;
+}
+
+int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need) {
+ nghttp3_ringbuf *chunks = &stream->chunks;
+ nghttp3_buf *chunk;
+ size_t len = nghttp3_ringbuf_len(chunks);
+ uint8_t *p;
+ int rv;
+ size_t n = NGHTTP3_STREAM_MIN_CHUNK_SIZE;
+
+ if (len) {
+ chunk = nghttp3_ringbuf_get(chunks, len - 1);
+ if (nghttp3_buf_left(chunk) >= need) {
+ return 0;
+ }
+ }
+
+ for (; n < need; n *= 2)
+ ;
+
+ if (n == NGHTTP3_STREAM_MIN_CHUNK_SIZE) {
+ p = (uint8_t *)nghttp3_objalloc_chunk_len_get(stream->out_chunk_objalloc,
+ n);
+ } else {
+ p = nghttp3_mem_malloc(stream->mem, n);
+ }
+ if (p == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ if (nghttp3_ringbuf_full(chunks)) {
+ size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, len * 2);
+ rv = nghttp3_ringbuf_reserve(chunks, nlen);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ chunk = nghttp3_ringbuf_push_back(chunks);
+ nghttp3_buf_wrap_init(chunk, p, n);
+
+ return 0;
+}
+
+nghttp3_buf *nghttp3_stream_get_chunk(nghttp3_stream *stream) {
+ nghttp3_ringbuf *chunks = &stream->chunks;
+ size_t len = nghttp3_ringbuf_len(chunks);
+
+ assert(len);
+
+ return nghttp3_ringbuf_get(chunks, len - 1);
+}
+
+int nghttp3_stream_is_blocked(nghttp3_stream *stream) {
+ return (stream->flags & NGHTTP3_STREAM_FLAG_FC_BLOCKED) ||
+ (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_WR) ||
+ (stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED);
+}
+
+int nghttp3_stream_require_schedule(nghttp3_stream *stream) {
+ return (!nghttp3_stream_outq_write_done(stream) &&
+ !(stream->flags & NGHTTP3_STREAM_FLAG_FC_BLOCKED) &&
+ !(stream->flags & NGHTTP3_STREAM_FLAG_SHUT_WR)) ||
+ (nghttp3_ringbuf_len(&stream->frq) &&
+ !(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED));
+}
+
+nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin,
+ nghttp3_vec *vec, size_t veccnt) {
+ nghttp3_ringbuf *outq = &stream->outq;
+ size_t len = nghttp3_ringbuf_len(outq);
+ size_t i = stream->outq_idx;
+ uint64_t offset = stream->outq_offset;
+ size_t buflen;
+ nghttp3_vec *vbegin = vec, *vend = vec + veccnt;
+ nghttp3_typed_buf *tbuf;
+
+ assert(veccnt > 0);
+
+ if (i < len) {
+ tbuf = nghttp3_ringbuf_get(outq, i);
+ buflen = nghttp3_buf_len(&tbuf->buf);
+
+ if (offset < buflen) {
+ vec->base = tbuf->buf.pos + offset;
+ vec->len = (size_t)(buflen - offset);
+ ++vec;
+ } else {
+ /* This is the only case that satisfies offset >= buflen */
+ assert(0 == offset);
+ assert(0 == buflen);
+ }
+
+ ++i;
+
+ for (; i < len && vec != vend; ++i, ++vec) {
+ tbuf = nghttp3_ringbuf_get(outq, i);
+ vec->base = tbuf->buf.pos;
+ vec->len = nghttp3_buf_len(&tbuf->buf);
+ }
+ }
+
+ /* TODO Rework this if we have finished implementing HTTP
+ messaging */
+ *pfin = nghttp3_ringbuf_len(&stream->frq) == 0 && i == len &&
+ (stream->flags & NGHTTP3_STREAM_FLAG_WRITE_END_STREAM);
+
+ return vec - vbegin;
+}
+
+int nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n) {
+ nghttp3_ringbuf *outq = &stream->outq;
+ size_t i;
+ size_t len = nghttp3_ringbuf_len(outq);
+ uint64_t offset = stream->outq_offset + n;
+ size_t buflen;
+ nghttp3_typed_buf *tbuf;
+
+ for (i = stream->outq_idx; i < len; ++i) {
+ tbuf = nghttp3_ringbuf_get(outq, i);
+ buflen = nghttp3_buf_len(&tbuf->buf);
+ if (offset >= buflen) {
+ offset -= buflen;
+ continue;
+ }
+
+ break;
+ }
+
+ assert(i < len || offset == 0);
+
+ stream->unsent_bytes -= n;
+ stream->outq_idx = i;
+ stream->outq_offset = offset;
+
+ return 0;
+}
+
+int nghttp3_stream_outq_write_done(nghttp3_stream *stream) {
+ nghttp3_ringbuf *outq = &stream->outq;
+ size_t len = nghttp3_ringbuf_len(outq);
+
+ return len == 0 || stream->outq_idx >= len;
+}
+
+static int stream_pop_outq_entry(nghttp3_stream *stream,
+ nghttp3_typed_buf *tbuf) {
+ nghttp3_ringbuf *chunks = &stream->chunks;
+ nghttp3_buf *chunk;
+
+ switch (tbuf->type) {
+ case NGHTTP3_BUF_TYPE_PRIVATE:
+ nghttp3_buf_free(&tbuf->buf, stream->mem);
+ break;
+ case NGHTTP3_BUF_TYPE_ALIEN:
+ break;
+ case NGHTTP3_BUF_TYPE_SHARED:
+ assert(nghttp3_ringbuf_len(chunks));
+
+ chunk = nghttp3_ringbuf_get(chunks, 0);
+
+ assert(chunk->begin == tbuf->buf.begin);
+ assert(chunk->end == tbuf->buf.end);
+
+ if (chunk->last == tbuf->buf.last) {
+ if (nghttp3_buf_cap(chunk) == NGHTTP3_STREAM_MIN_CHUNK_SIZE) {
+ nghttp3_objalloc_chunk_release(stream->out_chunk_objalloc,
+ (nghttp3_chunk *)(void *)chunk->begin);
+ } else {
+ nghttp3_buf_free(chunk, stream->mem);
+ }
+ nghttp3_ringbuf_pop_front(chunks);
+ }
+ break;
+ default:
+ nghttp3_unreachable();
+ };
+
+ nghttp3_ringbuf_pop_front(&stream->outq);
+
+ return 0;
+}
+
+int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n) {
+ nghttp3_ringbuf *outq = &stream->outq;
+ uint64_t offset = stream->ack_offset + n;
+ size_t buflen;
+ size_t npopped = 0;
+ uint64_t nack;
+ nghttp3_typed_buf *tbuf;
+ int rv;
+
+ for (; nghttp3_ringbuf_len(outq);) {
+ tbuf = nghttp3_ringbuf_get(outq, 0);
+ buflen = nghttp3_buf_len(&tbuf->buf);
+
+ if (tbuf->type == NGHTTP3_BUF_TYPE_ALIEN) {
+ nack = nghttp3_min(offset, (uint64_t)buflen) - stream->ack_done;
+ if (stream->callbacks.acked_data) {
+ rv = stream->callbacks.acked_data(stream, stream->node.id, nack,
+ stream->user_data);
+ if (rv != 0) {
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+ }
+ stream->ack_done += nack;
+ }
+
+ if (offset >= buflen) {
+ rv = stream_pop_outq_entry(stream, tbuf);
+ if (rv != 0) {
+ return rv;
+ }
+
+ offset -= buflen;
+ ++npopped;
+ stream->ack_done = 0;
+
+ if (stream->outq_idx + 1 == npopped) {
+ stream->outq_offset = 0;
+ break;
+ }
+
+ continue;
+ }
+
+ break;
+ }
+
+ assert(stream->outq_idx + 1 >= npopped);
+ if (stream->outq_idx >= npopped) {
+ stream->outq_idx -= npopped;
+ } else {
+ stream->outq_idx = 0;
+ }
+
+ stream->ack_offset = offset;
+
+ return 0;
+}
+
+int nghttp3_stream_buffer_data(nghttp3_stream *stream, const uint8_t *data,
+ size_t datalen) {
+ nghttp3_ringbuf *inq = &stream->inq;
+ size_t len = nghttp3_ringbuf_len(inq);
+ nghttp3_buf *buf;
+ size_t nwrite;
+ uint8_t *rawbuf;
+ size_t bufleft;
+ int rv;
+
+ if (len) {
+ buf = nghttp3_ringbuf_get(inq, len - 1);
+ bufleft = nghttp3_buf_left(buf);
+ nwrite = nghttp3_min(datalen, bufleft);
+ buf->last = nghttp3_cpymem(buf->last, data, nwrite);
+ data += nwrite;
+ datalen -= nwrite;
+ }
+
+ for (; datalen;) {
+ if (nghttp3_ringbuf_full(inq)) {
+ size_t nlen =
+ nghttp3_max(NGHTTP3_MIN_RBLEN, nghttp3_ringbuf_len(inq) * 2);
+ rv = nghttp3_ringbuf_reserve(inq, nlen);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ rawbuf = nghttp3_mem_malloc(stream->mem, 16384);
+ if (rawbuf == NULL) {
+ return NGHTTP3_ERR_NOMEM;
+ }
+
+ buf = nghttp3_ringbuf_push_back(inq);
+ nghttp3_buf_wrap_init(buf, rawbuf, 16384);
+ bufleft = nghttp3_buf_left(buf);
+ nwrite = nghttp3_min(datalen, bufleft);
+ buf->last = nghttp3_cpymem(buf->last, data, nwrite);
+ data += nwrite;
+ datalen -= nwrite;
+ }
+
+ return 0;
+}
+
+size_t nghttp3_stream_get_buffered_datalen(nghttp3_stream *stream) {
+ nghttp3_ringbuf *inq = &stream->inq;
+ size_t len = nghttp3_ringbuf_len(inq);
+ size_t i, n = 0;
+ nghttp3_buf *buf;
+
+ for (i = 0; i < len; ++i) {
+ buf = nghttp3_ringbuf_get(inq, i);
+ n += nghttp3_buf_len(buf);
+ }
+
+ return n;
+}
+
+int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
+ nghttp3_stream_http_event event) {
+ int rv;
+
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_NONE:
+ return NGHTTP3_ERR_H3_INTERNAL_ERROR;
+ case NGHTTP3_HTTP_STATE_REQ_INITIAL:
+ switch (event) {
+ case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN;
+ return 0;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_REQ_HEADERS_END:
+ switch (event) {
+ case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
+ /* TODO Better to check status code */
+ if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_DATA_BEGIN:
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_MSG_END:
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
+ return 0;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ case NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_DATA_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_REQ_DATA_END:
+ switch (event) {
+ case NGHTTP3_HTTP_EVENT_DATA_BEGIN:
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
+ /* TODO Better to check status code */
+ if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_MSG_END:
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
+ return 0;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_END:
+ if (event != NGHTTP3_HTTP_EVENT_MSG_END) {
+ /* TODO Should ignore unexpected frame in this state as per
+ spec. */
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_REQ_END:
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ case NGHTTP3_HTTP_STATE_RESP_INITIAL:
+ if (event != NGHTTP3_HTTP_EVENT_HEADERS_BEGIN) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_RESP_HEADERS_END:
+ switch (event) {
+ case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
+ if (stream->rx.http.status_code == -1) {
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN;
+ return 0;
+ }
+ if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) &&
+ stream->rx.http.status_code / 100 == 2) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_DATA_BEGIN:
+ if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_MSG_END:
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
+ return 0;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ case NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_DATA_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_RESP_DATA_END:
+ switch (event) {
+ case NGHTTP3_HTTP_EVENT_DATA_BEGIN:
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
+ if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) &&
+ stream->rx.http.status_code / 100 == 2) {
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN;
+ return 0;
+ case NGHTTP3_HTTP_EVENT_MSG_END:
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
+ return 0;
+ default:
+ return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
+ }
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_END:
+ if (event != NGHTTP3_HTTP_EVENT_MSG_END) {
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+ rv = nghttp3_http_on_remote_end_stream(stream);
+ if (rv != 0) {
+ return rv;
+ }
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
+ return 0;
+ case NGHTTP3_HTTP_STATE_RESP_END:
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ default:
+ nghttp3_unreachable();
+ }
+}
+
+int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream) {
+ switch (stream->rx.hstate) {
+ case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
+ case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
+ return 0;
+ default:
+ return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
+ }
+}
+
+int nghttp3_stream_uni(int64_t stream_id) { return (stream_id & 0x2) != 0; }
+
+int nghttp3_client_stream_bidi(int64_t stream_id) {
+ return (stream_id & 0x3) == 0;
+}
+
+int nghttp3_client_stream_uni(int64_t stream_id) {
+ return (stream_id & 0x3) == 0x2;
+}
+
+int nghttp3_server_stream_uni(int64_t stream_id) {
+ return (stream_id & 0x3) == 0x3;
+}
diff --git a/lib/nghttp3_stream.h b/lib/nghttp3_stream.h
new file mode 100644
index 0000000..be8881f
--- /dev/null
+++ b/lib/nghttp3_stream.h
@@ -0,0 +1,396 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_STREAM_H
+#define NGHTTP3_STREAM_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_map.h"
+#include "nghttp3_tnode.h"
+#include "nghttp3_ringbuf.h"
+#include "nghttp3_buf.h"
+#include "nghttp3_frame.h"
+#include "nghttp3_qpack.h"
+#include "nghttp3_objalloc.h"
+
+#define NGHTTP3_STREAM_MIN_CHUNK_SIZE 256
+
+/* NGHTTP3_MIN_UNSENT_BYTES is the minimum unsent bytes which is large
+ enough to fill outgoing single QUIC packet. */
+#define NGHTTP3_MIN_UNSENT_BYTES 4096
+
+/* NGHTTP3_STREAM_MIN_WRITELEN is the minimum length of write to cause
+ the stream to reschedule. */
+#define NGHTTP3_STREAM_MIN_WRITELEN 800
+
+/* nghttp3_stream_type is unidirectional stream type. */
+typedef enum nghttp3_stream_type {
+ NGHTTP3_STREAM_TYPE_CONTROL = 0x00,
+ NGHTTP3_STREAM_TYPE_PUSH = 0x01,
+ NGHTTP3_STREAM_TYPE_QPACK_ENCODER = 0x02,
+ NGHTTP3_STREAM_TYPE_QPACK_DECODER = 0x03,
+ NGHTTP3_STREAM_TYPE_UNKNOWN = UINT64_MAX,
+} nghttp3_stream_type;
+
+typedef enum nghttp3_ctrl_stream_state {
+ NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE,
+ NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH,
+ NGHTTP3_CTRL_STREAM_STATE_SETTINGS,
+ NGHTTP3_CTRL_STREAM_STATE_GOAWAY,
+ NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID,
+ NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME,
+ NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID,
+ NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE,
+ NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID,
+ NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE,
+} nghttp3_ctrl_stream_state;
+
+typedef enum nghttp3_req_stream_state {
+ NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE,
+ NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH,
+ NGHTTP3_REQ_STREAM_STATE_DATA,
+ NGHTTP3_REQ_STREAM_STATE_HEADERS,
+ NGHTTP3_REQ_STREAM_STATE_IGN_FRAME,
+ NGHTTP3_REQ_STREAM_STATE_IGN_REST,
+} nghttp3_req_stream_state;
+
+typedef struct nghttp3_varint_read_state {
+ int64_t acc;
+ size_t left;
+} nghttp3_varint_read_state;
+
+typedef struct nghttp3_stream_read_state {
+ nghttp3_varint_read_state rvint;
+ nghttp3_frame fr;
+ int state;
+ int64_t left;
+} nghttp3_stream_read_state;
+
+/* NGHTTP3_STREAM_FLAG_NONE indicates that no flag is set. */
+#define NGHTTP3_STREAM_FLAG_NONE 0x0000u
+/* NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED is set when a unidirectional
+ stream type is identified. */
+#define NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED 0x0001u
+/* NGHTTP3_STREAM_FLAG_FC_BLOCKED indicates that stream is blocked by
+ QUIC flow control. */
+#define NGHTTP3_STREAM_FLAG_FC_BLOCKED 0x0002u
+/* NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED indicates that application is
+ temporarily unable to provide data. */
+#define NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED 0x0004u
+/* NGHTTP3_STREAM_FLAG_WRITE_END_STREAM indicates that application
+ finished to feed outgoing data. */
+#define NGHTTP3_STREAM_FLAG_WRITE_END_STREAM 0x0008u
+/* NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED indicates that stream is
+ blocked due to QPACK decoding. */
+#define NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED 0x0010u
+/* NGHTTP3_STREAM_FLAG_READ_EOF indicates that remote endpoint sent
+ fin. */
+#define NGHTTP3_STREAM_FLAG_READ_EOF 0x0020u
+/* NGHTTP3_STREAM_FLAG_CLOSED indicates that QUIC stream was closed.
+ nghttp3_stream object can still alive because it might be blocked
+ by QPACK decoder. */
+#define NGHTTP3_STREAM_FLAG_CLOSED 0x0040u
+/* NGHTTP3_STREAM_FLAG_SHUT_WR indicates that any further write
+ operation to a stream is prohibited. */
+#define NGHTTP3_STREAM_FLAG_SHUT_WR 0x0100u
+/* NGHTTP3_STREAM_FLAG_SHUT_RD indicates that a read-side stream is
+ closed abruptly and any incoming and pending stream data is just
+ discarded for a stream. */
+#define NGHTTP3_STREAM_FLAG_SHUT_RD 0x0200u
+/* NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET indicates that server
+ overrides stream priority. */
+#define NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET 0x0400u
+/* NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED indicates that server
+ received PRIORITY_UPDATE frame for this stream. */
+#define NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED 0x0800u
+/* NGHTTP3_STREAM_FLAG_HTTP_ERROR indicates that
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER error is encountered while
+ processing incoming HTTP fields. */
+#define NGHTTP3_STREAM_FLAG_HTTP_ERROR 0x1000u
+
+typedef enum nghttp3_stream_http_state {
+ NGHTTP3_HTTP_STATE_NONE,
+ NGHTTP3_HTTP_STATE_REQ_INITIAL,
+ NGHTTP3_HTTP_STATE_REQ_BEGIN,
+ NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN,
+ NGHTTP3_HTTP_STATE_REQ_HEADERS_END,
+ NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN,
+ NGHTTP3_HTTP_STATE_REQ_DATA_END,
+ NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN,
+ NGHTTP3_HTTP_STATE_REQ_TRAILERS_END,
+ NGHTTP3_HTTP_STATE_REQ_END,
+ NGHTTP3_HTTP_STATE_RESP_INITIAL,
+ NGHTTP3_HTTP_STATE_RESP_BEGIN,
+ NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN,
+ NGHTTP3_HTTP_STATE_RESP_HEADERS_END,
+ NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN,
+ NGHTTP3_HTTP_STATE_RESP_DATA_END,
+ NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN,
+ NGHTTP3_HTTP_STATE_RESP_TRAILERS_END,
+ NGHTTP3_HTTP_STATE_RESP_END,
+} nghttp3_stream_http_state;
+
+typedef enum nghttp3_stream_http_event {
+ NGHTTP3_HTTP_EVENT_DATA_BEGIN,
+ NGHTTP3_HTTP_EVENT_DATA_END,
+ NGHTTP3_HTTP_EVENT_HEADERS_BEGIN,
+ NGHTTP3_HTTP_EVENT_HEADERS_END,
+ NGHTTP3_HTTP_EVENT_MSG_END,
+} nghttp3_stream_http_event;
+
+typedef struct nghttp3_stream nghttp3_stream;
+
+/*
+ * nghttp3_stream_acked_data is a callback function which is invoked
+ * when data sent on stream denoted by |stream_id| supplied from
+ * application is acknowledged by remote endpoint. The number of
+ * bytes acknowledged is given in |datalen|.
+ *
+ * The implementation of this callback must return 0 if it succeeds.
+ * Returning NGHTTP3_ERR_CALLBACK_FAILURE will return to the caller
+ * immediately. Any values other than 0 is treated as
+ * NGHTTP3_ERR_CALLBACK_FAILURE.
+ */
+typedef int (*nghttp3_stream_acked_data)(nghttp3_stream *stream,
+ int64_t stream_id, uint64_t datalen,
+ void *user_data);
+
+typedef struct nghttp3_stream_callbacks {
+ nghttp3_stream_acked_data acked_data;
+} nghttp3_stream_callbacks;
+
+typedef struct nghttp3_http_state {
+ /* status_code is HTTP status code received. This field is used
+ if connection is initialized as client. */
+ int32_t status_code;
+ /* content_length is the value of received content-length header
+ field. */
+ int64_t content_length;
+ /* recv_content_length is the number of body bytes received so
+ far. */
+ int64_t recv_content_length;
+ uint32_t flags;
+ /* pri is a stream priority produced by nghttp3_pri_to_uint8. */
+ uint8_t pri;
+} nghttp3_http_state;
+
+struct nghttp3_stream {
+ union {
+ struct {
+ const nghttp3_mem *mem;
+ nghttp3_objalloc *out_chunk_objalloc;
+ nghttp3_objalloc *stream_objalloc;
+ nghttp3_tnode node;
+ nghttp3_pq_entry qpack_blocked_pe;
+ nghttp3_stream_callbacks callbacks;
+ nghttp3_ringbuf frq;
+ nghttp3_ringbuf chunks;
+ nghttp3_ringbuf outq;
+ /* inq stores the stream raw data which cannot be read because
+ stream is blocked by QPACK decoder. */
+ nghttp3_ringbuf inq;
+ nghttp3_qpack_stream_context qpack_sctx;
+ /* conn is a reference to underlying connection. It could be NULL
+ if stream is not a request stream. */
+ nghttp3_conn *conn;
+ void *user_data;
+ /* unsent_bytes is the number of bytes in outq not written yet */
+ uint64_t unsent_bytes;
+ /* outq_idx is an index into outq where next write is made. */
+ size_t outq_idx;
+ /* outq_offset is write offset relative to the element at outq_idx
+ in outq. */
+ uint64_t outq_offset;
+ /* ack_offset is offset acknowledged by peer relative to the first
+ element in outq. */
+ uint64_t ack_offset;
+ /* ack_done is the number of bytes notified to an application that
+ they are acknowledged inside the first outq element if it is of
+ type NGHTTP3_BUF_TYPE_ALIEN. */
+ uint64_t ack_done;
+ uint64_t unscheduled_nwrite;
+ nghttp3_stream_type type;
+ nghttp3_stream_read_state rstate;
+ /* error_code indicates the reason of closure of this stream. */
+ uint64_t error_code;
+
+ struct {
+ uint64_t offset;
+ nghttp3_stream_http_state hstate;
+ } tx;
+
+ struct {
+ nghttp3_stream_http_state hstate;
+ nghttp3_http_state http;
+ } rx;
+
+ uint16_t flags;
+ };
+
+ nghttp3_opl_entry oplent;
+ };
+};
+
+nghttp3_objalloc_def(stream, nghttp3_stream, oplent);
+
+typedef struct nghttp3_frame_entry {
+ nghttp3_frame fr;
+ union {
+ struct {
+ nghttp3_settings *local_settings;
+ } settings;
+ struct {
+ nghttp3_data_reader dr;
+ } data;
+ } aux;
+} nghttp3_frame_entry;
+
+int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id,
+ const nghttp3_stream_callbacks *callbacks,
+ nghttp3_objalloc *out_chunk_objalloc,
+ nghttp3_objalloc *stream_objalloc,
+ const nghttp3_mem *mem);
+
+void nghttp3_stream_del(nghttp3_stream *stream);
+
+void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint);
+
+void nghttp3_stream_read_state_reset(nghttp3_stream_read_state *rstate);
+
+nghttp3_ssize nghttp3_read_varint(nghttp3_varint_read_state *rvint,
+ const uint8_t *src, size_t srclen, int fin);
+
+int nghttp3_stream_frq_add(nghttp3_stream *stream,
+ const nghttp3_frame_entry *frent);
+
+int nghttp3_stream_fill_outq(nghttp3_stream *stream);
+
+int nghttp3_stream_write_stream_type(nghttp3_stream *stream);
+
+nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin,
+ nghttp3_vec *vec, size_t veccnt);
+
+int nghttp3_stream_write_qpack_decoder_stream(nghttp3_stream *stream);
+
+int nghttp3_stream_outq_is_full(nghttp3_stream *stream);
+
+int nghttp3_stream_outq_add(nghttp3_stream *stream,
+ const nghttp3_typed_buf *tbuf);
+
+int nghttp3_stream_write_headers(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent);
+
+int nghttp3_stream_write_header_block(nghttp3_stream *stream,
+ nghttp3_qpack_encoder *qenc,
+ nghttp3_stream *qenc_stream,
+ nghttp3_buf *rbuf, nghttp3_buf *ebuf,
+ int64_t frame_type, const nghttp3_nv *nva,
+ size_t nvlen);
+
+int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
+ nghttp3_frame_entry *frent);
+
+int nghttp3_stream_write_settings(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent);
+
+int nghttp3_stream_write_goaway(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent);
+
+int nghttp3_stream_write_priority_update(nghttp3_stream *stream,
+ nghttp3_frame_entry *frent);
+
+int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need);
+
+nghttp3_buf *nghttp3_stream_get_chunk(nghttp3_stream *stream);
+
+int nghttp3_stream_is_blocked(nghttp3_stream *stream);
+
+int nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n);
+
+/*
+ * nghttp3_stream_outq_write_done returns nonzero if all contents in
+ * outq have been written.
+ */
+int nghttp3_stream_outq_write_done(nghttp3_stream *stream);
+
+int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n);
+
+/*
+ * nghttp3_stream_is_active returns nonzero if |stream| is active. In
+ * other words, it has something to send. This function does not take
+ * into account its descendants.
+ */
+int nghttp3_stream_is_active(nghttp3_stream *stream);
+
+/*
+ * nghttp3_stream_require_schedule returns nonzero if |stream| should
+ * be scheduled. In other words, |stream| or its descendants have
+ * something to send.
+ */
+int nghttp3_stream_require_schedule(nghttp3_stream *stream);
+
+int nghttp3_stream_buffer_data(nghttp3_stream *stream, const uint8_t *src,
+ size_t srclen);
+
+size_t nghttp3_stream_get_buffered_datalen(nghttp3_stream *stream);
+
+int nghttp3_stream_ensure_qpack_stream_context(nghttp3_stream *stream);
+
+void nghttp3_stream_delete_qpack_stream_context(nghttp3_stream *stream);
+
+int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
+ nghttp3_stream_http_event event);
+
+int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream);
+
+/*
+ * nghttp3_stream_uni returns nonzero if stream identified by
+ * |stream_id| is unidirectional.
+ */
+int nghttp3_stream_uni(int64_t stream_id);
+
+/*
+ * nghttp3_client_stream_bidi returns nonzero if stream identified by
+ * |stream_id| is client initiated bidirectional stream.
+ */
+int nghttp3_client_stream_bidi(int64_t stream_id);
+
+/*
+ * nghttp3_client_stream_uni returns nonzero if stream identified by
+ * |stream_id| is client initiated unidirectional stream.
+ */
+int nghttp3_client_stream_uni(int64_t stream_id);
+
+/*
+ * nghttp3_server_stream_uni returns nonzero if stream identified by
+ * |stream_id| is server initiated unidirectional stream.
+ */
+int nghttp3_server_stream_uni(int64_t stream_id);
+
+#endif /* NGHTTP3_STREAM_H */
diff --git a/lib/nghttp3_tnode.c b/lib/nghttp3_tnode.c
new file mode 100644
index 0000000..2fe7f3f
--- /dev/null
+++ b/lib/nghttp3_tnode.c
@@ -0,0 +1,97 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_tnode.h"
+
+#include <assert.h>
+
+#include "nghttp3_macro.h"
+#include "nghttp3_stream.h"
+#include "nghttp3_conn.h"
+#include "nghttp3_conv.h"
+
+void nghttp3_tnode_init(nghttp3_tnode *tnode, int64_t id, uint8_t pri) {
+ assert(nghttp3_pri_uint8_urgency(pri) < NGHTTP3_URGENCY_LEVELS);
+
+ tnode->pe.index = NGHTTP3_PQ_BAD_INDEX;
+ tnode->id = id;
+ tnode->cycle = 0;
+ tnode->pri = pri;
+}
+
+void nghttp3_tnode_free(nghttp3_tnode *tnode) { (void)tnode; }
+
+static void tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq) {
+ assert(tnode->pe.index != NGHTTP3_PQ_BAD_INDEX);
+
+ nghttp3_pq_remove(pq, &tnode->pe);
+ tnode->pe.index = NGHTTP3_PQ_BAD_INDEX;
+}
+
+void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq) {
+ if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) {
+ return;
+ }
+
+ tnode_unschedule(tnode, pq);
+}
+
+static uint64_t pq_get_first_cycle(nghttp3_pq *pq) {
+ nghttp3_tnode *top;
+
+ if (nghttp3_pq_empty(pq)) {
+ return 0;
+ }
+
+ top = nghttp3_struct_of(nghttp3_pq_top(pq), nghttp3_tnode, pe);
+ return top->cycle;
+}
+
+int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq,
+ uint64_t nwrite) {
+ uint64_t penalty = nwrite / NGHTTP3_STREAM_MIN_WRITELEN;
+
+ if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) {
+ tnode->cycle = pq_get_first_cycle(pq) +
+ ((nwrite == 0 || !nghttp3_pri_uint8_inc(tnode->pri))
+ ? 0
+ : nghttp3_max(1, penalty));
+ } else if (nwrite > 0) {
+ if (!nghttp3_pri_uint8_inc(tnode->pri) || nghttp3_pq_size(pq) == 1) {
+ return 0;
+ }
+
+ nghttp3_pq_remove(pq, &tnode->pe);
+ tnode->pe.index = NGHTTP3_PQ_BAD_INDEX;
+ tnode->cycle += nghttp3_max(1, penalty);
+ } else {
+ return 0;
+ }
+
+ return nghttp3_pq_push(pq, &tnode->pe);
+}
+
+int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode) {
+ return tnode->pe.index != NGHTTP3_PQ_BAD_INDEX;
+}
diff --git a/lib/nghttp3_tnode.h b/lib/nghttp3_tnode.h
new file mode 100644
index 0000000..f97120a
--- /dev/null
+++ b/lib/nghttp3_tnode.h
@@ -0,0 +1,66 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_TNODE_H
+#define NGHTTP3_TNODE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include "nghttp3_pq.h"
+
+#define NGHTTP3_TNODE_MAX_CYCLE_GAP (1llu << 24)
+
+typedef struct nghttp3_tnode {
+ nghttp3_pq_entry pe;
+ size_t num_children;
+ int64_t id;
+ uint64_t cycle;
+ /* pri is a stream priority produced by nghttp3_pri_to_uint8. */
+ uint8_t pri;
+} nghttp3_tnode;
+
+void nghttp3_tnode_init(nghttp3_tnode *tnode, int64_t id, uint8_t pri);
+
+void nghttp3_tnode_free(nghttp3_tnode *tnode);
+
+void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq);
+
+/*
+ * nghttp3_tnode_schedule schedules |tnode| using |nwrite| as penalty.
+ * If |tnode| has already been scheduled, it is rescheduled by the
+ * amount of |nwrite|.
+ */
+int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq,
+ uint64_t nwrite);
+
+/*
+ * nghttp3_tnode_is_scheduled returns nonzero if |tnode| is scheduled.
+ */
+int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode);
+
+#endif /* NGHTTP3_TNODE_H */
diff --git a/lib/nghttp3_unreachable.c b/lib/nghttp3_unreachable.c
new file mode 100644
index 0000000..6fea89b
--- /dev/null
+++ b/lib/nghttp3_unreachable.c
@@ -0,0 +1,72 @@
+/*
+ * 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_unreachable.h"
+
+#include <stdio.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <stdlib.h>
+#ifdef WIN32
+# include <io.h>
+#endif /* WIN32 */
+
+void nghttp3_unreachable_fail(const char *file, int line, const char *func) {
+ char *buf;
+ size_t buflen;
+ int rv;
+
+#define NGHTTP3_UNREACHABLE_TEMPLATE "%s:%d %s: Unreachable.\n"
+
+ rv = snprintf(NULL, 0, NGHTTP3_UNREACHABLE_TEMPLATE, file, line, func);
+ if (rv < 0) {
+ abort();
+ }
+
+ /* here we explicitly use system malloc */
+ buflen = (size_t)rv + 1;
+ buf = malloc(buflen);
+ if (buf == NULL) {
+ abort();
+ }
+
+ rv = snprintf(buf, buflen, NGHTTP3_UNREACHABLE_TEMPLATE, file, line, func);
+ if (rv < 0) {
+ abort();
+ }
+
+#ifndef WIN32
+ while (write(STDERR_FILENO, buf, (size_t)rv) == -1 && errno == EINTR)
+ ;
+#else /* WIN32 */
+ _write(_fileno(stderr), buf, (unsigned int)rv);
+#endif /* WIN32 */
+
+ free(buf);
+
+ abort();
+}
diff --git a/lib/nghttp3_unreachable.h b/lib/nghttp3_unreachable.h
new file mode 100644
index 0000000..c3520a4
--- /dev/null
+++ b/lib/nghttp3_unreachable.h
@@ -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.
+ */
+#ifndef NGHTTP3_UNREACHABLE_H
+#define NGHTTP3_UNREACHABLE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#define nghttp3_unreachable() \
+ nghttp3_unreachable_fail(__FILE__, __LINE__, __func__)
+
+#ifdef _MSC_VER
+__declspec(noreturn)
+#endif /* _MSC_VER */
+ void nghttp3_unreachable_fail(const char *file, int line, const char *func)
+#ifndef _MSC_VER
+ __attribute__((noreturn))
+#endif /* !_MSC_VER */
+ ;
+
+#endif /* NGHTTP3_UNREACHABLE_H */
diff --git a/lib/nghttp3_vec.c b/lib/nghttp3_vec.c
new file mode 100644
index 0000000..ab58ff5
--- /dev/null
+++ b/lib/nghttp3_vec.c
@@ -0,0 +1,55 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_vec.h"
+#include "nghttp3_macro.h"
+
+uint64_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) {
+ size_t i;
+ uint64_t res = 0;
+
+ for (i = 0; i < n; ++i) {
+ res += vec[i].len;
+ }
+
+ return res;
+}
+
+int64_t nghttp3_vec_len_varint(const nghttp3_vec *vec, size_t n) {
+ uint64_t res = 0;
+ size_t len;
+ size_t i;
+
+ for (i = 0; i < n; ++i) {
+ len = vec[i].len;
+ if (len > NGHTTP3_MAX_VARINT - res) {
+ return -1;
+ }
+
+ res += len;
+ }
+
+ return (int64_t)res;
+}
diff --git a/lib/nghttp3_vec.h b/lib/nghttp3_vec.h
new file mode 100644
index 0000000..473d146
--- /dev/null
+++ b/lib/nghttp3_vec.h
@@ -0,0 +1,41 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGHTTP3_VEC_H
+#define NGHTTP3_VEC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+/*
+ * nghttp3_vec_len_varint is similar to nghttp3_vec_len, but it
+ * returns -1 if the sum of the length exceeds NGHTTP3_MAX_VARINT.
+ */
+int64_t nghttp3_vec_len_varint(const nghttp3_vec *vec, size_t n);
+
+#endif /* NGHTTP3_VEC_H */
diff --git a/lib/nghttp3_version.c b/lib/nghttp3_version.c
new file mode 100644
index 0000000..c460cc7
--- /dev/null
+++ b/lib/nghttp3_version.c
@@ -0,0 +1,39 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+static nghttp3_info version = {NGHTTP3_VERSION_AGE, NGHTTP3_VERSION_NUM,
+ NGHTTP3_VERSION};
+
+const nghttp3_info *nghttp3_version(int least_version) {
+ if (least_version > NGHTTP3_VERSION_NUM) {
+ return NULL;
+ }
+ return &version;
+}
diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4
new file mode 100644
index 0000000..ca36397
--- /dev/null
+++ b/m4/ax_check_compile_flag.m4
@@ -0,0 +1,74 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+# Check whether the given FLAG works with the current language's compiler
+# or gives an error. (Warnings, however, are ignored)
+#
+# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+# success/failure.
+#
+# If EXTRA-FLAGS is defined, it is added to the current language's default
+# flags (e.g. CFLAGS) when the check is done. The check is thus made with
+# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
+# force the compiler to issue an error when a bad flag is given.
+#
+# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
+#
+# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 4
+
+AC_DEFUN([AX_CHECK_COMPILE_FLAG],
+[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
+AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
+ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
+ _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
+ AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
+ [AS_VAR_SET(CACHEVAR,[yes])],
+ [AS_VAR_SET(CACHEVAR,[no])])
+ _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
+AS_VAR_IF(CACHEVAR,yes,
+ [m4_default([$2], :)],
+ [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_COMPILE_FLAGS
diff --git a/m4/ax_cxx_compile_stdcxx.m4 b/m4/ax_cxx_compile_stdcxx.m4
new file mode 100644
index 0000000..9e9eaed
--- /dev/null
+++ b/m4/ax_cxx_compile_stdcxx.m4
@@ -0,0 +1,948 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
+#
+# DESCRIPTION
+#
+# Check for baseline language coverage in the compiler for the specified
+# version of the C++ standard. If necessary, add switches to CXX and
+# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
+# or '14' (for the C++14 standard).
+#
+# The second argument, if specified, indicates whether you insist on an
+# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
+# -std=c++11). If neither is specified, you get whatever works, with
+# preference for an extended mode.
+#
+# The third argument, if specified 'mandatory' or if left unspecified,
+# indicates that baseline support for the specified C++ standard is
+# required and that the macro should error out if no mode with that
+# support is found. If specified 'optional', then configuration proceeds
+# regardless, after defining HAVE_CXX${VERSION} if and only if a
+# supporting mode is found.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+# Copyright (c) 2015 Paul Norman <penorman@mac.com>
+# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+# Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 10
+
+dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
+dnl (serial version number 13).
+
+AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
+ m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
+ [$1], [14], [ax_cxx_compile_alternatives="14 1y"],
+ [$1], [17], [ax_cxx_compile_alternatives="17 1z"],
+ [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
+ m4_if([$2], [], [],
+ [$2], [ext], [],
+ [$2], [noext], [],
+ [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
+ m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
+ [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
+ [$3], [optional], [ax_cxx_compile_cxx$1_required=false],
+ [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
+ AC_LANG_PUSH([C++])dnl
+ ac_success=no
+
+ m4_if([$2], [noext], [], [dnl
+ if test x$ac_success = xno; then
+ for alternative in ${ax_cxx_compile_alternatives}; do
+ switch="-std=gnu++${alternative}"
+ cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+ AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+ $cachevar,
+ [ac_save_CXX="$CXX"
+ CXX="$CXX $switch"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+ [eval $cachevar=yes],
+ [eval $cachevar=no])
+ CXX="$ac_save_CXX"])
+ if eval test x\$$cachevar = xyes; then
+ CXX="$CXX $switch"
+ if test -n "$CXXCPP" ; then
+ CXXCPP="$CXXCPP $switch"
+ fi
+ ac_success=yes
+ break
+ fi
+ done
+ fi])
+
+ m4_if([$2], [ext], [], [dnl
+ if test x$ac_success = xno; then
+ dnl HP's aCC needs +std=c++11 according to:
+ dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
+ dnl Cray's crayCC needs "-h std=c++11"
+ for alternative in ${ax_cxx_compile_alternatives}; do
+ for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
+ cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+ AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+ $cachevar,
+ [ac_save_CXX="$CXX"
+ CXX="$CXX $switch"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+ [eval $cachevar=yes],
+ [eval $cachevar=no])
+ CXX="$ac_save_CXX"])
+ if eval test x\$$cachevar = xyes; then
+ CXX="$CXX $switch"
+ if test -n "$CXXCPP" ; then
+ CXXCPP="$CXXCPP $switch"
+ fi
+ ac_success=yes
+ break
+ fi
+ done
+ if test x$ac_success = xyes; then
+ break
+ fi
+ done
+ fi])
+ AC_LANG_POP([C++])
+ if test x$ax_cxx_compile_cxx$1_required = xtrue; then
+ if test x$ac_success = xno; then
+ AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
+ fi
+ fi
+ if test x$ac_success = xno; then
+ HAVE_CXX$1=0
+ AC_MSG_NOTICE([No compiler with C++$1 support was found])
+ else
+ HAVE_CXX$1=1
+ AC_DEFINE(HAVE_CXX$1,1,
+ [define if the compiler supports basic C++$1 syntax])
+ fi
+ AC_SUBST(HAVE_CXX$1)
+])
+
+
+dnl Test body for checking C++11 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+)
+
+
+dnl Test body for checking C++14 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+)
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_17
+)
+
+dnl Tests for new features in C++11
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
+
+// If the compiler admits that it is not ready for C++11, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201103L
+
+#error "This is not a C++11 compiler"
+
+#else
+
+namespace cxx11
+{
+
+ namespace test_static_assert
+ {
+
+ template <typename T>
+ struct check
+ {
+ static_assert(sizeof(int) <= sizeof(T), "not big enough");
+ };
+
+ }
+
+ namespace test_final_override
+ {
+
+ struct Base
+ {
+ virtual void f() {}
+ };
+
+ struct Derived : public Base
+ {
+ virtual void f() override {}
+ };
+
+ }
+
+ namespace test_double_right_angle_brackets
+ {
+
+ template < typename T >
+ struct check {};
+
+ typedef check<void> single_type;
+ typedef check<check<void>> double_type;
+ typedef check<check<check<void>>> triple_type;
+ typedef check<check<check<check<void>>>> quadruple_type;
+
+ }
+
+ namespace test_decltype
+ {
+
+ int
+ f()
+ {
+ int a = 1;
+ decltype(a) b = 2;
+ return a + b;
+ }
+
+ }
+
+ namespace test_type_deduction
+ {
+
+ template < typename T1, typename T2 >
+ struct is_same
+ {
+ static const bool value = false;
+ };
+
+ template < typename T >
+ struct is_same<T, T>
+ {
+ static const bool value = true;
+ };
+
+ template < typename T1, typename T2 >
+ auto
+ add(T1 a1, T2 a2) -> decltype(a1 + a2)
+ {
+ return a1 + a2;
+ }
+
+ int
+ test(const int c, volatile int v)
+ {
+ static_assert(is_same<int, decltype(0)>::value == true, "");
+ static_assert(is_same<int, decltype(c)>::value == false, "");
+ static_assert(is_same<int, decltype(v)>::value == false, "");
+ auto ac = c;
+ auto av = v;
+ auto sumi = ac + av + 'x';
+ auto sumf = ac + av + 1.0;
+ static_assert(is_same<int, decltype(ac)>::value == true, "");
+ static_assert(is_same<int, decltype(av)>::value == true, "");
+ static_assert(is_same<int, decltype(sumi)>::value == true, "");
+ static_assert(is_same<int, decltype(sumf)>::value == false, "");
+ static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
+ return (sumf > 0.0) ? sumi : add(c, v);
+ }
+
+ }
+
+ namespace test_noexcept
+ {
+
+ int f() { return 0; }
+ int g() noexcept { return 0; }
+
+ static_assert(noexcept(f()) == false, "");
+ static_assert(noexcept(g()) == true, "");
+
+ }
+
+ namespace test_constexpr
+ {
+
+ template < typename CharT >
+ unsigned long constexpr
+ strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
+ {
+ return *s ? strlen_c_r(s + 1, acc + 1) : acc;
+ }
+
+ template < typename CharT >
+ unsigned long constexpr
+ strlen_c(const CharT *const s) noexcept
+ {
+ return strlen_c_r(s, 0UL);
+ }
+
+ static_assert(strlen_c("") == 0UL, "");
+ static_assert(strlen_c("1") == 1UL, "");
+ static_assert(strlen_c("example") == 7UL, "");
+ static_assert(strlen_c("another\0example") == 7UL, "");
+
+ }
+
+ namespace test_rvalue_references
+ {
+
+ template < int N >
+ struct answer
+ {
+ static constexpr int value = N;
+ };
+
+ answer<1> f(int&) { return answer<1>(); }
+ answer<2> f(const int&) { return answer<2>(); }
+ answer<3> f(int&&) { return answer<3>(); }
+
+ void
+ test()
+ {
+ int i = 0;
+ const int c = 0;
+ static_assert(decltype(f(i))::value == 1, "");
+ static_assert(decltype(f(c))::value == 2, "");
+ static_assert(decltype(f(0))::value == 3, "");
+ }
+
+ }
+
+ namespace test_uniform_initialization
+ {
+
+ struct test
+ {
+ static const int zero {};
+ static const int one {1};
+ };
+
+ static_assert(test::zero == 0, "");
+ static_assert(test::one == 1, "");
+
+ }
+
+ namespace test_lambdas
+ {
+
+ void
+ test1()
+ {
+ auto lambda1 = [](){};
+ auto lambda2 = lambda1;
+ lambda1();
+ lambda2();
+ }
+
+ int
+ test2()
+ {
+ auto a = [](int i, int j){ return i + j; }(1, 2);
+ auto b = []() -> int { return '0'; }();
+ auto c = [=](){ return a + b; }();
+ auto d = [&](){ return c; }();
+ auto e = [a, &b](int x) mutable {
+ const auto identity = [](int y){ return y; };
+ for (auto i = 0; i < a; ++i)
+ a += b--;
+ return x + identity(a + b);
+ }(0);
+ return a + b + c + d + e;
+ }
+
+ int
+ test3()
+ {
+ const auto nullary = [](){ return 0; };
+ const auto unary = [](int x){ return x; };
+ using nullary_t = decltype(nullary);
+ using unary_t = decltype(unary);
+ const auto higher1st = [](nullary_t f){ return f(); };
+ const auto higher2nd = [unary](nullary_t f1){
+ return [unary, f1](unary_t f2){ return f2(unary(f1())); };
+ };
+ return higher1st(nullary) + higher2nd(nullary)(unary);
+ }
+
+ }
+
+ namespace test_variadic_templates
+ {
+
+ template <int...>
+ struct sum;
+
+ template <int N0, int... N1toN>
+ struct sum<N0, N1toN...>
+ {
+ static constexpr auto value = N0 + sum<N1toN...>::value;
+ };
+
+ template <>
+ struct sum<>
+ {
+ static constexpr auto value = 0;
+ };
+
+ static_assert(sum<>::value == 0, "");
+ static_assert(sum<1>::value == 1, "");
+ static_assert(sum<23>::value == 23, "");
+ static_assert(sum<1, 2>::value == 3, "");
+ static_assert(sum<5, 5, 11>::value == 21, "");
+ static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
+
+ }
+
+ // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+ // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
+ // because of this.
+ namespace test_template_alias_sfinae
+ {
+
+ struct foo {};
+
+ template<typename T>
+ using member = typename T::member_type;
+
+ template<typename T>
+ void func(...) {}
+
+ template<typename T>
+ void func(member<T>*) {}
+
+ void test();
+
+ void test() { func<foo>(0); }
+
+ }
+
+} // namespace cxx11
+
+#endif // __cplusplus >= 201103L
+
+]])
+
+
+dnl Tests for new features in C++14
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
+
+// If the compiler admits that it is not ready for C++14, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201402L
+
+#error "This is not a C++14 compiler"
+
+#else
+
+namespace cxx14
+{
+
+ namespace test_polymorphic_lambdas
+ {
+
+ int
+ test()
+ {
+ const auto lambda = [](auto&&... args){
+ const auto istiny = [](auto x){
+ return (sizeof(x) == 1UL) ? 1 : 0;
+ };
+ const int aretiny[] = { istiny(args)... };
+ return aretiny[0];
+ };
+ return lambda(1, 1L, 1.0f, '1');
+ }
+
+ }
+
+ namespace test_binary_literals
+ {
+
+ constexpr auto ivii = 0b0000000000101010;
+ static_assert(ivii == 42, "wrong value");
+
+ }
+
+ namespace test_generalized_constexpr
+ {
+
+ template < typename CharT >
+ constexpr unsigned long
+ strlen_c(const CharT *const s) noexcept
+ {
+ auto length = 0UL;
+ for (auto p = s; *p; ++p)
+ ++length;
+ return length;
+ }
+
+ static_assert(strlen_c("") == 0UL, "");
+ static_assert(strlen_c("x") == 1UL, "");
+ static_assert(strlen_c("test") == 4UL, "");
+ static_assert(strlen_c("another\0test") == 7UL, "");
+
+ }
+
+ namespace test_lambda_init_capture
+ {
+
+ int
+ test()
+ {
+ auto x = 0;
+ const auto lambda1 = [a = x](int b){ return a + b; };
+ const auto lambda2 = [a = lambda1(x)](){ return a; };
+ return lambda2();
+ }
+
+ }
+
+ namespace test_digit_separators
+ {
+
+ constexpr auto ten_million = 100'000'000;
+ static_assert(ten_million == 100000000, "");
+
+ }
+
+ namespace test_return_type_deduction
+ {
+
+ auto f(int& x) { return x; }
+ decltype(auto) g(int& x) { return x; }
+
+ template < typename T1, typename T2 >
+ struct is_same
+ {
+ static constexpr auto value = false;
+ };
+
+ template < typename T >
+ struct is_same<T, T>
+ {
+ static constexpr auto value = true;
+ };
+
+ int
+ test()
+ {
+ auto x = 0;
+ static_assert(is_same<int, decltype(f(x))>::value, "");
+ static_assert(is_same<int&, decltype(g(x))>::value, "");
+ return x;
+ }
+
+ }
+
+} // namespace cxx14
+
+#endif // __cplusplus >= 201402L
+
+]])
+
+
+dnl Tests for new features in C++17
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
+
+// If the compiler admits that it is not ready for C++17, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201703L
+
+#error "This is not a C++17 compiler"
+
+#else
+
+#include <initializer_list>
+#include <utility>
+#include <type_traits>
+
+namespace cxx17
+{
+
+ namespace test_constexpr_lambdas
+ {
+
+ constexpr int foo = [](){return 42;}();
+
+ }
+
+ namespace test::nested_namespace::definitions
+ {
+
+ }
+
+ namespace test_fold_expression
+ {
+
+ template<typename... Args>
+ int multiply(Args... args)
+ {
+ return (args * ... * 1);
+ }
+
+ template<typename... Args>
+ bool all(Args... args)
+ {
+ return (args && ...);
+ }
+
+ }
+
+ namespace test_extended_static_assert
+ {
+
+ static_assert (true);
+
+ }
+
+ namespace test_auto_brace_init_list
+ {
+
+ auto foo = {5};
+ auto bar {5};
+
+ static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
+ static_assert(std::is_same<int, decltype(bar)>::value);
+ }
+
+ namespace test_typename_in_template_template_parameter
+ {
+
+ template<template<typename> typename X> struct D;
+
+ }
+
+ namespace test_fallthrough_nodiscard_maybe_unused_attributes
+ {
+
+ int f1()
+ {
+ return 42;
+ }
+
+ [[nodiscard]] int f2()
+ {
+ [[maybe_unused]] auto unused = f1();
+
+ switch (f1())
+ {
+ case 17:
+ f1();
+ [[fallthrough]];
+ case 42:
+ f1();
+ }
+ return f1();
+ }
+
+ }
+
+ namespace test_extended_aggregate_initialization
+ {
+
+ struct base1
+ {
+ int b1, b2 = 42;
+ };
+
+ struct base2
+ {
+ base2() {
+ b3 = 42;
+ }
+ int b3;
+ };
+
+ struct derived : base1, base2
+ {
+ int d;
+ };
+
+ derived d1 {{1, 2}, {}, 4}; // full initialization
+ derived d2 {{}, {}, 4}; // value-initialized bases
+
+ }
+
+ namespace test_general_range_based_for_loop
+ {
+
+ struct iter
+ {
+ int i;
+
+ int& operator* ()
+ {
+ return i;
+ }
+
+ const int& operator* () const
+ {
+ return i;
+ }
+
+ iter& operator++()
+ {
+ ++i;
+ return *this;
+ }
+ };
+
+ struct sentinel
+ {
+ int i;
+ };
+
+ bool operator== (const iter& i, const sentinel& s)
+ {
+ return i.i == s.i;
+ }
+
+ bool operator!= (const iter& i, const sentinel& s)
+ {
+ return !(i == s);
+ }
+
+ struct range
+ {
+ iter begin() const
+ {
+ return {0};
+ }
+
+ sentinel end() const
+ {
+ return {5};
+ }
+ };
+
+ void f()
+ {
+ range r {};
+
+ for (auto i : r)
+ {
+ [[maybe_unused]] auto v = i;
+ }
+ }
+
+ }
+
+ namespace test_lambda_capture_asterisk_this_by_value
+ {
+
+ struct t
+ {
+ int i;
+ int foo()
+ {
+ return [*this]()
+ {
+ return i;
+ }();
+ }
+ };
+
+ }
+
+ namespace test_enum_class_construction
+ {
+
+ enum class byte : unsigned char
+ {};
+
+ byte foo {42};
+
+ }
+
+ namespace test_constexpr_if
+ {
+
+ template <bool cond>
+ int f ()
+ {
+ if constexpr(cond)
+ {
+ return 13;
+ }
+ else
+ {
+ return 42;
+ }
+ }
+
+ }
+
+ namespace test_selection_statement_with_initializer
+ {
+
+ int f()
+ {
+ return 13;
+ }
+
+ int f2()
+ {
+ if (auto i = f(); i > 0)
+ {
+ return 3;
+ }
+
+ switch (auto i = f(); i + 4)
+ {
+ case 17:
+ return 2;
+
+ default:
+ return 1;
+ }
+ }
+
+ }
+
+ namespace test_template_argument_deduction_for_class_templates
+ {
+
+ template <typename T1, typename T2>
+ struct pair
+ {
+ pair (T1 p1, T2 p2)
+ : m1 {p1},
+ m2 {p2}
+ {}
+
+ T1 m1;
+ T2 m2;
+ };
+
+ void f()
+ {
+ [[maybe_unused]] auto p = pair{13, 42u};
+ }
+
+ }
+
+ namespace test_non_type_auto_template_parameters
+ {
+
+ template <auto n>
+ struct B
+ {};
+
+ B<5> b1;
+ B<'a'> b2;
+
+ }
+
+ namespace test_structured_bindings
+ {
+
+ int arr[2] = { 1, 2 };
+ std::pair<int, int> pr = { 1, 2 };
+
+ auto f1() -> int(&)[2]
+ {
+ return arr;
+ }
+
+ auto f2() -> std::pair<int, int>&
+ {
+ return pr;
+ }
+
+ struct S
+ {
+ int x1 : 2;
+ volatile double y1;
+ };
+
+ S f3()
+ {
+ return {};
+ }
+
+ auto [ x1, y1 ] = f1();
+ auto& [ xr1, yr1 ] = f1();
+ auto [ x2, y2 ] = f2();
+ auto& [ xr2, yr2 ] = f2();
+ const auto [ x3, y3 ] = f3();
+
+ }
+
+ namespace test_exception_spec_type_system
+ {
+
+ struct Good {};
+ struct Bad {};
+
+ void g1() noexcept;
+ void g2();
+
+ template<typename T>
+ Bad
+ f(T*, T*);
+
+ template<typename T1, typename T2>
+ Good
+ f(T1*, T2*);
+
+ static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
+
+ }
+
+ namespace test_inline_variables
+ {
+
+ template<class T> void f(T)
+ {}
+
+ template<class T> inline T g(T)
+ {
+ return T{};
+ }
+
+ template<> inline void f<>(int)
+ {}
+
+ template<> int g<>(int)
+ {
+ return 5;
+ }
+
+ }
+
+} // namespace cxx17
+
+#endif // __cplusplus < 201703L
+
+]])
diff --git a/mkhufftbl.py b/mkhufftbl.py
new file mode 100755
index 0000000..3c30188
--- /dev/null
+++ b/mkhufftbl.py
@@ -0,0 +1,468 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# This script reads Huffman Code table [1] and generates symbol table
+# and decoding tables in C language. The resulting code is used in
+# lib/nghttp3_qpack_huffman.h and lib/nghttp3_qpack_huffman_data.c
+#
+# [1] http://http2.github.io/http2-spec/compression.html
+
+import re
+import sys
+import io
+
+# From [1]
+HUFFMAN_CODE_TABLE = """\
+ ( 0) |11111111|11000 1ff8 [13]
+ ( 1) |11111111|11111111|1011000 7fffd8 [23]
+ ( 2) |11111111|11111111|11111110|0010 fffffe2 [28]
+ ( 3) |11111111|11111111|11111110|0011 fffffe3 [28]
+ ( 4) |11111111|11111111|11111110|0100 fffffe4 [28]
+ ( 5) |11111111|11111111|11111110|0101 fffffe5 [28]
+ ( 6) |11111111|11111111|11111110|0110 fffffe6 [28]
+ ( 7) |11111111|11111111|11111110|0111 fffffe7 [28]
+ ( 8) |11111111|11111111|11111110|1000 fffffe8 [28]
+ ( 9) |11111111|11111111|11101010 ffffea [24]
+ ( 10) |11111111|11111111|11111111|111100 3ffffffc [30]
+ ( 11) |11111111|11111111|11111110|1001 fffffe9 [28]
+ ( 12) |11111111|11111111|11111110|1010 fffffea [28]
+ ( 13) |11111111|11111111|11111111|111101 3ffffffd [30]
+ ( 14) |11111111|11111111|11111110|1011 fffffeb [28]
+ ( 15) |11111111|11111111|11111110|1100 fffffec [28]
+ ( 16) |11111111|11111111|11111110|1101 fffffed [28]
+ ( 17) |11111111|11111111|11111110|1110 fffffee [28]
+ ( 18) |11111111|11111111|11111110|1111 fffffef [28]
+ ( 19) |11111111|11111111|11111111|0000 ffffff0 [28]
+ ( 20) |11111111|11111111|11111111|0001 ffffff1 [28]
+ ( 21) |11111111|11111111|11111111|0010 ffffff2 [28]
+ ( 22) |11111111|11111111|11111111|111110 3ffffffe [30]
+ ( 23) |11111111|11111111|11111111|0011 ffffff3 [28]
+ ( 24) |11111111|11111111|11111111|0100 ffffff4 [28]
+ ( 25) |11111111|11111111|11111111|0101 ffffff5 [28]
+ ( 26) |11111111|11111111|11111111|0110 ffffff6 [28]
+ ( 27) |11111111|11111111|11111111|0111 ffffff7 [28]
+ ( 28) |11111111|11111111|11111111|1000 ffffff8 [28]
+ ( 29) |11111111|11111111|11111111|1001 ffffff9 [28]
+ ( 30) |11111111|11111111|11111111|1010 ffffffa [28]
+ ( 31) |11111111|11111111|11111111|1011 ffffffb [28]
+' ' ( 32) |010100 14 [ 6]
+'!' ( 33) |11111110|00 3f8 [10]
+'"' ( 34) |11111110|01 3f9 [10]
+'#' ( 35) |11111111|1010 ffa [12]
+'$' ( 36) |11111111|11001 1ff9 [13]
+'%' ( 37) |010101 15 [ 6]
+'&' ( 38) |11111000 f8 [ 8]
+''' ( 39) |11111111|010 7fa [11]
+'(' ( 40) |11111110|10 3fa [10]
+')' ( 41) |11111110|11 3fb [10]
+'*' ( 42) |11111001 f9 [ 8]
+'+' ( 43) |11111111|011 7fb [11]
+',' ( 44) |11111010 fa [ 8]
+'-' ( 45) |010110 16 [ 6]
+'.' ( 46) |010111 17 [ 6]
+'/' ( 47) |011000 18 [ 6]
+'0' ( 48) |00000 0 [ 5]
+'1' ( 49) |00001 1 [ 5]
+'2' ( 50) |00010 2 [ 5]
+'3' ( 51) |011001 19 [ 6]
+'4' ( 52) |011010 1a [ 6]
+'5' ( 53) |011011 1b [ 6]
+'6' ( 54) |011100 1c [ 6]
+'7' ( 55) |011101 1d [ 6]
+'8' ( 56) |011110 1e [ 6]
+'9' ( 57) |011111 1f [ 6]
+':' ( 58) |1011100 5c [ 7]
+';' ( 59) |11111011 fb [ 8]
+'<' ( 60) |11111111|1111100 7ffc [15]
+'=' ( 61) |100000 20 [ 6]
+'>' ( 62) |11111111|1011 ffb [12]
+'?' ( 63) |11111111|00 3fc [10]
+'@' ( 64) |11111111|11010 1ffa [13]
+'A' ( 65) |100001 21 [ 6]
+'B' ( 66) |1011101 5d [ 7]
+'C' ( 67) |1011110 5e [ 7]
+'D' ( 68) |1011111 5f [ 7]
+'E' ( 69) |1100000 60 [ 7]
+'F' ( 70) |1100001 61 [ 7]
+'G' ( 71) |1100010 62 [ 7]
+'H' ( 72) |1100011 63 [ 7]
+'I' ( 73) |1100100 64 [ 7]
+'J' ( 74) |1100101 65 [ 7]
+'K' ( 75) |1100110 66 [ 7]
+'L' ( 76) |1100111 67 [ 7]
+'M' ( 77) |1101000 68 [ 7]
+'N' ( 78) |1101001 69 [ 7]
+'O' ( 79) |1101010 6a [ 7]
+'P' ( 80) |1101011 6b [ 7]
+'Q' ( 81) |1101100 6c [ 7]
+'R' ( 82) |1101101 6d [ 7]
+'S' ( 83) |1101110 6e [ 7]
+'T' ( 84) |1101111 6f [ 7]
+'U' ( 85) |1110000 70 [ 7]
+'V' ( 86) |1110001 71 [ 7]
+'W' ( 87) |1110010 72 [ 7]
+'X' ( 88) |11111100 fc [ 8]
+'Y' ( 89) |1110011 73 [ 7]
+'Z' ( 90) |11111101 fd [ 8]
+'[' ( 91) |11111111|11011 1ffb [13]
+'\' ( 92) |11111111|11111110|000 7fff0 [19]
+']' ( 93) |11111111|11100 1ffc [13]
+'^' ( 94) |11111111|111100 3ffc [14]
+'_' ( 95) |100010 22 [ 6]
+'`' ( 96) |11111111|1111101 7ffd [15]
+'a' ( 97) |00011 3 [ 5]
+'b' ( 98) |100011 23 [ 6]
+'c' ( 99) |00100 4 [ 5]
+'d' (100) |100100 24 [ 6]
+'e' (101) |00101 5 [ 5]
+'f' (102) |100101 25 [ 6]
+'g' (103) |100110 26 [ 6]
+'h' (104) |100111 27 [ 6]
+'i' (105) |00110 6 [ 5]
+'j' (106) |1110100 74 [ 7]
+'k' (107) |1110101 75 [ 7]
+'l' (108) |101000 28 [ 6]
+'m' (109) |101001 29 [ 6]
+'n' (110) |101010 2a [ 6]
+'o' (111) |00111 7 [ 5]
+'p' (112) |101011 2b [ 6]
+'q' (113) |1110110 76 [ 7]
+'r' (114) |101100 2c [ 6]
+'s' (115) |01000 8 [ 5]
+'t' (116) |01001 9 [ 5]
+'u' (117) |101101 2d [ 6]
+'v' (118) |1110111 77 [ 7]
+'w' (119) |1111000 78 [ 7]
+'x' (120) |1111001 79 [ 7]
+'y' (121) |1111010 7a [ 7]
+'z' (122) |1111011 7b [ 7]
+'{' (123) |11111111|1111110 7ffe [15]
+'|' (124) |11111111|100 7fc [11]
+'}' (125) |11111111|111101 3ffd [14]
+'~' (126) |11111111|11101 1ffd [13]
+ (127) |11111111|11111111|11111111|1100 ffffffc [28]
+ (128) |11111111|11111110|0110 fffe6 [20]
+ (129) |11111111|11111111|010010 3fffd2 [22]
+ (130) |11111111|11111110|0111 fffe7 [20]
+ (131) |11111111|11111110|1000 fffe8 [20]
+ (132) |11111111|11111111|010011 3fffd3 [22]
+ (133) |11111111|11111111|010100 3fffd4 [22]
+ (134) |11111111|11111111|010101 3fffd5 [22]
+ (135) |11111111|11111111|1011001 7fffd9 [23]
+ (136) |11111111|11111111|010110 3fffd6 [22]
+ (137) |11111111|11111111|1011010 7fffda [23]
+ (138) |11111111|11111111|1011011 7fffdb [23]
+ (139) |11111111|11111111|1011100 7fffdc [23]
+ (140) |11111111|11111111|1011101 7fffdd [23]
+ (141) |11111111|11111111|1011110 7fffde [23]
+ (142) |11111111|11111111|11101011 ffffeb [24]
+ (143) |11111111|11111111|1011111 7fffdf [23]
+ (144) |11111111|11111111|11101100 ffffec [24]
+ (145) |11111111|11111111|11101101 ffffed [24]
+ (146) |11111111|11111111|010111 3fffd7 [22]
+ (147) |11111111|11111111|1100000 7fffe0 [23]
+ (148) |11111111|11111111|11101110 ffffee [24]
+ (149) |11111111|11111111|1100001 7fffe1 [23]
+ (150) |11111111|11111111|1100010 7fffe2 [23]
+ (151) |11111111|11111111|1100011 7fffe3 [23]
+ (152) |11111111|11111111|1100100 7fffe4 [23]
+ (153) |11111111|11111110|11100 1fffdc [21]
+ (154) |11111111|11111111|011000 3fffd8 [22]
+ (155) |11111111|11111111|1100101 7fffe5 [23]
+ (156) |11111111|11111111|011001 3fffd9 [22]
+ (157) |11111111|11111111|1100110 7fffe6 [23]
+ (158) |11111111|11111111|1100111 7fffe7 [23]
+ (159) |11111111|11111111|11101111 ffffef [24]
+ (160) |11111111|11111111|011010 3fffda [22]
+ (161) |11111111|11111110|11101 1fffdd [21]
+ (162) |11111111|11111110|1001 fffe9 [20]
+ (163) |11111111|11111111|011011 3fffdb [22]
+ (164) |11111111|11111111|011100 3fffdc [22]
+ (165) |11111111|11111111|1101000 7fffe8 [23]
+ (166) |11111111|11111111|1101001 7fffe9 [23]
+ (167) |11111111|11111110|11110 1fffde [21]
+ (168) |11111111|11111111|1101010 7fffea [23]
+ (169) |11111111|11111111|011101 3fffdd [22]
+ (170) |11111111|11111111|011110 3fffde [22]
+ (171) |11111111|11111111|11110000 fffff0 [24]
+ (172) |11111111|11111110|11111 1fffdf [21]
+ (173) |11111111|11111111|011111 3fffdf [22]
+ (174) |11111111|11111111|1101011 7fffeb [23]
+ (175) |11111111|11111111|1101100 7fffec [23]
+ (176) |11111111|11111111|00000 1fffe0 [21]
+ (177) |11111111|11111111|00001 1fffe1 [21]
+ (178) |11111111|11111111|100000 3fffe0 [22]
+ (179) |11111111|11111111|00010 1fffe2 [21]
+ (180) |11111111|11111111|1101101 7fffed [23]
+ (181) |11111111|11111111|100001 3fffe1 [22]
+ (182) |11111111|11111111|1101110 7fffee [23]
+ (183) |11111111|11111111|1101111 7fffef [23]
+ (184) |11111111|11111110|1010 fffea [20]
+ (185) |11111111|11111111|100010 3fffe2 [22]
+ (186) |11111111|11111111|100011 3fffe3 [22]
+ (187) |11111111|11111111|100100 3fffe4 [22]
+ (188) |11111111|11111111|1110000 7ffff0 [23]
+ (189) |11111111|11111111|100101 3fffe5 [22]
+ (190) |11111111|11111111|100110 3fffe6 [22]
+ (191) |11111111|11111111|1110001 7ffff1 [23]
+ (192) |11111111|11111111|11111000|00 3ffffe0 [26]
+ (193) |11111111|11111111|11111000|01 3ffffe1 [26]
+ (194) |11111111|11111110|1011 fffeb [20]
+ (195) |11111111|11111110|001 7fff1 [19]
+ (196) |11111111|11111111|100111 3fffe7 [22]
+ (197) |11111111|11111111|1110010 7ffff2 [23]
+ (198) |11111111|11111111|101000 3fffe8 [22]
+ (199) |11111111|11111111|11110110|0 1ffffec [25]
+ (200) |11111111|11111111|11111000|10 3ffffe2 [26]
+ (201) |11111111|11111111|11111000|11 3ffffe3 [26]
+ (202) |11111111|11111111|11111001|00 3ffffe4 [26]
+ (203) |11111111|11111111|11111011|110 7ffffde [27]
+ (204) |11111111|11111111|11111011|111 7ffffdf [27]
+ (205) |11111111|11111111|11111001|01 3ffffe5 [26]
+ (206) |11111111|11111111|11110001 fffff1 [24]
+ (207) |11111111|11111111|11110110|1 1ffffed [25]
+ (208) |11111111|11111110|010 7fff2 [19]
+ (209) |11111111|11111111|00011 1fffe3 [21]
+ (210) |11111111|11111111|11111001|10 3ffffe6 [26]
+ (211) |11111111|11111111|11111100|000 7ffffe0 [27]
+ (212) |11111111|11111111|11111100|001 7ffffe1 [27]
+ (213) |11111111|11111111|11111001|11 3ffffe7 [26]
+ (214) |11111111|11111111|11111100|010 7ffffe2 [27]
+ (215) |11111111|11111111|11110010 fffff2 [24]
+ (216) |11111111|11111111|00100 1fffe4 [21]
+ (217) |11111111|11111111|00101 1fffe5 [21]
+ (218) |11111111|11111111|11111010|00 3ffffe8 [26]
+ (219) |11111111|11111111|11111010|01 3ffffe9 [26]
+ (220) |11111111|11111111|11111111|1101 ffffffd [28]
+ (221) |11111111|11111111|11111100|011 7ffffe3 [27]
+ (222) |11111111|11111111|11111100|100 7ffffe4 [27]
+ (223) |11111111|11111111|11111100|101 7ffffe5 [27]
+ (224) |11111111|11111110|1100 fffec [20]
+ (225) |11111111|11111111|11110011 fffff3 [24]
+ (226) |11111111|11111110|1101 fffed [20]
+ (227) |11111111|11111111|00110 1fffe6 [21]
+ (228) |11111111|11111111|101001 3fffe9 [22]
+ (229) |11111111|11111111|00111 1fffe7 [21]
+ (230) |11111111|11111111|01000 1fffe8 [21]
+ (231) |11111111|11111111|1110011 7ffff3 [23]
+ (232) |11111111|11111111|101010 3fffea [22]
+ (233) |11111111|11111111|101011 3fffeb [22]
+ (234) |11111111|11111111|11110111|0 1ffffee [25]
+ (235) |11111111|11111111|11110111|1 1ffffef [25]
+ (236) |11111111|11111111|11110100 fffff4 [24]
+ (237) |11111111|11111111|11110101 fffff5 [24]
+ (238) |11111111|11111111|11111010|10 3ffffea [26]
+ (239) |11111111|11111111|1110100 7ffff4 [23]
+ (240) |11111111|11111111|11111010|11 3ffffeb [26]
+ (241) |11111111|11111111|11111100|110 7ffffe6 [27]
+ (242) |11111111|11111111|11111011|00 3ffffec [26]
+ (243) |11111111|11111111|11111011|01 3ffffed [26]
+ (244) |11111111|11111111|11111100|111 7ffffe7 [27]
+ (245) |11111111|11111111|11111101|000 7ffffe8 [27]
+ (246) |11111111|11111111|11111101|001 7ffffe9 [27]
+ (247) |11111111|11111111|11111101|010 7ffffea [27]
+ (248) |11111111|11111111|11111101|011 7ffffeb [27]
+ (249) |11111111|11111111|11111111|1110 ffffffe [28]
+ (250) |11111111|11111111|11111101|100 7ffffec [27]
+ (251) |11111111|11111111|11111101|101 7ffffed [27]
+ (252) |11111111|11111111|11111101|110 7ffffee [27]
+ (253) |11111111|11111111|11111101|111 7ffffef [27]
+ (254) |11111111|11111111|11111110|000 7fffff0 [27]
+ (255) |11111111|11111111|11111011|10 3ffffee [26]
+EOS (256) |11111111|11111111|11111111|111111 3fffffff [30]
+"""
+
+class Node:
+
+ def __init__(self, term = None):
+ self.term = term
+ self.left = None
+ self.right = None
+ self.trans = []
+ self.id = None
+ self.accept = False
+
+class Context:
+
+ def __init__(self):
+ self.next_id_ = 0
+ self.root = Node()
+
+ def next_id(self):
+ id = self.next_id_
+ self.next_id_ += 1
+ return id
+
+def _add(node, sym, bits):
+ if len(bits) == 0:
+ node.term = sym
+ return
+ else:
+ if bits[0] == '0':
+ if node.left is None:
+ node.left = Node()
+ child = node.left
+ else:
+ if node.right is None:
+ node.right = Node()
+ child = node.right
+ _add(child, sym, bits[1:])
+
+def huffman_tree_add(ctx, sym, bits):
+ _add(ctx.root, sym, bits)
+
+def _set_node_id(ctx, node, prefix):
+ if node.term is not None:
+ return
+ if len(prefix) <= 7 and [1] * len(prefix) == prefix:
+ node.accept = True
+ node.id = ctx.next_id()
+ _set_node_id(ctx, node.left, prefix + [0])
+ _set_node_id(ctx, node.right, prefix + [1])
+
+def huffman_tree_set_node_id(ctx):
+ _set_node_id(ctx, ctx.root, [])
+
+def _traverse(node, sym, start_node, root, left):
+ if left == 0:
+ if sym == 256:
+ sym = None
+ node = None
+ start_node.trans.append((node, sym))
+ return
+
+ if node.term is not None:
+ node = root
+
+ def go(node):
+ if node.term is not None:
+ assert sym is None
+ nsym = node.term
+ else:
+ nsym = sym
+
+ _traverse(node, nsym, start_node, root, left - 1)
+
+ go(node.left)
+ go(node.right)
+
+def _build_transition_table(ctx, node):
+ if node is None:
+ return
+ _traverse(node, None, node, ctx.root, 4)
+ _build_transition_table(ctx, node.left)
+ _build_transition_table(ctx, node.right)
+
+def huffman_tree_build_transition_table(ctx):
+ _build_transition_table(ctx, ctx.root)
+
+NGHTTP3_QPACK_HUFFMAN_ACCEPTED = 1 << 14
+NGHTTP3_QPACK_HUFFMAN_SYM = 1 << 15
+
+def _print_transition_table(node):
+ if node.term is not None:
+ return
+ print('/* {} */'.format(node.id))
+ print('{')
+ for nd, sym in node.trans:
+ flags = 0
+ if sym is None:
+ out = 0
+ else:
+ out = sym
+ flags |= NGHTTP3_QPACK_HUFFMAN_SYM
+ if nd is None:
+ id = 256
+ else:
+ id = nd.id
+ if id is None:
+ # if nd.id is None, it is a leaf node
+ id = 0
+ flags |= NGHTTP3_QPACK_HUFFMAN_ACCEPTED
+ elif nd.accept:
+ flags |= NGHTTP3_QPACK_HUFFMAN_ACCEPTED
+ print(' {{0x{:02x}, {}}},'.format(id | flags, out))
+ print('},')
+ _print_transition_table(node.left)
+ _print_transition_table(node.right)
+
+def huffman_tree_print_transition_table(ctx):
+ _print_transition_table(ctx.root)
+ print('/* 256 */')
+ print('{')
+ print(' {0x100, 0},')
+ print(' {0x100, 0},')
+ print(' {0x100, 0},')
+ print(' {0x100, 0},')
+ print(' {0x100, 0},')
+ print(' {0x100, 0},')
+ print(' {0x100, 0},')
+ print(' {0x100, 0},')
+ print(' {0x100, 0},')
+ print(' {0x100, 0},')
+ print(' {0x100, 0},')
+ print(' {0x100, 0},')
+ print(' {0x100, 0},')
+ print(' {0x100, 0},')
+ print(' {0x100, 0},')
+ print(' {0x100, 0},')
+ print('},')
+
+if __name__ == '__main__':
+ ctx = Context()
+ symbol_tbl = [(None, 0) for i in range(257)]
+
+ for line in io.StringIO(HUFFMAN_CODE_TABLE):
+ m = re.match(
+ r'.*\(\s*(\d+)\)\s+([|01]+)\s+(\S+)\s+\[\s*(\d+)\].*', line)
+ if m:
+ sym = int(m.group(1))
+ bits = re.sub(r'\|', '', m.group(2))
+ code = m.group(3)
+ nbits = int(m.group(4))
+ if len(code) > 8:
+ raise Error('Code is more than 4 bytes long')
+ assert(len(bits) == nbits)
+ symbol_tbl[sym] = (nbits, code)
+ huffman_tree_add(ctx, sym, bits)
+
+ huffman_tree_set_node_id(ctx)
+ huffman_tree_build_transition_table(ctx)
+
+ print('''\
+typedef struct {
+ uint32_t nbits;
+ uint32_t code;
+} nghttp3_qpack_huffman_sym;
+''')
+
+ print('''\
+const nghttp3_qpack_huffman_sym huffman_sym_table[] = {''')
+ for i in range(257):
+ nbits = symbol_tbl[i][0]
+ k = int(symbol_tbl[i][1], 16)
+ k = k << (32 - nbits)
+ print('''\
+ {{ {}, 0x{}u }}{}\
+'''.format(symbol_tbl[i][0], hex(k)[2:], ',' if i < 256 else ''))
+ print('};')
+ print('')
+
+ print('''\
+enum {{
+ NGHTTP3_QPACK_HUFFMAN_ACCEPTED = {},
+ NGHTTP3_QPACK_HUFFMAN_SYM = {},
+}} nghttp3_qpack_huffman_decode_flag;
+'''.format(NGHTTP3_QPACK_HUFFMAN_ACCEPTED, NGHTTP3_QPACK_HUFFMAN_SYM))
+
+ print('''\
+typedef struct {
+ uint16_t fstate;
+ uint8_t sym;
+} nghttp3_qpack_huffman_decode_node;
+''')
+
+ print('''\
+const nghttp3_qpack_huffman_decode_node qpack_huffman_decode_table[][16] = {''')
+ huffman_tree_print_transition_table(ctx)
+ print('};')
diff --git a/mkstatichdtbl.py b/mkstatichdtbl.py
new file mode 100755
index 0000000..feacd2a
--- /dev/null
+++ b/mkstatichdtbl.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# This scripts reads static table entries [1] and generates
+# token_stable and stable. This table is used in lib/nghttp3_qpack.c.
+#
+# [1] https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#name-static-table-2
+
+import re, sys
+
+def hd_map_hash(name):
+ h = 2166136261
+
+ # FNV hash variant: http://isthe.com/chongo/tech/comp/fnv/
+ for c in name:
+ h ^= ord(c)
+ h *= 16777619
+ h &= 0xffffffff
+
+ return h
+
+class Header:
+ def __init__(self, idx, name, value):
+ self.idx = idx
+ self.name = name
+ self.value = value
+ self.token = -1
+
+entries = []
+for line in sys.stdin:
+ m = re.match(r'(\d+)\s+(\S+)\s+(\S.*)?', line)
+ val = m.group(3).strip() if m.group(3) else ''
+ entries.append(Header(int(m.group(1)), m.group(2), val))
+
+token_entries = sorted(entries, key=lambda ent: ent.name)
+
+token = 0
+seq = 0
+name = token_entries[0].name
+for i, ent in enumerate(token_entries):
+ if name != ent.name:
+ name = ent.name
+ token = seq
+ seq += 1
+ ent.token = token
+
+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 gen_enum(entries):
+ used = {}
+ print('typedef enum nghttp3_qpack_token {')
+ for ent in entries:
+ if ent.name in used:
+ continue
+ used[ent.name] = True
+ enumname = to_enum_hd(ent.name)
+ print('''\
+ /**
+ * :enum:`{enumname}` is a token for ``{name}``.
+ */'''.format(enumname=enumname, name=ent.name))
+ if ent.token is None:
+ print(' {},'.format(enumname))
+ else:
+ print(' {} = {},'.format(enumname, ent.token))
+ print('} nghttp3_qpack_token;')
+
+gen_enum(entries)
+
+print()
+
+print('static nghttp3_qpack_static_entry token_stable[] = {')
+for i, ent in enumerate(token_entries):
+ print('MAKE_STATIC_ENT({}, {}, {}u),'\
+ .format(ent.idx, to_enum_hd(ent.name), hd_map_hash(ent.name)))
+print('};')
+
+print()
+
+print('static nghttp3_qpack_static_header stable[] = {')
+for ent in entries:
+ print('MAKE_STATIC_HD("{}", "{}", {}),'\
+ .format(ent.name, ent.value, to_enum_hd(ent.name)))
+print('};')
diff --git a/qifs-check.sh b/qifs-check.sh
new file mode 100755
index 0000000..c343a29
--- /dev/null
+++ b/qifs-check.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+set -e
+
+for f in qifs/encoded/qpack-06/*/*; do
+ echo $f
+
+ name=`basename "$f"`
+ IFS='.' read -ra params <<< "$name"
+ [ "${params[1]}" = "out" ] || continue
+ prefix=${params[0]}
+ maxtablesize=${params[2]}
+ maxblocked=${params[3]}
+ immediateack=${params[4]}
+
+ opts="-s$maxtablesize -m$maxblocked"
+ if [ "$immediateack" = "1" ]; then
+ opts="$opts -a"
+ fi
+
+ examples/qpack decode "$f" qpack-check.out $opts
+ qifs/bin/sort-qif.pl --strip-comments qpack-check.out > qpack-check-canonical.out
+ diff -u qpack-check-canonical.out "qifs/qifs/$prefix.qif"
+done
diff --git a/qifs.sh b/qifs.sh
new file mode 100755
index 0000000..53afc8f
--- /dev/null
+++ b/qifs.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+set -e
+
+destdir=qifs/encoded/qpack-05/nghttp3
+mkdir -p "$destdir"
+
+for f in qifs/qifs/*.qif; do
+ prefix=`basename ${f%.qif}`
+
+ for maxtablesize in 0 256 512 4096; do
+ for maxblocked in 0 100; do
+ echo $f $maxtablesize $maxblocked 0
+ outprefix=$prefix.out.$maxtablesize.$maxblocked
+ examples/qpack encode "$f" "$destdir/$outprefix.0" -s$maxtablesize -m$maxblocked
+ echo $f $maxtablesize $maxblocked 1
+ examples/qpack encode "$f" "$destdir/$outprefix.1" -s$maxtablesize -m$maxblocked -a
+ done
+ done
+done
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 0000000..3e57a18
--- /dev/null
+++ b/tests/.gitignore
@@ -0,0 +1 @@
+/main \ No newline at end of file
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..003706b
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,57 @@
+# 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.
+
+if(HAVE_CUNIT)
+ include_directories(
+ "${CMAKE_SOURCE_DIR}/lib"
+ "${CMAKE_SOURCE_DIR}/lib/includes"
+ "${CMAKE_BINARY_DIR}/lib/includes"
+ ${CUNIT_INCLUDE_DIRS}
+ )
+
+ set(main_SOURCES
+ main.c
+ nghttp3_qpack_test.c
+ nghttp3_conn_test.c
+ nghttp3_tnode_test.c
+ nghttp3_http_test.c
+ nghttp3_conv_test.c
+ nghttp3_test_helper.c
+ )
+
+ add_executable(main EXCLUDE_FROM_ALL
+ ${main_SOURCES}
+ )
+ target_include_directories(main PRIVATE ${CUNIT_INCLUDE_DIRS})
+ # FIXME enable and fix warnings
+ #set_target_properties(main PROPERTIES COMPILE_FLAGS "${WARNCFLAGS}")
+ target_link_libraries(main
+ nghttp3_static
+ ${CUNIT_LIBRARIES}
+ m
+ )
+ add_test(main main)
+ add_dependencies(check main)
+endif()
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..aa3566c
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,65 @@
+# 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.
+EXTRA_DIST = CMakeLists.txt
+
+if HAVE_CUNIT
+
+check_PROGRAMS = main
+
+OBJECTS = \
+ main.c \
+ nghttp3_qpack_test.c \
+ nghttp3_conn_test.c \
+ nghttp3_tnode_test.c \
+ nghttp3_http_test.c \
+ nghttp3_conv_test.c \
+ nghttp3_test_helper.c
+HFILES = \
+ nghttp3_qpack_test.h \
+ nghttp3_conn_test.h \
+ nghttp3_tnode_test.h \
+ nghttp3_http_test.h \
+ nghttp3_conv_test.h \
+ nghttp3_test_helper.h
+
+main_SOURCES = $(HFILES) $(OBJECTS)
+
+# With static lib disabled and symbol hiding enabled, we have to link object
+# files directly because the tests use symbols not included in public API.
+main_LDADD = ${top_builddir}/lib/.libs/*.o
+main_LDADD += @CUNIT_LIBS@ -lm
+main_LDFLAGS = -static
+
+AM_CFLAGS = $(WARNCFLAGS) \
+ -I${top_srcdir}/lib \
+ -I${top_srcdir}/lib/includes \
+ -I${top_builddir}/lib/includes \
+ -DBUILDING_NGHTTP3 \
+ @CUNIT_CFLAGS@ @DEFS@
+AM_LDFLAGS = -no-install
+
+TESTS = main
+
+endif # HAVE_CUNIT
diff --git a/tests/main.c b/tests/main.c
new file mode 100644
index 0000000..201c1bc
--- /dev/null
+++ b/tests/main.c
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <string.h>
+#include <CUnit/Basic.h>
+/* include test cases' include files here */
+#include "nghttp3_qpack_test.h"
+#include "nghttp3_conn_test.h"
+#include "nghttp3_tnode_test.h"
+#include "nghttp3_http_test.h"
+#include "nghttp3_conv_test.h"
+
+static int init_suite1(void) { return 0; }
+
+static int clean_suite1(void) { return 0; }
+
+int main(void) {
+ CU_pSuite pSuite = NULL;
+ unsigned int num_tests_failed;
+
+ /* initialize the CUnit test registry */
+ if (CUE_SUCCESS != CU_initialize_registry())
+ return (int)CU_get_error();
+
+ /* add a suite to the registry */
+ pSuite = CU_add_suite("libnghttp3_TestSuite", init_suite1, clean_suite1);
+ if (NULL == pSuite) {
+ CU_cleanup_registry();
+ return (int)CU_get_error();
+ }
+
+ /* add the tests to the suite */
+ if (!CU_add_test(pSuite, "qpack_encoder_encode",
+ test_nghttp3_qpack_encoder_encode) ||
+ !CU_add_test(pSuite, "qpack_encoder_still_blocked",
+ test_nghttp3_qpack_encoder_still_blocked) ||
+ !CU_add_test(pSuite, "qpack_encoder_set_dtable_cap",
+ test_nghttp3_qpack_encoder_set_dtable_cap) ||
+ !CU_add_test(pSuite, "qpack_decoder_feedback",
+ test_nghttp3_qpack_decoder_feedback) ||
+ !CU_add_test(pSuite, "qpack_decoder_stream_overflow",
+ test_nghttp3_qpack_decoder_stream_overflow) ||
+ !CU_add_test(pSuite, "qpack_huffman", test_nghttp3_qpack_huffman) ||
+ !CU_add_test(pSuite, "qpack_huffman_decode_failure_state",
+ test_nghttp3_qpack_huffman_decode_failure_state) ||
+ !CU_add_test(pSuite, "qpack_decoder_reconstruct_ricnt",
+ test_nghttp3_qpack_decoder_reconstruct_ricnt) ||
+ !CU_add_test(pSuite, "conn_read_control",
+ test_nghttp3_conn_read_control) ||
+ !CU_add_test(pSuite, "conn_write_control",
+ test_nghttp3_conn_write_control) ||
+ !CU_add_test(pSuite, "conn_submit_request",
+ test_nghttp3_conn_submit_request) ||
+ !CU_add_test(pSuite, "conn_http_request",
+ test_nghttp3_conn_http_request) ||
+ !CU_add_test(pSuite, "conn_http_resp_header",
+ test_nghttp3_conn_http_resp_header) ||
+ !CU_add_test(pSuite, "conn_http_req_header",
+ test_nghttp3_conn_http_req_header) ||
+ !CU_add_test(pSuite, "conn_http_content_length",
+ test_nghttp3_conn_http_content_length) ||
+ !CU_add_test(pSuite, "conn_http_content_length_mismatch",
+ test_nghttp3_conn_http_content_length_mismatch) ||
+ !CU_add_test(pSuite, "conn_http_non_final_response",
+ test_nghttp3_conn_http_non_final_response) ||
+ !CU_add_test(pSuite, "conn_http_trailers",
+ test_nghttp3_conn_http_trailers) ||
+ !CU_add_test(pSuite, "conn_http_ignore_content_length",
+ test_nghttp3_conn_http_ignore_content_length) ||
+ !CU_add_test(pSuite, "conn_http_record_request_method",
+ test_nghttp3_conn_http_record_request_method) ||
+ !CU_add_test(pSuite, "conn_http_error", test_nghttp3_conn_http_error) ||
+ !CU_add_test(pSuite, "conn_qpack_blocked_stream",
+ test_nghttp3_conn_qpack_blocked_stream) ||
+ !CU_add_test(pSuite, "conn_submit_response_read_blocked",
+ test_nghttp3_conn_submit_response_read_blocked) ||
+ !CU_add_test(pSuite, "conn_just_fin", test_nghttp3_conn_just_fin) ||
+ !CU_add_test(pSuite, "conn_recv_uni", test_nghttp3_conn_recv_uni) ||
+ !CU_add_test(pSuite, "conn_recv_goaway", test_nghttp3_conn_recv_goaway) ||
+ !CU_add_test(pSuite, "conn_shutdown_server",
+ test_nghttp3_conn_shutdown_server) ||
+ !CU_add_test(pSuite, "conn_shutdown_client",
+ test_nghttp3_conn_shutdown_client) ||
+ !CU_add_test(pSuite, "conn_priority_update",
+ test_nghttp3_conn_priority_update) ||
+ !CU_add_test(pSuite, "conn_request_priority",
+ test_nghttp3_conn_request_priority) ||
+ !CU_add_test(pSuite, "conn_set_stream_priority",
+ test_nghttp3_conn_set_stream_priority) ||
+ !CU_add_test(pSuite, "conn_shutdown_stream_read",
+ test_nghttp3_conn_shutdown_stream_read) ||
+ !CU_add_test(pSuite, "conn_stream_data_overflow",
+ test_nghttp3_conn_stream_data_overflow) ||
+ !CU_add_test(pSuite, "tnode_schedule", test_nghttp3_tnode_schedule) ||
+ !CU_add_test(pSuite, "http_parse_priority",
+ test_nghttp3_http_parse_priority) ||
+ !CU_add_test(pSuite, "sf_parse_item", test_nghttp3_sf_parse_item) ||
+ !CU_add_test(pSuite, "sf_parse_inner_list",
+ test_nghttp3_sf_parse_inner_list) ||
+ !CU_add_test(pSuite, "check_header_value",
+ test_nghttp3_check_header_value) ||
+ !CU_add_test(pSuite, "pri_to_uint8", test_nghttp3_pri_to_uint8)) {
+ CU_cleanup_registry();
+ return (int)CU_get_error();
+ }
+
+ /* Run all tests using the CUnit Basic interface */
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ num_tests_failed = CU_get_number_of_tests_failed();
+ CU_cleanup_registry();
+ if (CU_get_error() == CUE_SUCCESS) {
+ return (int)num_tests_failed;
+ } else {
+ printf("CUnit Error: %s\n", CU_get_error_msg());
+ return (int)CU_get_error();
+ }
+}
diff --git a/tests/nghttp3_conn_test.c b/tests/nghttp3_conn_test.c
new file mode 100644
index 0000000..b60220a
--- /dev/null
+++ b/tests/nghttp3_conn_test.c
@@ -0,0 +1,3448 @@
+/*
+ * 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_test.h"
+
+#include <assert.h>
+
+#include <CUnit/CUnit.h>
+
+#include "nghttp3_conn.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_conv.h"
+#include "nghttp3_frame.h"
+#include "nghttp3_vec.h"
+#include "nghttp3_test_helper.h"
+#include "nghttp3_http.h"
+
+static uint8_t nulldata[4096];
+
+typedef struct {
+ struct {
+ size_t nblock;
+ size_t left;
+ size_t step;
+ } data;
+ struct {
+ uint64_t acc;
+ } ack;
+ struct {
+ size_t ncalled;
+ int64_t stream_id;
+ uint64_t app_error_code;
+ } stop_sending_cb;
+ struct {
+ size_t ncalled;
+ int64_t stream_id;
+ uint64_t app_error_code;
+ } reset_stream_cb;
+ struct {
+ size_t ncalled;
+ int64_t id;
+ } shutdown_cb;
+ struct {
+ uint64_t consumed_total;
+ } deferred_consume_cb;
+} userdata;
+
+static int acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t datalen, void *user_data,
+ void *stream_user_data) {
+ userdata *ud = user_data;
+
+ (void)conn;
+ (void)stream_id;
+ (void)stream_user_data;
+
+ ud->ack.acc += datalen;
+
+ return 0;
+}
+
+static int begin_headers(nghttp3_conn *conn, int64_t stream_id, void *user_data,
+ void *stream_user_data) {
+ (void)conn;
+ (void)stream_id;
+ (void)stream_user_data;
+ (void)user_data;
+ return 0;
+}
+
+static int recv_header(nghttp3_conn *conn, int64_t stream_id, int32_t token,
+ nghttp3_rcbuf *name, nghttp3_rcbuf *value, uint8_t flags,
+ void *user_data, void *stream_user_data) {
+ (void)conn;
+ (void)stream_id;
+ (void)token;
+ (void)name;
+ (void)value;
+ (void)flags;
+ (void)stream_user_data;
+ (void)user_data;
+ return 0;
+}
+
+static int end_headers(nghttp3_conn *conn, int64_t stream_id, int fin,
+ void *user_data, void *stream_user_data) {
+ (void)conn;
+ (void)stream_id;
+ (void)fin;
+ (void)stream_user_data;
+ (void)user_data;
+ return 0;
+}
+
+static nghttp3_ssize step_read_data(nghttp3_conn *conn, int64_t stream_id,
+ nghttp3_vec *vec, size_t veccnt,
+ uint32_t *pflags, void *user_data,
+ void *stream_user_data) {
+ userdata *ud = user_data;
+ size_t n = nghttp3_min(ud->data.left, ud->data.step);
+
+ (void)conn;
+ (void)stream_id;
+ (void)veccnt;
+ (void)stream_user_data;
+
+ ud->data.left -= n;
+ if (ud->data.left == 0) {
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+
+ if (n == 0) {
+ return 0;
+ }
+ }
+
+ vec[0].base = nulldata;
+ vec[0].len = n;
+
+ return 1;
+}
+
+static nghttp3_ssize
+block_then_step_read_data(nghttp3_conn *conn, int64_t stream_id,
+ nghttp3_vec *vec, size_t veccnt, uint32_t *pflags,
+ void *user_data, void *stream_user_data) {
+ userdata *ud = user_data;
+
+ if (ud->data.nblock == 0) {
+ return step_read_data(conn, stream_id, vec, veccnt, pflags, user_data,
+ stream_user_data);
+ }
+
+ --ud->data.nblock;
+
+ return NGHTTP3_ERR_WOULDBLOCK;
+}
+
+static nghttp3_ssize
+step_then_block_read_data(nghttp3_conn *conn, int64_t stream_id,
+ nghttp3_vec *vec, size_t veccnt, uint32_t *pflags,
+ void *user_data, void *stream_user_data) {
+ nghttp3_ssize rv;
+
+ rv = step_read_data(conn, stream_id, vec, veccnt, pflags, user_data,
+ stream_user_data);
+
+ assert(rv >= 0);
+
+ if (*pflags & NGHTTP3_DATA_FLAG_EOF) {
+ *pflags &= (uint32_t)~NGHTTP3_DATA_FLAG_EOF;
+
+ if (nghttp3_vec_len(vec, (size_t)rv) == 0) {
+ return NGHTTP3_ERR_WOULDBLOCK;
+ }
+ }
+
+ return rv;
+}
+
+#if SIZE_MAX > UINT32_MAX
+static nghttp3_ssize stream_data_overflow_read_data(
+ nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec, size_t veccnt,
+ uint32_t *pflags, void *user_data, void *stream_user_data) {
+ (void)conn;
+ (void)stream_id;
+ (void)veccnt;
+ (void)pflags;
+ (void)user_data;
+ (void)stream_user_data;
+
+ vec[0].base = nulldata;
+ vec[0].len = NGHTTP3_MAX_VARINT + 1;
+
+ return 1;
+}
+
+static nghttp3_ssize stream_data_almost_overflow_read_data(
+ nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec, size_t veccnt,
+ uint32_t *pflags, void *user_data, void *stream_user_data) {
+ (void)conn;
+ (void)stream_id;
+ (void)veccnt;
+ (void)pflags;
+ (void)user_data;
+ (void)stream_user_data;
+
+ vec[0].base = nulldata;
+ vec[0].len = NGHTTP3_MAX_VARINT;
+
+ return 1;
+}
+#endif /* SIZE_MAX > UINT32_MAX */
+
+static int stop_sending(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data) {
+ userdata *ud = user_data;
+
+ (void)conn;
+ (void)stream_user_data;
+
+ ++ud->stop_sending_cb.ncalled;
+ ud->stop_sending_cb.stream_id = stream_id;
+ ud->stop_sending_cb.app_error_code = app_error_code;
+
+ return 0;
+}
+
+static int reset_stream(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data) {
+ userdata *ud = user_data;
+
+ (void)conn;
+ (void)stream_user_data;
+
+ ++ud->reset_stream_cb.ncalled;
+ ud->reset_stream_cb.stream_id = stream_id;
+ ud->reset_stream_cb.app_error_code = app_error_code;
+
+ return 0;
+}
+
+static int conn_shutdown(nghttp3_conn *conn, int64_t id, void *user_data) {
+ userdata *ud = user_data;
+
+ (void)conn;
+
+ ++ud->shutdown_cb.ncalled;
+ ud->shutdown_cb.id = id;
+
+ return 0;
+}
+
+static int deferred_consume(nghttp3_conn *conn, int64_t stream_id,
+ size_t consumed, void *user_data,
+ void *stream_user_data) {
+ userdata *ud = user_data;
+
+ (void)conn;
+ (void)stream_user_data;
+ (void)stream_id;
+
+ ud->deferred_consume_cb.consumed_total += consumed;
+
+ return 0;
+}
+
+void test_nghttp3_conn_read_control(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ int rv;
+ uint8_t rawbuf[2048];
+ nghttp3_buf buf;
+ struct {
+ nghttp3_frame_settings settings;
+ nghttp3_settings_entry iv[15];
+ } fr;
+ nghttp3_ssize nconsumed;
+ nghttp3_settings_entry *iv;
+ size_t i;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_CONTROL);
+
+ fr.settings.hd.type = NGHTTP3_FRAME_SETTINGS;
+ iv = fr.settings.iv;
+ iv[0].id = NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE;
+ iv[0].value = 65536;
+ iv[1].id = 1000000009;
+ iv[1].value = 1000000007;
+ iv[2].id = NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY;
+ iv[2].value = 4096;
+ iv[3].id = NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS;
+ iv[3].value = 99;
+ fr.settings.niv = 4;
+
+ nghttp3_write_frame(&buf, (nghttp3_frame *)&fr);
+
+ rv = nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ nconsumed = nghttp3_conn_read_stream(conn, 2, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(nconsumed == (nghttp3_ssize)nghttp3_buf_len(&buf));
+ CU_ASSERT(65536 == conn->remote.settings.max_field_section_size);
+ CU_ASSERT(4096 == conn->remote.settings.qpack_max_dtable_capacity);
+ CU_ASSERT(99 == conn->remote.settings.qpack_blocked_streams);
+ CU_ASSERT(4096 == conn->qenc.ctx.hard_max_dtable_capacity);
+ CU_ASSERT(4096 == conn->qenc.ctx.max_dtable_capacity);
+ CU_ASSERT(99 == conn->qenc.ctx.max_blocked_streams);
+
+ nghttp3_conn_del(conn);
+
+ /* Feed 1 byte at a time to verify that state machine works */
+ rv = nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ for (i = 0; i < nghttp3_buf_len(&buf); ++i) {
+ nconsumed =
+ nghttp3_conn_read_stream(conn, 2, buf.pos + i, 1, /* fin = */ 0);
+
+ CU_ASSERT(1 == nconsumed);
+ }
+
+ CU_ASSERT(65536 == conn->remote.settings.max_field_section_size);
+ CU_ASSERT(4096 == conn->remote.settings.qpack_max_dtable_capacity);
+ CU_ASSERT(99 == conn->remote.settings.qpack_blocked_streams);
+
+ nghttp3_conn_del(conn);
+
+ /* Receiver should enforce its own limits for QPACK parameters. */
+ nghttp3_settings_default(&settings);
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_CONTROL);
+
+ fr.settings.hd.type = NGHTTP3_FRAME_SETTINGS;
+ iv = fr.settings.iv;
+ iv[0].id = NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY;
+ iv[0].value = 4097;
+ iv[1].id = NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS;
+ iv[1].value = 101;
+ fr.settings.niv = 2;
+
+ nghttp3_write_frame(&buf, (nghttp3_frame *)&fr);
+
+ rv = nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ nconsumed = nghttp3_conn_read_stream(conn, 2, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(nconsumed == (nghttp3_ssize)nghttp3_buf_len(&buf));
+ CU_ASSERT(4097 == conn->remote.settings.qpack_max_dtable_capacity);
+ CU_ASSERT(101 == conn->remote.settings.qpack_blocked_streams);
+ CU_ASSERT(4096 == conn->qenc.ctx.hard_max_dtable_capacity);
+ CU_ASSERT(4096 == conn->qenc.ctx.max_dtable_capacity);
+ CU_ASSERT(100 == conn->qenc.ctx.max_blocked_streams);
+
+ nghttp3_conn_del(conn);
+
+ /* Receiving multiple nonzero SETTINGS_QPACK_MAX_TABLE_CAPACITY is
+ treated as error. */
+ nghttp3_settings_default(&settings);
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_CONTROL);
+
+ fr.settings.hd.type = NGHTTP3_FRAME_SETTINGS;
+ iv = fr.settings.iv;
+ iv[0].id = NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY;
+ iv[0].value = 4097;
+ iv[1].id = NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY;
+ iv[1].value = 4097;
+ fr.settings.niv = 2;
+
+ nghttp3_write_frame(&buf, (nghttp3_frame *)&fr);
+
+ rv = nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ nconsumed = nghttp3_conn_read_stream(conn, 2, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(NGHTTP3_ERR_H3_SETTINGS_ERROR == nconsumed);
+
+ nghttp3_conn_del(conn);
+
+ /* Receiving multiple nonzero SETTINGS_QPACK_BLOCKED_STREAMS is
+ treated as error. */
+ nghttp3_settings_default(&settings);
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_CONTROL);
+
+ fr.settings.hd.type = NGHTTP3_FRAME_SETTINGS;
+ iv = fr.settings.iv;
+ iv[0].id = NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS;
+ iv[0].value = 1;
+ iv[1].id = NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS;
+ iv[1].value = 1;
+ fr.settings.niv = 2;
+
+ nghttp3_write_frame(&buf, (nghttp3_frame *)&fr);
+
+ rv = nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ nconsumed = nghttp3_conn_read_stream(conn, 2, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(NGHTTP3_ERR_H3_SETTINGS_ERROR == nconsumed);
+
+ nghttp3_conn_del(conn);
+
+ /* Receive ENABLE_CONNECT_PROTOCOL = 1 */
+ nghttp3_settings_default(&settings);
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_CONTROL);
+
+ fr.settings.hd.type = NGHTTP3_FRAME_SETTINGS;
+ iv = fr.settings.iv;
+ iv[0].id = NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL;
+ iv[0].value = 1;
+ fr.settings.niv = 1;
+
+ nghttp3_write_frame(&buf, (nghttp3_frame *)&fr);
+
+ rv = nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ nconsumed = nghttp3_conn_read_stream(conn, 2, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == nconsumed);
+ CU_ASSERT(1 == conn->remote.settings.enable_connect_protocol);
+
+ nghttp3_conn_del(conn);
+
+ /* Receiving ENABLE_CONNECT_PROTOCOL = 0 after seeing
+ ENABLE_CONNECT_PROTOCOL = 1 */
+ nghttp3_settings_default(&settings);
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_CONTROL);
+
+ fr.settings.hd.type = NGHTTP3_FRAME_SETTINGS;
+ iv = fr.settings.iv;
+ iv[0].id = NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL;
+ iv[0].value = 1;
+ iv[1].id = NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL;
+ iv[1].value = 0;
+ fr.settings.niv = 2;
+
+ nghttp3_write_frame(&buf, (nghttp3_frame *)&fr);
+
+ rv = nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ nconsumed = nghttp3_conn_read_stream(conn, 2, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(NGHTTP3_ERR_H3_SETTINGS_ERROR == nconsumed);
+
+ nghttp3_conn_del(conn);
+}
+
+void test_nghttp3_conn_write_control(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ nghttp3_vec vec[256];
+ nghttp3_ssize sveccnt;
+ int rv;
+ int64_t stream_id;
+ int fin;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+
+ rv = nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ rv = nghttp3_conn_bind_control_stream(conn, 3);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != conn->tx.ctrl);
+ CU_ASSERT(NGHTTP3_STREAM_TYPE_CONTROL == conn->tx.ctrl->type);
+
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(3 == stream_id);
+ CU_ASSERT(1 == sveccnt);
+ CU_ASSERT(vec[0].len > 1);
+ CU_ASSERT(NGHTTP3_STREAM_TYPE_CONTROL == vec[0].base[0]);
+
+ nghttp3_conn_del(conn);
+}
+
+void test_nghttp3_conn_submit_request(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ nghttp3_vec vec[256];
+ nghttp3_ssize sveccnt;
+ int rv;
+ int64_t stream_id;
+ const nghttp3_nv nva[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":authority", "example.com"),
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":method", "GET"),
+ };
+ uint64_t len;
+ size_t i;
+ nghttp3_stream *stream;
+ userdata ud;
+ nghttp3_data_reader dr;
+ int fin;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ memset(&ud, 0, sizeof(ud));
+ nghttp3_settings_default(&settings);
+
+ callbacks.acked_stream_data = acked_stream_data;
+
+ ud.data.left = 2000;
+ ud.data.step = 1200;
+
+ rv = nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, &ud);
+
+ CU_ASSERT(0 == rv);
+
+ rv = nghttp3_conn_bind_qpack_streams(conn, 6, 10);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(NULL != conn->tx.qenc);
+ CU_ASSERT(NGHTTP3_STREAM_TYPE_QPACK_ENCODER == conn->tx.qenc->type);
+ CU_ASSERT(NULL != conn->tx.qdec);
+ CU_ASSERT(NGHTTP3_STREAM_TYPE_QPACK_DECODER == conn->tx.qdec->type);
+
+ dr.read_data = step_read_data;
+ rv = nghttp3_conn_submit_request(conn, 0, nva, nghttp3_arraylen(nva), &dr,
+ NULL);
+
+ CU_ASSERT(0 == rv);
+
+ /* This will write QPACK decoder stream; just stream type */
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(10 == stream_id);
+ CU_ASSERT(1 == sveccnt);
+ CU_ASSERT(1 == nghttp3_ringbuf_len(&conn->tx.qdec->outq));
+ CU_ASSERT(0 == conn->tx.qdec->outq_idx);
+ CU_ASSERT(0 == conn->tx.qdec->outq_offset);
+
+ /* Calling twice will return the same result */
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(10 == stream_id);
+ CU_ASSERT(1 == sveccnt);
+
+ rv = nghttp3_conn_add_write_offset(conn, 10, vec[0].len);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == nghttp3_ringbuf_len(&conn->tx.qdec->outq));
+ CU_ASSERT(1 == conn->tx.qdec->outq_idx);
+ CU_ASSERT(0 == conn->tx.qdec->outq_offset);
+
+ rv = nghttp3_conn_add_ack_offset(conn, 10, vec[0].len);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == nghttp3_ringbuf_len(&conn->tx.qdec->outq));
+ CU_ASSERT(0 == conn->tx.qdec->outq_idx);
+ CU_ASSERT(0 == conn->tx.qdec->outq_offset);
+ CU_ASSERT(0 == conn->tx.qdec->ack_offset);
+
+ /* This will write QPACK encoder stream; just stream type */
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(6 == stream_id);
+ CU_ASSERT(1 == sveccnt);
+
+ rv = nghttp3_conn_add_write_offset(conn, 6, vec[0].len);
+
+ CU_ASSERT(0 == rv);
+
+ rv = nghttp3_conn_add_ack_offset(conn, 6, vec[0].len);
+
+ CU_ASSERT(0 == rv);
+
+ /* This will write request stream */
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(0 == stream_id);
+ CU_ASSERT(2 == sveccnt);
+
+ len = nghttp3_vec_len(vec, (size_t)sveccnt);
+ for (i = 0; i < len; ++i) {
+ rv = nghttp3_conn_add_write_offset(conn, 0, 1);
+
+ CU_ASSERT(0 == rv);
+
+ rv = nghttp3_conn_add_ack_offset(conn, 0, 1);
+
+ CU_ASSERT(0 == rv);
+ }
+
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(0 == stream_id);
+ CU_ASSERT(2 == sveccnt);
+
+ len = nghttp3_vec_len(vec, (size_t)sveccnt);
+
+ for (i = 0; i < len; ++i) {
+ rv = nghttp3_conn_add_write_offset(conn, 0, 1);
+
+ CU_ASSERT(0 == rv);
+
+ rv = nghttp3_conn_add_ack_offset(conn, 0, 1);
+
+ CU_ASSERT(0 == rv);
+ }
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(0 == nghttp3_ringbuf_len(&stream->outq));
+ CU_ASSERT(0 == nghttp3_ringbuf_len(&stream->chunks));
+ CU_ASSERT(0 == stream->outq_idx);
+ CU_ASSERT(0 == stream->outq_offset);
+ CU_ASSERT(0 == stream->ack_offset);
+ CU_ASSERT(2000 == ud.ack.acc);
+
+ nghttp3_conn_del(conn);
+}
+
+void test_nghttp3_conn_http_request(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *cl, *sv;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ nghttp3_vec vec[256];
+ nghttp3_ssize sveccnt;
+ nghttp3_ssize sconsumed;
+ int rv;
+ int64_t stream_id;
+ const nghttp3_nv reqnva[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":authority", "example.com"),
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":method", "GET"),
+ };
+ const nghttp3_nv respnva[] = {
+ MAKE_NV(":status", "200"),
+ MAKE_NV("server", "nghttp3"),
+ MAKE_NV("content-length", "1999"),
+ };
+ nghttp3_data_reader dr;
+ int fin;
+ userdata clud, svud;
+ size_t i;
+ size_t nconsumed;
+ size_t nread;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+ memset(&clud, 0, sizeof(clud));
+
+ callbacks.begin_headers = begin_headers;
+ callbacks.recv_header = recv_header;
+ callbacks.end_headers = end_headers;
+
+ settings.qpack_max_dtable_capacity = 4096;
+ settings.qpack_blocked_streams = 100;
+
+ clud.data.left = 2000;
+ clud.data.step = 1200;
+
+ svud.data.left = 1999;
+ svud.data.step = 1000;
+
+ nghttp3_conn_client_new(&cl, &callbacks, &settings, mem, &clud);
+ nghttp3_conn_server_new(&sv, &callbacks, &settings, mem, &svud);
+
+ nghttp3_conn_bind_control_stream(cl, 2);
+ nghttp3_conn_bind_control_stream(sv, 3);
+
+ nghttp3_conn_bind_qpack_streams(cl, 6, 10);
+ nghttp3_conn_bind_qpack_streams(sv, 7, 11);
+
+ dr.read_data = step_read_data;
+ rv = nghttp3_conn_submit_request(cl, 0, reqnva, nghttp3_arraylen(reqnva), &dr,
+ NULL);
+
+ CU_ASSERT(0 == rv);
+
+ nread = 0;
+ nconsumed = 0;
+
+ for (;;) {
+ sveccnt = nghttp3_conn_writev_stream(cl, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(sveccnt >= 0);
+
+ if (sveccnt <= 0) {
+ break;
+ }
+
+ rv = nghttp3_conn_add_write_offset(
+ cl, stream_id, (size_t)nghttp3_vec_len(vec, (size_t)sveccnt));
+
+ CU_ASSERT(0 == rv);
+
+ for (i = 0; i < (size_t)sveccnt; ++i) {
+ sconsumed =
+ nghttp3_conn_read_stream(sv, stream_id, vec[i].base, vec[i].len,
+ fin && i == (size_t)sveccnt - 1);
+ CU_ASSERT(sconsumed >= 0);
+
+ nread += vec[i].len;
+ nconsumed += (size_t)sconsumed;
+ }
+
+ rv = nghttp3_conn_add_ack_offset(cl, stream_id,
+ nghttp3_vec_len(vec, (size_t)sveccnt));
+
+ CU_ASSERT(0 == rv);
+ }
+
+ CU_ASSERT(nread == nconsumed + 2000);
+
+ rv = nghttp3_conn_submit_response(sv, 0, respnva, nghttp3_arraylen(respnva),
+ &dr);
+
+ CU_ASSERT(0 == rv);
+
+ nread = 0;
+ nconsumed = 0;
+
+ for (;;) {
+ sveccnt = nghttp3_conn_writev_stream(sv, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(sveccnt >= 0);
+
+ if (sveccnt <= 0) {
+ break;
+ }
+
+ rv = nghttp3_conn_add_write_offset(
+ sv, stream_id, (size_t)nghttp3_vec_len(vec, (size_t)sveccnt));
+
+ CU_ASSERT(0 == rv);
+
+ for (i = 0; i < (size_t)sveccnt; ++i) {
+ sconsumed =
+ nghttp3_conn_read_stream(cl, stream_id, vec[i].base, vec[i].len,
+ fin && i == (size_t)sveccnt - 1);
+ CU_ASSERT(sconsumed >= 0);
+
+ nread += vec[i].len;
+ nconsumed += (size_t)sconsumed;
+ }
+
+ rv = nghttp3_conn_add_ack_offset(sv, stream_id,
+ nghttp3_vec_len(vec, (size_t)sveccnt));
+
+ CU_ASSERT(0 == rv);
+ }
+
+ CU_ASSERT(nread == nconsumed + 1999);
+
+ nghttp3_conn_del(sv);
+ nghttp3_conn_del(cl);
+}
+
+static void check_http_header(const nghttp3_nv *nva, size_t nvlen, int request,
+ int want_lib_error) {
+ uint8_t rawbuf[4096];
+ nghttp3_buf buf;
+ nghttp3_frame_headers fr;
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_ssize sconsumed;
+ nghttp3_qpack_encoder qenc;
+ nghttp3_stream *stream;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+ settings.enable_connect_protocol = 1;
+
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)nva;
+ fr.nvlen = nvlen;
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ if (request) {
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+ } else {
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ }
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ if (want_lib_error) {
+ if (want_lib_error == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) {
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(stream->flags & NGHTTP3_STREAM_FLAG_HTTP_ERROR);
+ } else {
+ CU_ASSERT(want_lib_error == sconsumed);
+ }
+ } else {
+ CU_ASSERT(sconsumed > 0);
+ }
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+}
+
+static void check_http_resp_header(const nghttp3_nv *nva, size_t nvlen,
+ int want_lib_error) {
+ check_http_header(nva, nvlen, /* request = */ 0, want_lib_error);
+}
+
+static void check_http_req_header(const nghttp3_nv *nva, size_t nvlen,
+ int want_lib_error) {
+ check_http_header(nva, nvlen, /* request = */ 1, want_lib_error);
+}
+
+void test_nghttp3_conn_http_resp_header(void) {
+ /* test case for response */
+ /* response header lacks :status */
+ const nghttp3_nv nostatus_resnv[] = {
+ MAKE_NV("server", "foo"),
+ };
+ /* response header has 2 :status */
+ const nghttp3_nv dupstatus_resnv[] = {
+ MAKE_NV(":status", "200"),
+ MAKE_NV(":status", "200"),
+ };
+ /* response header has bad pseudo header :scheme */
+ const nghttp3_nv badpseudo_resnv[] = {
+ MAKE_NV(":status", "200"),
+ MAKE_NV(":scheme", "https"),
+ };
+ /* response header has :status after regular header field */
+ const nghttp3_nv latepseudo_resnv[] = {
+ MAKE_NV("server", "foo"),
+ MAKE_NV(":status", "200"),
+ };
+ /* response header has bad status code */
+ const nghttp3_nv badstatus_resnv[] = {
+ MAKE_NV(":status", "2000"),
+ };
+ /* response header has bad content-length */
+ const nghttp3_nv badcl_resnv[] = {
+ MAKE_NV(":status", "200"),
+ MAKE_NV("content-length", "-1"),
+ };
+ /* response header has multiple content-length */
+ const nghttp3_nv dupcl_resnv[] = {
+ MAKE_NV(":status", "200"),
+ MAKE_NV("content-length", "0"),
+ MAKE_NV("content-length", "0"),
+ };
+ /* response header has disallowed header field */
+ const nghttp3_nv badhd_resnv[] = {
+ MAKE_NV(":status", "200"),
+ MAKE_NV("connection", "close"),
+ };
+ /* response header has content-length with 100 status code */
+ const nghttp3_nv cl1xx_resnv[] = {
+ MAKE_NV(":status", "100"),
+ MAKE_NV("content-length", "0"),
+ };
+ /* response header has 0 content-length with 204 status code */
+ const nghttp3_nv cl204_resnv[] = {
+ MAKE_NV(":status", "204"),
+ MAKE_NV("content-length", "0"),
+ };
+ /* response header has nonzero content-length with 204 status
+ code */
+ const nghttp3_nv clnonzero204_resnv[] = {
+ MAKE_NV(":status", "204"),
+ MAKE_NV("content-length", "100"),
+ };
+ /* status code 101 should not be used in HTTP/3 because it is used
+ for HTTP Upgrade which HTTP/3 removes. */
+ const nghttp3_nv status101_resnv[] = {
+ MAKE_NV(":status", "101"),
+ };
+
+ check_http_resp_header(nostatus_resnv, nghttp3_arraylen(nostatus_resnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_resp_header(dupstatus_resnv, nghttp3_arraylen(dupstatus_resnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_resp_header(badpseudo_resnv, nghttp3_arraylen(badpseudo_resnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_resp_header(latepseudo_resnv, nghttp3_arraylen(latepseudo_resnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_resp_header(badstatus_resnv, nghttp3_arraylen(badstatus_resnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_resp_header(badcl_resnv, nghttp3_arraylen(badcl_resnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_resp_header(dupcl_resnv, nghttp3_arraylen(dupcl_resnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_resp_header(badhd_resnv, nghttp3_arraylen(badhd_resnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ /* Ignore content-length in 1xx response. */
+ check_http_resp_header(cl1xx_resnv, nghttp3_arraylen(cl1xx_resnv), 0);
+ /* This is allowed to work with widely used services. */
+ check_http_resp_header(cl204_resnv, nghttp3_arraylen(cl204_resnv), 0);
+ check_http_resp_header(clnonzero204_resnv,
+ nghttp3_arraylen(clnonzero204_resnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_resp_header(status101_resnv, nghttp3_arraylen(status101_resnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+}
+
+void test_nghttp3_conn_http_req_header(void) {
+ /* test case for request */
+ /* request header has no :path */
+ const nghttp3_nv nopath_reqnv[] = {
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":method", "GET"),
+ MAKE_NV(":authority", "localhost"),
+ };
+ /* request header has CONNECT method, but followed by :path */
+ const nghttp3_nv earlyconnect_reqnv[] = {
+ MAKE_NV(":method", "CONNECT"),
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":authority", "localhost"),
+ };
+ /* request header has CONNECT method following :path */
+ const nghttp3_nv lateconnect_reqnv[] = {
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":method", "CONNECT"),
+ MAKE_NV(":authority", "localhost"),
+ };
+ /* request header has multiple :path */
+ const nghttp3_nv duppath_reqnv[] = {
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":method", "GET"),
+ MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":path", "/"),
+ };
+ /* request header has bad content-length */
+ const nghttp3_nv badcl_reqnv[] = {
+ MAKE_NV(":scheme", "https"), MAKE_NV(":method", "POST"),
+ MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"),
+ MAKE_NV("content-length", "-1"),
+ };
+ /* request header has multiple content-length */
+ const nghttp3_nv dupcl_reqnv[] = {
+ MAKE_NV(":scheme", "https"), MAKE_NV(":method", "POST"),
+ MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"),
+ MAKE_NV("content-length", "0"), MAKE_NV("content-length", "0"),
+ };
+ /* request header has disallowed header field */
+ const nghttp3_nv badhd_reqnv[] = {
+ MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"),
+ MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"),
+ MAKE_NV("connection", "close"),
+ };
+ /* request header has :authority header field containing illegal
+ characters */
+ const nghttp3_nv badauthority_reqnv[] = {
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":method", "GET"),
+ MAKE_NV(":authority", "\x0d\x0alocalhost"),
+ MAKE_NV(":path", "/"),
+ };
+ /* request header has regular header field containing illegal
+ character before all mandatory header fields are seen. */
+ const nghttp3_nv badhdbtw_reqnv[] = {
+ MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"),
+ MAKE_NV("foo", "\x0d\x0a"), MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":path", "/"),
+ };
+ /* request header has "*" in :path header field while method is GET.
+ :path is received before :method */
+ const nghttp3_nv asteriskget1_reqnv[] = {
+ MAKE_NV(":path", "*"),
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":method", "GET"),
+ };
+ /* request header has "*" in :path header field while method is GET.
+ :method is received before :path */
+ const nghttp3_nv asteriskget2_reqnv[] = {
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":method", "GET"),
+ MAKE_NV(":path", "*"),
+ };
+ /* OPTIONS method can include "*" in :path header field. :path is
+ received before :method. */
+ const nghttp3_nv asteriskoptions1_reqnv[] = {
+ MAKE_NV(":path", "*"),
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":method", "OPTIONS"),
+ };
+ /* OPTIONS method can include "*" in :path header field. :method is
+ received before :path. */
+ const nghttp3_nv asteriskoptions2_reqnv[] = {
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":method", "OPTIONS"),
+ MAKE_NV(":path", "*"),
+ };
+ /* header name contains invalid character */
+ const nghttp3_nv invalidname_reqnv[] = {
+ MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"),
+ MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"),
+ MAKE_NV("\x0foo", "zzz"),
+ };
+ /* header value contains invalid character */
+ const nghttp3_nv invalidvalue_reqnv[] = {
+ MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"),
+ MAKE_NV(":authority", "localhost"), MAKE_NV(":path", "/"),
+ MAKE_NV("foo", "\x0zzz"),
+ };
+ /* :protocol is not allowed unless it is enabled by the local
+ endpoint. */
+ /* :protocol is allowed if SETTINGS_CONNECT_PROTOCOL is enabled by
+ the local endpoint. */
+ const nghttp3_nv connectproto_reqnv[] = {
+ MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"),
+ MAKE_NV(":method", "CONNECT"), MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":protocol", "websocket"),
+ };
+ /* :protocol is only allowed with CONNECT method. */
+ const nghttp3_nv connectprotoget_reqnv[] = {
+ MAKE_NV(":scheme", "https"), MAKE_NV(":path", "/"),
+ MAKE_NV(":method", "GET"), MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":protocol", "websocket"),
+ };
+ /* CONNECT method with :protocol requires :path. */
+ const nghttp3_nv connectprotonopath_reqnv[] = {
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":method", "CONNECT"),
+ MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":protocol", "websocket"),
+ };
+ /* CONNECT method with :protocol requires :authority. */
+ const nghttp3_nv connectprotonoauth_reqnv[] = {
+ MAKE_NV(":scheme", "http"), MAKE_NV(":path", "/"),
+ MAKE_NV(":method", "CONNECT"), MAKE_NV("host", "localhost"),
+ MAKE_NV(":protocol", "websocket"),
+ };
+ /* regular CONNECT method should succeed with
+ SETTINGS_CONNECT_PROTOCOL */
+ const nghttp3_nv regularconnect_reqnv[] = {
+ MAKE_NV(":method", "CONNECT"),
+ MAKE_NV(":authority", "localhost"),
+ };
+
+ /* request header has no :path */
+ check_http_req_header(nopath_reqnv, nghttp3_arraylen(nopath_reqnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_req_header(earlyconnect_reqnv,
+ nghttp3_arraylen(earlyconnect_reqnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_req_header(lateconnect_reqnv, nghttp3_arraylen(lateconnect_reqnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_req_header(duppath_reqnv, nghttp3_arraylen(duppath_reqnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_req_header(badcl_reqnv, nghttp3_arraylen(badcl_reqnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_req_header(dupcl_reqnv, nghttp3_arraylen(dupcl_reqnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_req_header(badhd_reqnv, nghttp3_arraylen(badhd_reqnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_req_header(badauthority_reqnv,
+ nghttp3_arraylen(badauthority_reqnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_req_header(badhdbtw_reqnv, nghttp3_arraylen(badhdbtw_reqnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_req_header(asteriskget1_reqnv,
+ nghttp3_arraylen(asteriskget1_reqnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_req_header(asteriskget2_reqnv,
+ nghttp3_arraylen(asteriskget2_reqnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_req_header(asteriskoptions1_reqnv,
+ nghttp3_arraylen(asteriskoptions1_reqnv), 0);
+ check_http_req_header(asteriskoptions2_reqnv,
+ nghttp3_arraylen(asteriskoptions2_reqnv), 0);
+ check_http_req_header(invalidname_reqnv, nghttp3_arraylen(invalidname_reqnv),
+ 0);
+ check_http_req_header(invalidvalue_reqnv,
+ nghttp3_arraylen(invalidvalue_reqnv), 0);
+ check_http_req_header(connectproto_reqnv,
+ nghttp3_arraylen(connectproto_reqnv), 0);
+ check_http_req_header(connectprotoget_reqnv,
+ nghttp3_arraylen(connectprotoget_reqnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_req_header(connectprotonopath_reqnv,
+ nghttp3_arraylen(connectprotonopath_reqnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_req_header(connectprotonoauth_reqnv,
+ nghttp3_arraylen(connectprotonoauth_reqnv),
+ NGHTTP3_ERR_MALFORMED_HTTP_HEADER);
+ check_http_req_header(regularconnect_reqnv,
+ nghttp3_arraylen(regularconnect_reqnv), 0);
+}
+
+void test_nghttp3_conn_http_content_length(void) {
+ uint8_t rawbuf[4096];
+ nghttp3_buf buf;
+ nghttp3_frame_headers fr;
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_ssize sconsumed;
+ nghttp3_qpack_encoder qenc;
+ nghttp3_stream *stream;
+ const nghttp3_nv reqnv[] = {
+ MAKE_NV(":path", "/"), MAKE_NV(":method", "PUT"),
+ MAKE_NV(":scheme", "https"), MAKE_NV("te", "trailers"),
+ MAKE_NV("host", "localhost"), MAKE_NV("content-length", "9000000000"),
+ };
+ const nghttp3_nv resnv[] = {
+ MAKE_NV(":status", "200"),
+ MAKE_NV("te", "trailers"),
+ MAKE_NV("content-length", "9000000000"),
+ };
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+
+ /* client */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)resnv;
+ fr.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+ CU_ASSERT(9000000000LL == stream->rx.http.content_length);
+ CU_ASSERT(200 == stream->rx.http.status_code);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* server */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)reqnv;
+ fr.nvlen = nghttp3_arraylen(reqnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(9000000000LL == stream->rx.http.content_length);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+}
+
+void test_nghttp3_conn_http_content_length_mismatch(void) {
+ uint8_t rawbuf[4096];
+ nghttp3_buf buf;
+ nghttp3_frame_headers fr;
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_ssize sconsumed;
+ nghttp3_qpack_encoder qenc;
+ const nghttp3_nv reqnv[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":method", "PUT"),
+ MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV("content-length", "20"),
+ };
+ const nghttp3_nv resnv[] = {
+ MAKE_NV(":status", "200"),
+ MAKE_NV("content-length", "20"),
+ };
+ int rv;
+ nghttp3_stream *stream;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+
+ /* content-length is 20, but no DATA is present and see fin */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)reqnv;
+ fr.nvlen = nghttp3_arraylen(reqnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 1);
+
+ CU_ASSERT(NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING == sconsumed);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* content-length is 20, but no DATA is present and stream is
+ reset */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)reqnv;
+ fr.nvlen = nghttp3_arraylen(reqnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+
+ rv = nghttp3_conn_shutdown_stream_read(conn, 0);
+
+ CU_ASSERT(0 == rv);
+
+ rv = nghttp3_conn_close_stream(conn, 0, NGHTTP3_H3_NO_ERROR);
+
+ CU_ASSERT(0 == rv);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* content-length is 20, but server receives 21 bytes DATA. */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)reqnv;
+ fr.nvlen = nghttp3_arraylen(reqnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+ nghttp3_write_frame_data(&buf, 21);
+
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING == sconsumed);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* Check client side as well */
+
+ /* content-length is 20, but no DATA is present and see fin */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)resnv;
+ fr.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 1);
+
+ CU_ASSERT(NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING == sconsumed);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* content-length is 20, but no DATA is present and stream is
+ reset */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)resnv;
+ fr.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+
+ rv = nghttp3_conn_shutdown_stream_read(conn, 0);
+
+ CU_ASSERT(0 == rv);
+
+ rv = nghttp3_conn_close_stream(conn, 0, NGHTTP3_H3_NO_ERROR);
+
+ CU_ASSERT(0 == rv);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* content-length is 20, but server receives 21 bytes DATA. */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)resnv;
+ fr.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+ nghttp3_write_frame_data(&buf, 21);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING == sconsumed);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+}
+
+void test_nghttp3_conn_http_non_final_response(void) {
+ uint8_t rawbuf[4096];
+ nghttp3_buf buf;
+ nghttp3_frame_headers fr;
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_ssize sconsumed;
+ nghttp3_qpack_encoder qenc;
+ const nghttp3_nv infonv[] = {
+ MAKE_NV(":status", "103"),
+ };
+ const nghttp3_nv resnv[] = {
+ MAKE_NV(":status", "204"),
+ };
+ const nghttp3_nv trnv[] = {
+ MAKE_NV("my-status", "ok"),
+ };
+ nghttp3_stream *stream;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+
+ /* non-final followed by DATA is illegal. */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)infonv;
+ fr.nvlen = nghttp3_arraylen(infonv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+ nghttp3_write_frame_data(&buf, 0);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(NGHTTP3_ERR_H3_FRAME_UNEXPECTED == sconsumed);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* 2 non-finals followed by final headers */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)infonv;
+ fr.nvlen = nghttp3_arraylen(infonv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)resnv;
+ fr.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* non-finals followed by trailers; this trailer is treated as
+ another non-final or final header fields. Since it does not
+ include mandatory header field, it is treated as error. */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)infonv;
+ fr.nvlen = nghttp3_arraylen(infonv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)trnv;
+ fr.nvlen = nghttp3_arraylen(trnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(stream->flags & NGHTTP3_STREAM_FLAG_HTTP_ERROR);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+}
+
+void test_nghttp3_conn_http_trailers(void) {
+ uint8_t rawbuf[4096];
+ nghttp3_buf buf;
+ nghttp3_frame_headers fr;
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_ssize sconsumed;
+ nghttp3_qpack_encoder qenc;
+ const nghttp3_nv reqnv[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":method", "PUT"),
+ MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":scheme", "https"),
+ };
+ const nghttp3_nv connect_reqnv[] = {
+ MAKE_NV(":method", "CONNECT"),
+ MAKE_NV(":authority", "localhost"),
+ };
+ const nghttp3_nv resnv[] = {
+ MAKE_NV(":status", "200"),
+ };
+ const nghttp3_nv trnv[] = {
+ MAKE_NV("foo", "bar"),
+ };
+ nghttp3_stream *stream;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+
+ /* final response followed by trailers */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)resnv;
+ fr.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)trnv;
+ fr.nvlen = nghttp3_arraylen(trnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* trailers contain :status */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)resnv;
+ fr.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)resnv;
+ fr.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(stream->flags & NGHTTP3_STREAM_FLAG_HTTP_ERROR);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* Receiving 2 trailers HEADERS is invalid*/
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)resnv;
+ fr.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)trnv;
+ fr.nvlen = nghttp3_arraylen(trnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING == sconsumed);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* We don't expect response trailers after HEADERS with CONNECT
+ request */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)resnv;
+ fr.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)trnv;
+ fr.nvlen = nghttp3_arraylen(trnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ nghttp3_http_record_request_method(stream, connect_reqnv,
+ nghttp3_arraylen(connect_reqnv));
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(NGHTTP3_ERR_H3_FRAME_UNEXPECTED == sconsumed);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* We don't expect response trailers after DATA with CONNECT
+ request */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)resnv;
+ fr.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+ nghttp3_write_frame_data(&buf, 99);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)trnv;
+ fr.nvlen = nghttp3_arraylen(trnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ nghttp3_http_record_request_method(stream, connect_reqnv,
+ nghttp3_arraylen(connect_reqnv));
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(NGHTTP3_ERR_H3_FRAME_UNEXPECTED == sconsumed);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* request followed by trailers */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)reqnv;
+ fr.nvlen = nghttp3_arraylen(reqnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)trnv;
+ fr.nvlen = nghttp3_arraylen(trnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* request followed by trailers which contains pseudo headers */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)reqnv;
+ fr.nvlen = nghttp3_arraylen(reqnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(stream->flags & NGHTTP3_STREAM_FLAG_HTTP_ERROR);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* request followed by 2 trailers */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)reqnv;
+ fr.nvlen = nghttp3_arraylen(reqnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)trnv;
+ fr.nvlen = nghttp3_arraylen(trnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(NGHTTP3_ERR_H3_FRAME_UNEXPECTED == sconsumed);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* We don't expect trailers after HEADERS with CONNECT request */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)connect_reqnv;
+ fr.nvlen = nghttp3_arraylen(connect_reqnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)trnv;
+ fr.nvlen = nghttp3_arraylen(trnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(NGHTTP3_ERR_H3_FRAME_UNEXPECTED == sconsumed);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* We don't expect trailers after DATA with CONNECT request */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)connect_reqnv;
+ fr.nvlen = nghttp3_arraylen(connect_reqnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+ nghttp3_write_frame_data(&buf, 11);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)trnv;
+ fr.nvlen = nghttp3_arraylen(trnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(NGHTTP3_ERR_H3_FRAME_UNEXPECTED == sconsumed);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+}
+
+void test_nghttp3_conn_http_ignore_content_length(void) {
+ uint8_t rawbuf[4096];
+ nghttp3_buf buf;
+ nghttp3_frame_headers fr;
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_ssize sconsumed;
+ nghttp3_qpack_encoder qenc;
+ const nghttp3_nv reqnv[] = {
+ MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":method", "CONNECT"),
+ MAKE_NV("content-length", "999999"),
+ };
+ const nghttp3_nv resnv[] = {
+ MAKE_NV(":status", "304"),
+ MAKE_NV("content-length", "20"),
+ };
+ const nghttp3_nv cl_resnv[] = {
+ MAKE_NV(":status", "200"),
+ MAKE_NV("content-length", "0"),
+ };
+ int rv;
+ nghttp3_stream *stream;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+
+ /* If status code is 304, content-length must be ignored. */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)resnv;
+ fr.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+ CU_ASSERT(0 == stream->rx.http.content_length);
+
+ rv = nghttp3_conn_close_stream(conn, 0, NGHTTP3_H3_NO_ERROR);
+
+ CU_ASSERT(0 == rv);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* If method is CONNECT, content-length must be ignored. */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)reqnv;
+ fr.nvlen = nghttp3_arraylen(reqnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(-1 == stream->rx.http.content_length);
+
+ rv = nghttp3_conn_close_stream(conn, 0, NGHTTP3_H3_NO_ERROR);
+
+ CU_ASSERT(0 == rv);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* Content-Length in 200 response to CONNECT is ignored */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)cl_resnv;
+ fr.nvlen = nghttp3_arraylen(cl_resnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ stream->rx.http.flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT;
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+ CU_ASSERT(-1 == stream->rx.http.content_length);
+
+ rv = nghttp3_conn_close_stream(conn, 0, NGHTTP3_H3_NO_ERROR);
+
+ CU_ASSERT(0 == rv);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+}
+
+void test_nghttp3_conn_http_record_request_method(void) {
+ uint8_t rawbuf[4096];
+ nghttp3_buf buf;
+ nghttp3_frame_headers fr;
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_ssize sconsumed;
+ nghttp3_qpack_encoder qenc;
+ const nghttp3_nv connect_reqnv[] = {
+ MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":method", "CONNECT"),
+ };
+ const nghttp3_nv head_reqnv[] = {
+ MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":method", "HEAD"),
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":path", "/"),
+ };
+ const nghttp3_nv resnv[] = {
+ MAKE_NV(":status", "200"),
+ MAKE_NV("content-length", "1000000007"),
+ };
+ nghttp3_stream *stream;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+
+ /* content-length is not allowed with 200 status code in response to
+ CONNECT request. Just ignore it. */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)resnv;
+ fr.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ nghttp3_http_record_request_method(stream, connect_reqnv,
+ nghttp3_arraylen(connect_reqnv));
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+ CU_ASSERT(-1 == stream->rx.http.content_length);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* The content-length in response to HEAD request must be
+ ignored. */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)resnv;
+ fr.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_create_stream(conn, &stream, 0);
+ stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
+ nghttp3_http_record_request_method(stream, head_reqnv,
+ nghttp3_arraylen(head_reqnv));
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+ CU_ASSERT(0 == stream->rx.http.content_length);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+}
+
+void test_nghttp3_conn_http_error(void) {
+ uint8_t rawbuf[4096];
+ nghttp3_buf buf, ebuf;
+ nghttp3_frame_headers fr;
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_ssize sconsumed;
+ nghttp3_qpack_encoder qenc;
+ nghttp3_settings settings;
+ const nghttp3_nv dupschemenv[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":method", "GET"),
+ MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":scheme", "https"),
+ };
+ const nghttp3_nv noschemenv[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":method", "GET"),
+ MAKE_NV(":authority", "localhost"),
+ };
+ userdata ud;
+ nghttp3_stream *stream;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.stop_sending = stop_sending;
+ callbacks.reset_stream = reset_stream;
+ nghttp3_settings_default(&settings);
+ settings.qpack_max_dtable_capacity = 4096;
+ settings.qpack_blocked_streams = 100;
+
+ /* duplicated :scheme */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+ memset(&ud, 0, sizeof(ud));
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)dupschemenv;
+ fr.nvlen = nghttp3_arraylen(dupschemenv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, &ud);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+ CU_ASSERT(1 == ud.stop_sending_cb.ncalled);
+ CU_ASSERT(0 == ud.stop_sending_cb.stream_id);
+ CU_ASSERT(NGHTTP3_H3_MESSAGE_ERROR == ud.stop_sending_cb.app_error_code);
+ CU_ASSERT(1 == ud.reset_stream_cb.ncalled);
+ CU_ASSERT(0 == ud.reset_stream_cb.stream_id);
+ CU_ASSERT(NGHTTP3_H3_MESSAGE_ERROR == ud.reset_stream_cb.app_error_code);
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(stream->flags & NGHTTP3_STREAM_FLAG_HTTP_ERROR);
+
+ /* After the error, everything is just discarded. */
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+ CU_ASSERT(1 == ud.stop_sending_cb.ncalled);
+ CU_ASSERT(1 == ud.reset_stream_cb.ncalled);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* without :scheme */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+ memset(&ud, 0, sizeof(ud));
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)noschemenv;
+ fr.nvlen = nghttp3_arraylen(noschemenv);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, &ud);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+ CU_ASSERT(1 == ud.stop_sending_cb.ncalled);
+ CU_ASSERT(0 == ud.stop_sending_cb.stream_id);
+ CU_ASSERT(NGHTTP3_H3_MESSAGE_ERROR == ud.stop_sending_cb.app_error_code);
+ CU_ASSERT(1 == ud.reset_stream_cb.ncalled);
+ CU_ASSERT(0 == ud.reset_stream_cb.stream_id);
+ CU_ASSERT(NGHTTP3_H3_MESSAGE_ERROR == ud.reset_stream_cb.app_error_code);
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(stream->flags & NGHTTP3_STREAM_FLAG_HTTP_ERROR);
+
+ /* After the error, everything is just discarded. */
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+ CU_ASSERT(1 == ud.stop_sending_cb.ncalled);
+ CU_ASSERT(1 == ud.reset_stream_cb.ncalled);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+
+ /* error on blocked stream */
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+ nghttp3_qpack_encoder_init(&qenc, settings.qpack_max_dtable_capacity, mem);
+ nghttp3_qpack_encoder_set_max_blocked_streams(&qenc,
+ settings.qpack_blocked_streams);
+ nghttp3_qpack_encoder_set_max_dtable_capacity(
+ &qenc, settings.qpack_max_dtable_capacity);
+ memset(&ud, 0, sizeof(ud));
+
+ nghttp3_buf_init(&ebuf);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.nva = (nghttp3_nv *)noschemenv;
+ fr.nvlen = nghttp3_arraylen(noschemenv);
+
+ nghttp3_write_frame_qpack_dyn(&buf, &ebuf, &qenc, 0, (nghttp3_frame *)&fr);
+
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, &ud);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(sconsumed > 0);
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) != sconsumed);
+ CU_ASSERT(0 == ud.stop_sending_cb.ncalled);
+ CU_ASSERT(0 == ud.reset_stream_cb.ncalled);
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(!(stream->flags & NGHTTP3_STREAM_FLAG_HTTP_ERROR));
+ CU_ASSERT(0 != nghttp3_ringbuf_len(&stream->inq));
+
+ nghttp3_buf_reset(&buf);
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_QPACK_ENCODER);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 7, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 7, ebuf.pos,
+ nghttp3_buf_len(&ebuf), /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&ebuf) == sconsumed);
+ CU_ASSERT(stream->flags & NGHTTP3_STREAM_FLAG_HTTP_ERROR);
+ CU_ASSERT(0 == nghttp3_ringbuf_len(&stream->inq));
+ CU_ASSERT(1 == ud.stop_sending_cb.ncalled);
+ CU_ASSERT(0 == ud.stop_sending_cb.stream_id);
+ CU_ASSERT(NGHTTP3_H3_MESSAGE_ERROR == ud.stop_sending_cb.app_error_code);
+ CU_ASSERT(1 == ud.reset_stream_cb.ncalled);
+ CU_ASSERT(0 == ud.reset_stream_cb.stream_id);
+ CU_ASSERT(NGHTTP3_H3_MESSAGE_ERROR == ud.reset_stream_cb.app_error_code);
+
+ /* After the error, everything is just discarded. */
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == sconsumed);
+ CU_ASSERT(1 == ud.stop_sending_cb.ncalled);
+ CU_ASSERT(1 == ud.reset_stream_cb.ncalled);
+
+ nghttp3_buf_free(&ebuf, mem);
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+}
+
+void test_nghttp3_conn_qpack_blocked_stream(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ nghttp3_qpack_encoder qenc;
+ int rv;
+ nghttp3_buf ebuf;
+ uint8_t rawbuf[4096];
+ nghttp3_buf buf;
+ const nghttp3_nv reqnv[] = {
+ MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":method", "GET"),
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":scheme", "https"),
+ };
+ const nghttp3_nv resnv[] = {
+ MAKE_NV(":status", "200"),
+ MAKE_NV("server", "nghttp3"),
+ };
+ nghttp3_frame fr;
+ nghttp3_ssize sconsumed;
+ nghttp3_stream *stream;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+ settings.qpack_max_dtable_capacity = 4096;
+ settings.qpack_blocked_streams = 100;
+
+ /* The deletion of QPACK blocked stream is deferred to the moment
+ when it is unblocked */
+ nghttp3_buf_init(&ebuf);
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+
+ nghttp3_qpack_encoder_init(&qenc, settings.qpack_max_dtable_capacity, mem);
+ nghttp3_qpack_encoder_set_max_blocked_streams(&qenc,
+ settings.qpack_blocked_streams);
+ nghttp3_qpack_encoder_set_max_dtable_capacity(
+ &qenc, settings.qpack_max_dtable_capacity);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_bind_qpack_streams(conn, 2, 6);
+
+ rv = nghttp3_conn_submit_request(conn, 0, reqnv, nghttp3_arraylen(reqnv),
+ NULL, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.headers.nva = (nghttp3_nv *)resnv;
+ fr.headers.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack_dyn(&buf, &ebuf, &qenc, 0, &fr);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 1);
+
+ CU_ASSERT(sconsumed > 0);
+ CU_ASSERT(sconsumed != (nghttp3_ssize)nghttp3_buf_len(&buf));
+
+ rv = nghttp3_conn_close_stream(conn, 0, NGHTTP3_H3_NO_ERROR);
+
+ CU_ASSERT(0 == rv);
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(stream->flags & NGHTTP3_STREAM_FLAG_CLOSED);
+
+ nghttp3_buf_reset(&buf);
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_QPACK_ENCODER);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 7, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(sconsumed == (nghttp3_ssize)nghttp3_buf_len(&buf));
+ CU_ASSERT(NULL != nghttp3_conn_find_stream(conn, 0));
+
+ sconsumed = nghttp3_conn_read_stream(conn, 7, ebuf.pos,
+ nghttp3_buf_len(&ebuf), /* fin = */ 0);
+
+ CU_ASSERT(sconsumed == (nghttp3_ssize)nghttp3_buf_len(&ebuf));
+ CU_ASSERT(NULL == nghttp3_conn_find_stream(conn, 0));
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+ nghttp3_buf_free(&ebuf, mem);
+
+ /* Stream that is blocked receives HEADERS which has empty
+ representation (that is only include Header Block Prefix) */
+ nghttp3_buf_init(&ebuf);
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+
+ nghttp3_qpack_encoder_init(&qenc, settings.qpack_max_dtable_capacity, mem);
+ nghttp3_qpack_encoder_set_max_blocked_streams(&qenc,
+ settings.qpack_blocked_streams);
+ nghttp3_qpack_encoder_set_max_dtable_capacity(
+ &qenc, settings.qpack_max_dtable_capacity);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_bind_qpack_streams(conn, 2, 6);
+
+ rv = nghttp3_conn_submit_request(conn, 0, reqnv, nghttp3_arraylen(reqnv),
+ NULL, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.headers.nva = (nghttp3_nv *)resnv;
+ fr.headers.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack_dyn(&buf, &ebuf, &qenc, 0, &fr);
+
+ assert(nghttp3_buf_len(&buf) > 4);
+
+ /* Craft empty HEADERS (just leave Header Block Prefix) */
+ buf.pos[1] = 2;
+ /* Write garbage to continue to read stream */
+ buf.pos[4] = 0xff;
+
+ sconsumed = nghttp3_conn_read_stream(
+ conn, 0, buf.pos, 5 /* Frame header + Header Block Prefix */,
+ /* fin = */ 1);
+
+ CU_ASSERT(sconsumed > 0);
+ CU_ASSERT(sconsumed != (nghttp3_ssize)nghttp3_buf_len(&buf));
+
+ rv = nghttp3_conn_close_stream(conn, 0, NGHTTP3_H3_NO_ERROR);
+
+ CU_ASSERT(0 == rv);
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(stream->flags & NGHTTP3_STREAM_FLAG_CLOSED);
+
+ nghttp3_buf_reset(&buf);
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_QPACK_ENCODER);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 7, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(sconsumed == (nghttp3_ssize)nghttp3_buf_len(&buf));
+ CU_ASSERT(NULL != nghttp3_conn_find_stream(conn, 0));
+
+ sconsumed = nghttp3_conn_read_stream(conn, 7, ebuf.pos,
+ nghttp3_buf_len(&ebuf), /* fin = */ 0);
+
+ CU_ASSERT(NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED == sconsumed);
+ CU_ASSERT(NULL != nghttp3_conn_find_stream(conn, 0));
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+ nghttp3_buf_free(&ebuf, mem);
+}
+
+void test_nghttp3_conn_just_fin(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ nghttp3_vec vec[256];
+ nghttp3_ssize sveccnt;
+ int rv;
+ int64_t stream_id;
+ const nghttp3_nv nva[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":authority", "example.com"),
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":method", "GET"),
+ };
+ nghttp3_data_reader dr;
+ int fin;
+ userdata ud;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+ memset(&ud, 0, sizeof(ud));
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, &ud);
+
+ nghttp3_conn_bind_control_stream(conn, 2);
+ nghttp3_conn_bind_qpack_streams(conn, 6, 10);
+
+ /* Write control streams */
+ for (;;) {
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(sveccnt >= 0);
+
+ if (sveccnt == 0) {
+ break;
+ }
+
+ rv = nghttp3_conn_add_write_offset(
+ conn, stream_id, (size_t)nghttp3_vec_len(vec, (size_t)sveccnt));
+
+ CU_ASSERT(0 == rv);
+ }
+
+ /* No DATA frame header */
+ dr.read_data = step_read_data;
+ rv = nghttp3_conn_submit_request(conn, 0, nva, nghttp3_arraylen(nva), &dr,
+ NULL);
+
+ CU_ASSERT(0 == rv);
+
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(1 == sveccnt);
+ CU_ASSERT(0 == stream_id);
+ CU_ASSERT(1 == fin);
+
+ rv = nghttp3_conn_add_write_offset(
+ conn, stream_id, (size_t)nghttp3_vec_len(vec, (size_t)sveccnt));
+
+ CU_ASSERT(0 == rv);
+
+ /* Just fin */
+ ud.data.nblock = 1;
+ dr.read_data = block_then_step_read_data;
+
+ rv = nghttp3_conn_submit_request(conn, 4, nva, nghttp3_arraylen(nva), &dr,
+ NULL);
+
+ CU_ASSERT(0 == rv);
+
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(1 == sveccnt);
+ CU_ASSERT(4 == stream_id);
+ CU_ASSERT(0 == fin);
+
+ rv = nghttp3_conn_add_write_offset(
+ conn, stream_id, (size_t)nghttp3_vec_len(vec, (size_t)sveccnt));
+
+ CU_ASSERT(0 == rv);
+
+ /* Resume stream 4 because it was blocked */
+ nghttp3_conn_resume_stream(conn, 4);
+
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(0 == sveccnt);
+ CU_ASSERT(4 == stream_id);
+ CU_ASSERT(1 == fin);
+
+ rv = nghttp3_conn_add_write_offset(
+ conn, stream_id, (size_t)nghttp3_vec_len(vec, (size_t)sveccnt));
+
+ CU_ASSERT(0 == rv);
+
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(0 == sveccnt);
+ CU_ASSERT(-1 == stream_id);
+ CU_ASSERT(0 == fin);
+
+ nghttp3_conn_del(conn);
+}
+
+void test_nghttp3_conn_submit_response_read_blocked(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ const nghttp3_nv nva[] = {
+ MAKE_NV(":status", "200"),
+ };
+ nghttp3_stream *stream;
+ int rv;
+ nghttp3_vec vec[256];
+ int fin;
+ int64_t stream_id;
+ nghttp3_ssize sveccnt;
+ nghttp3_data_reader dr = {step_then_block_read_data};
+ userdata ud;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+
+ /* Make sure that flushing serialized data while
+ NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED is set does not cause any
+ error */
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, &ud);
+ conn->remote.bidi.max_client_streams = 1;
+ nghttp3_conn_bind_qpack_streams(conn, 7, 11);
+
+ nghttp3_conn_create_stream(conn, &stream, 0);
+
+ ud.data.left = 1000;
+ ud.data.step = 1000;
+ rv = nghttp3_conn_submit_response(conn, 0, nva, nghttp3_arraylen(nva), &dr);
+
+ CU_ASSERT(0 == rv);
+
+ for (;;) {
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(sveccnt >= 0);
+
+ if (sveccnt <= 0) {
+ break;
+ }
+
+ rv = nghttp3_conn_add_write_offset(conn, stream_id, 1);
+
+ CU_ASSERT(0 == rv);
+ }
+
+ nghttp3_conn_del(conn);
+}
+
+void test_nghttp3_conn_recv_uni(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ nghttp3_ssize nread;
+ uint8_t buf[256];
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+
+ /* 0 length unidirectional stream must be ignored */
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+
+ nread = nghttp3_conn_read_stream(conn, 3, NULL, 0, /* fin = */ 1);
+
+ CU_ASSERT(0 == nread);
+ CU_ASSERT(NULL == nghttp3_conn_find_stream(conn, 3));
+
+ nghttp3_conn_del(conn);
+
+ /* 0 length unidirectional stream; first get 0 length without fin,
+ and then get 0 length with fin. */
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+
+ nread = nghttp3_conn_read_stream(conn, 3, NULL, 0, /* fin = */ 0);
+
+ CU_ASSERT(0 == nread);
+ CU_ASSERT(NULL != nghttp3_conn_find_stream(conn, 3));
+
+ nread = nghttp3_conn_read_stream(conn, 3, NULL, 0, /* fin = */ 1);
+
+ CU_ASSERT(0 == nread);
+ CU_ASSERT(NULL == nghttp3_conn_find_stream(conn, 3));
+
+ nghttp3_conn_del(conn);
+
+ /* Fin while reading stream header is treated as error. */
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+
+ /* 4 bytes integer */
+ buf[0] = 0xc0;
+ nread = nghttp3_conn_read_stream(conn, 3, buf, 1, /* fin = */ 0);
+
+ CU_ASSERT(1 == nread);
+ CU_ASSERT(NULL != nghttp3_conn_find_stream(conn, 3));
+
+ nread = nghttp3_conn_read_stream(conn, 3, NULL, 0, /* fin = */ 1);
+
+ CU_ASSERT(NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR == nread);
+
+ nghttp3_conn_del(conn);
+}
+
+void test_nghttp3_conn_recv_goaway(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ nghttp3_frame fr;
+ uint8_t rawbuf[1024];
+ nghttp3_buf buf;
+ nghttp3_ssize nconsumed;
+ const nghttp3_nv nva[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":authority", "example.com"),
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":method", "GET"),
+ };
+ int rv;
+ userdata ud;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.shutdown = conn_shutdown;
+ memset(&ud, 0, sizeof(ud));
+ nghttp3_settings_default(&settings);
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+
+ /* Client receives GOAWAY */
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, &ud);
+ nghttp3_conn_bind_control_stream(conn, 2);
+ nghttp3_conn_bind_qpack_streams(conn, 6, 10);
+
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_CONTROL);
+
+ fr.hd.type = NGHTTP3_FRAME_SETTINGS;
+ fr.settings.niv = 0;
+
+ nghttp3_write_frame(&buf, &fr);
+
+ fr.hd.type = NGHTTP3_FRAME_GOAWAY;
+ fr.goaway.id = 12;
+
+ nghttp3_write_frame(&buf, &fr);
+
+ ud.shutdown_cb.ncalled = 0;
+ ud.shutdown_cb.id = 0;
+ nconsumed = nghttp3_conn_read_stream(conn, 3, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == nconsumed);
+ CU_ASSERT(conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_RECVED);
+ CU_ASSERT(12 == conn->rx.goaway_id);
+ CU_ASSERT(1 == ud.shutdown_cb.ncalled);
+ CU_ASSERT(12 == ud.shutdown_cb.id);
+
+ /* Cannot submit request anymore */
+ rv = nghttp3_conn_submit_request(conn, 0, nva, nghttp3_arraylen(nva), NULL,
+ NULL);
+
+ CU_ASSERT(NGHTTP3_ERR_CONN_CLOSING == rv);
+
+ nghttp3_conn_del(conn);
+
+ nghttp3_buf_reset(&buf);
+
+ /* Receiving GOAWAY with increased ID is treated as error */
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, &ud);
+ nghttp3_conn_bind_control_stream(conn, 2);
+ nghttp3_conn_bind_qpack_streams(conn, 6, 10);
+
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_CONTROL);
+
+ fr.hd.type = NGHTTP3_FRAME_SETTINGS;
+ fr.settings.niv = 0;
+
+ nghttp3_write_frame(&buf, &fr);
+
+ fr.hd.type = NGHTTP3_FRAME_GOAWAY;
+ fr.goaway.id = 12;
+
+ nghttp3_write_frame(&buf, &fr);
+
+ fr.hd.type = NGHTTP3_FRAME_GOAWAY;
+ fr.goaway.id = 16;
+
+ nghttp3_write_frame(&buf, &fr);
+
+ ud.shutdown_cb.ncalled = 0;
+ ud.shutdown_cb.id = 0;
+ nconsumed = nghttp3_conn_read_stream(conn, 3, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(NGHTTP3_ERR_H3_ID_ERROR == nconsumed);
+ CU_ASSERT(conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_RECVED);
+ CU_ASSERT(12 == conn->rx.goaway_id);
+ CU_ASSERT(1 == ud.shutdown_cb.ncalled);
+ CU_ASSERT(12 == ud.shutdown_cb.id);
+
+ nghttp3_conn_del(conn);
+
+ nghttp3_buf_reset(&buf);
+
+ /* Server receives GOAWAY */
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, &ud);
+ nghttp3_conn_bind_control_stream(conn, 3);
+ nghttp3_conn_bind_qpack_streams(conn, 7, 11);
+
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_CONTROL);
+
+ fr.hd.type = NGHTTP3_FRAME_SETTINGS;
+ fr.settings.niv = 0;
+
+ nghttp3_write_frame(&buf, &fr);
+
+ fr.hd.type = NGHTTP3_FRAME_GOAWAY;
+ fr.goaway.id = 101;
+
+ nghttp3_write_frame(&buf, &fr);
+
+ ud.shutdown_cb.ncalled = 0;
+ ud.shutdown_cb.id = 0;
+ nconsumed = nghttp3_conn_read_stream(conn, 2, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == nconsumed);
+ CU_ASSERT(conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_RECVED);
+ CU_ASSERT(101 == conn->rx.goaway_id);
+ CU_ASSERT(1 == ud.shutdown_cb.ncalled);
+ CU_ASSERT(101 == ud.shutdown_cb.id);
+
+ nghttp3_conn_del(conn);
+}
+
+void test_nghttp3_conn_shutdown_server(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ nghttp3_frame fr;
+ uint8_t rawbuf[1024];
+ nghttp3_buf buf;
+ nghttp3_ssize nconsumed;
+ nghttp3_stream *stream;
+ nghttp3_qpack_encoder qenc;
+ const nghttp3_nv nva[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":authority", "example.com"),
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":method", "GET"),
+ };
+ int rv;
+ userdata ud;
+ nghttp3_ssize sveccnt;
+ nghttp3_vec vec[256];
+ int64_t stream_id;
+ int fin;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.stop_sending = stop_sending;
+ callbacks.reset_stream = reset_stream;
+ nghttp3_settings_default(&settings);
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+
+ /* Server sends GOAWAY and rejects stream whose ID is greater than
+ or equal to the ID in GOAWAY. */
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, &ud);
+ nghttp3_conn_bind_control_stream(conn, 3);
+ nghttp3_conn_bind_qpack_streams(conn, 7, 11);
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.headers.nva = (nghttp3_nv *)nva;
+ fr.headers.nvlen = nghttp3_arraylen(nva);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 4, &fr);
+
+ nconsumed = nghttp3_conn_read_stream(conn, 4, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == nconsumed);
+ CU_ASSERT(4 == conn->rx.max_stream_id_bidi);
+
+ rv = nghttp3_conn_shutdown(conn);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED);
+ CU_ASSERT(8 == conn->tx.goaway_id);
+
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(sveccnt > 0);
+ CU_ASSERT(3 == stream_id);
+
+ nghttp3_buf_reset(&buf);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.headers.nva = (nghttp3_nv *)nva;
+ fr.headers.nvlen = nghttp3_arraylen(nva);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 8, &fr);
+
+ memset(&ud, 0, sizeof(ud));
+ nconsumed = nghttp3_conn_read_stream(conn, 8, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == nconsumed);
+ CU_ASSERT(8 == conn->rx.max_stream_id_bidi);
+ CU_ASSERT(1 == ud.stop_sending_cb.ncalled);
+ CU_ASSERT(8 == ud.stop_sending_cb.stream_id);
+ CU_ASSERT(NGHTTP3_H3_REQUEST_REJECTED == ud.stop_sending_cb.app_error_code);
+ CU_ASSERT(1 == ud.reset_stream_cb.ncalled);
+ CU_ASSERT(8 == ud.reset_stream_cb.stream_id);
+ CU_ASSERT(NGHTTP3_H3_REQUEST_REJECTED == ud.reset_stream_cb.app_error_code);
+
+ stream = nghttp3_conn_find_stream(conn, 8);
+
+ CU_ASSERT(NGHTTP3_REQ_STREAM_STATE_IGN_REST == stream->rstate.state);
+
+ nghttp3_qpack_encoder_free(&qenc);
+ nghttp3_conn_del(conn);
+}
+
+void test_nghttp3_conn_shutdown_client(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ uint8_t rawbuf[1024];
+ nghttp3_buf buf;
+ const nghttp3_nv nva[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":authority", "example.com"),
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":method", "GET"),
+ };
+ int rv;
+ userdata ud;
+ nghttp3_ssize sveccnt;
+ nghttp3_vec vec[256];
+ int64_t stream_id;
+ int fin;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.stop_sending = stop_sending;
+ callbacks.reset_stream = reset_stream;
+ nghttp3_settings_default(&settings);
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+
+ /* Client sends GOAWAY and rejects PUSH_PROMISE whose ID is greater
+ than or equal to the ID in GOAWAY. */
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, &ud);
+ nghttp3_conn_bind_control_stream(conn, 2);
+ nghttp3_conn_bind_qpack_streams(conn, 6, 10);
+
+ rv = nghttp3_conn_submit_request(conn, 0, nva, nghttp3_arraylen(nva), NULL,
+ NULL);
+
+ CU_ASSERT(0 == rv);
+
+ rv = nghttp3_conn_shutdown(conn);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == conn->tx.goaway_id);
+
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(sveccnt > 0);
+ CU_ASSERT(2 == stream_id);
+
+ nghttp3_buf_reset(&buf);
+
+ nghttp3_conn_del(conn);
+
+ nghttp3_buf_reset(&buf);
+}
+
+void test_nghttp3_conn_priority_update(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ nghttp3_frame fr;
+ nghttp3_ssize nconsumed;
+ uint8_t rawbuf[2048];
+ nghttp3_buf buf;
+ nghttp3_stream *stream;
+ int rv;
+ userdata ud;
+ nghttp3_qpack_encoder qenc;
+ const nghttp3_nv nva[] = {
+ MAKE_NV(":path", "/"), MAKE_NV(":authority", "example.com"),
+ MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"),
+ MAKE_NV("priority", "u=5, i"),
+ };
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ memset(&ud, 0, sizeof(ud));
+ nghttp3_settings_default(&settings);
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+
+ /* Receive PRIORITY_UPDATE and stream has not been created yet */
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_bind_control_stream(conn, 3);
+ nghttp3_conn_bind_qpack_streams(conn, 7, 11);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_CONTROL);
+
+ fr.hd.type = NGHTTP3_FRAME_SETTINGS;
+ fr.settings.niv = 0;
+
+ nghttp3_write_frame(&buf, (nghttp3_frame *)&fr);
+
+ fr.hd.type = NGHTTP3_FRAME_PRIORITY_UPDATE;
+ fr.priority_update.pri_elem_id = 0;
+ fr.priority_update.pri.urgency = 2;
+ fr.priority_update.pri.inc = 1;
+
+ nghttp3_write_frame(&buf, (nghttp3_frame *)&fr);
+
+ nconsumed = nghttp3_conn_read_stream(conn, 2, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == nconsumed);
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(NULL != stream);
+ CU_ASSERT(stream->flags & NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED);
+ CU_ASSERT(2 == nghttp3_pri_uint8_urgency(stream->node.pri));
+ CU_ASSERT(1 == nghttp3_pri_uint8_inc(stream->node.pri));
+
+ nghttp3_buf_reset(&buf);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.headers.nva = (nghttp3_nv *)nva;
+ fr.headers.nvlen = nghttp3_arraylen(nva);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, &fr);
+
+ nconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 1);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == nconsumed);
+
+ /* priority header field should not override the value set by
+ PRIORITY_UPDATE frame. */
+ CU_ASSERT(2 == nghttp3_pri_uint8_urgency(stream->node.pri));
+ CU_ASSERT(1 == nghttp3_pri_uint8_inc(stream->node.pri));
+
+ nghttp3_qpack_encoder_free(&qenc);
+ nghttp3_conn_del(conn);
+ nghttp3_buf_reset(&buf);
+
+ /* Receive PRIORITY_UPDATE and stream has been created */
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_bind_control_stream(conn, 3);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ rv = nghttp3_conn_create_stream(conn, &stream, 0);
+
+ CU_ASSERT(0 == rv);
+
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_CONTROL);
+
+ fr.hd.type = NGHTTP3_FRAME_SETTINGS;
+ fr.settings.niv = 0;
+
+ nghttp3_write_frame(&buf, (nghttp3_frame *)&fr);
+
+ fr.hd.type = NGHTTP3_FRAME_PRIORITY_UPDATE;
+ fr.priority_update.pri_elem_id = 0;
+ fr.priority_update.pri.urgency = 6;
+ fr.priority_update.pri.inc = 0;
+
+ nghttp3_write_frame(&buf, (nghttp3_frame *)&fr);
+
+ nconsumed = nghttp3_conn_read_stream(conn, 2, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == nconsumed);
+ CU_ASSERT(stream->flags & NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED);
+ CU_ASSERT(6 == nghttp3_pri_uint8_urgency(stream->node.pri));
+ CU_ASSERT(0 == nghttp3_pri_uint8_inc(stream->node.pri));
+
+ nghttp3_conn_del(conn);
+ nghttp3_buf_reset(&buf);
+
+ /* Receive PRIORITY_UPDATE against non-existent push_promise */
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, &ud);
+ nghttp3_conn_bind_control_stream(conn, 3);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_CONTROL);
+
+ fr.hd.type = NGHTTP3_FRAME_SETTINGS;
+ fr.settings.niv = 0;
+
+ nghttp3_write_frame(&buf, (nghttp3_frame *)&fr);
+
+ fr.hd.type = NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID;
+ fr.priority_update.pri_elem_id = 0;
+ fr.priority_update.pri.urgency = 6;
+ fr.priority_update.pri.inc = 0;
+
+ nghttp3_write_frame(&buf, (nghttp3_frame *)&fr);
+
+ nconsumed = nghttp3_conn_read_stream(conn, 2, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(NGHTTP3_ERR_H3_ID_ERROR == nconsumed);
+
+ nghttp3_conn_del(conn);
+ nghttp3_buf_reset(&buf);
+
+ /* Receive PRIORITY_UPDATE and its Priority Field Value is larger
+ than buffer */
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, &ud);
+ nghttp3_conn_bind_control_stream(conn, 3);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_CONTROL);
+
+ fr.hd.type = NGHTTP3_FRAME_SETTINGS;
+ fr.settings.niv = 0;
+
+ nghttp3_write_frame(&buf, (nghttp3_frame *)&fr);
+
+ fr.hd.type = NGHTTP3_FRAME_PRIORITY_UPDATE;
+ fr.priority_update.pri_elem_id = 0;
+ fr.priority_update.pri.urgency = 2;
+ fr.priority_update.pri.inc = 1;
+
+ nghttp3_frame_write_priority_update_len(&fr.hd.length, &fr.priority_update);
+ fr.hd.length += 10;
+ buf.last = nghttp3_frame_write_priority_update(buf.last, &fr.priority_update);
+ memset(buf.last, ' ', 10);
+ buf.last += 10;
+
+ /* Make sure boundary check works when frame is fragmented. */
+ nconsumed =
+ nghttp3_conn_read_stream(conn, 2, buf.pos, nghttp3_buf_len(&buf) - 10,
+ /* fin = */ 0);
+ stream = nghttp3_conn_find_stream(conn, 2);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) - 10 == nconsumed);
+ CU_ASSERT(NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE == stream->rstate.state);
+
+ nconsumed =
+ nghttp3_conn_read_stream(conn, 2, buf.pos + nconsumed, 10, /* fin = */ 0);
+
+ CU_ASSERT(10 == nconsumed);
+ CU_ASSERT(NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE == stream->rstate.state);
+ CU_ASSERT(NULL == nghttp3_conn_find_stream(conn, 0));
+
+ nghttp3_conn_del(conn);
+}
+
+void test_nghttp3_conn_request_priority(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ nghttp3_frame fr;
+ nghttp3_ssize nconsumed;
+ uint8_t rawbuf[2048];
+ nghttp3_buf buf;
+ nghttp3_stream *stream;
+ userdata ud;
+ nghttp3_qpack_encoder qenc;
+ const nghttp3_nv nva[] = {
+ MAKE_NV(":path", "/"), MAKE_NV(":authority", "example.com"),
+ MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"),
+ MAKE_NV("priority", "u=5, i"),
+ };
+ const nghttp3_nv badpri_nva[] = {
+ MAKE_NV(":path", "/"), MAKE_NV(":authority", "example.com"),
+ MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"),
+ MAKE_NV("priority", "u=5, i"), MAKE_NV("priority", "i, u=x"),
+ };
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ memset(&ud, 0, sizeof(ud));
+ nghttp3_settings_default(&settings);
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+
+ /* Priority in request */
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_bind_control_stream(conn, 3);
+ nghttp3_conn_bind_qpack_streams(conn, 7, 11);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_CONTROL);
+
+ fr.hd.type = NGHTTP3_FRAME_SETTINGS;
+ fr.settings.niv = 0;
+
+ nghttp3_write_frame(&buf, (nghttp3_frame *)&fr);
+
+ nconsumed = nghttp3_conn_read_stream(conn, 2, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == nconsumed);
+
+ nghttp3_buf_reset(&buf);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.headers.nva = (nghttp3_nv *)nva;
+ fr.headers.nvlen = nghttp3_arraylen(nva);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, &fr);
+
+ nconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 1);
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(NULL != stream);
+ CU_ASSERT(5 == nghttp3_pri_uint8_urgency(stream->node.pri));
+ CU_ASSERT(1 == nghttp3_pri_uint8_inc(stream->node.pri));
+
+ nghttp3_qpack_encoder_free(&qenc);
+ nghttp3_conn_del(conn);
+ nghttp3_buf_reset(&buf);
+
+ /* Bad priority in request */
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_bind_control_stream(conn, 3);
+ nghttp3_conn_bind_qpack_streams(conn, 7, 11);
+ nghttp3_conn_set_max_client_streams_bidi(conn, 1);
+ nghttp3_qpack_encoder_init(&qenc, 0, mem);
+
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_CONTROL);
+
+ fr.hd.type = NGHTTP3_FRAME_SETTINGS;
+ fr.settings.niv = 0;
+
+ nghttp3_write_frame(&buf, (nghttp3_frame *)&fr);
+
+ nconsumed = nghttp3_conn_read_stream(conn, 2, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&buf) == nconsumed);
+
+ nghttp3_buf_reset(&buf);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.headers.nva = (nghttp3_nv *)badpri_nva;
+ fr.headers.nvlen = nghttp3_arraylen(badpri_nva);
+
+ nghttp3_write_frame_qpack(&buf, &qenc, 0, &fr);
+
+ nconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 1);
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(NULL != stream);
+ CU_ASSERT(NGHTTP3_DEFAULT_URGENCY == stream->node.pri);
+
+ nghttp3_qpack_encoder_free(&qenc);
+ nghttp3_conn_del(conn);
+ nghttp3_buf_reset(&buf);
+}
+
+void test_nghttp3_conn_set_stream_priority(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ const nghttp3_nv nva[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":authority", "example.com"),
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":method", "GET"),
+ };
+ int rv;
+ nghttp3_pri pri;
+ nghttp3_frame_entry *ent;
+ nghttp3_stream *stream;
+ size_t i;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+
+ /* Update stream priority by client */
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_bind_control_stream(conn, 2);
+ nghttp3_conn_bind_qpack_streams(conn, 6, 10);
+
+ rv = nghttp3_conn_submit_request(conn, 0, nva, nghttp3_arraylen(nva), NULL,
+ NULL);
+
+ CU_ASSERT(0 == rv);
+
+ pri.urgency = 2;
+ pri.inc = 1;
+
+ rv = nghttp3_conn_set_stream_priority(conn, 0, &pri);
+
+ CU_ASSERT(0 == rv);
+
+ stream = nghttp3_conn_find_stream(conn, 2);
+
+ for (i = 0; i < nghttp3_ringbuf_len(&stream->frq); ++i) {
+ ent = nghttp3_ringbuf_get(&stream->frq, i);
+ if (ent->fr.hd.type != NGHTTP3_FRAME_PRIORITY_UPDATE) {
+ continue;
+ }
+
+ CU_ASSERT(2 == ent->fr.priority_update.pri.urgency);
+ CU_ASSERT(1 == ent->fr.priority_update.pri.inc);
+
+ break;
+ }
+
+ CU_ASSERT(i < nghttp3_ringbuf_len(&stream->frq));
+
+ nghttp3_conn_del(conn);
+
+ /* Updating priority of stream which does not exist is an error */
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_bind_control_stream(conn, 2);
+ nghttp3_conn_bind_qpack_streams(conn, 6, 10);
+
+ pri.urgency = 2;
+ pri.inc = 1;
+
+ rv = nghttp3_conn_set_stream_priority(conn, 0, &pri);
+
+ CU_ASSERT(NGHTTP3_ERR_STREAM_NOT_FOUND == rv);
+
+ nghttp3_conn_del(conn);
+
+ /* Update stream priority by server */
+ nghttp3_conn_server_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_bind_control_stream(conn, 3);
+
+ rv = nghttp3_conn_create_stream(conn, &stream, 0);
+
+ CU_ASSERT(0 == rv);
+
+ pri.urgency = 4;
+ pri.inc = 0;
+
+ rv = nghttp3_conn_set_stream_priority(conn, 0, &pri);
+
+ CU_ASSERT(0 == rv);
+
+ stream = nghttp3_conn_find_stream(conn, 0);
+
+ CU_ASSERT(stream->flags & NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET);
+ CU_ASSERT(nghttp3_pri_to_uint8(&pri) == stream->node.pri);
+
+ nghttp3_conn_del(conn);
+}
+
+void test_nghttp3_conn_shutdown_stream_read(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ nghttp3_qpack_encoder qenc;
+ int rv;
+ nghttp3_buf ebuf;
+ uint8_t rawbuf[4096];
+ nghttp3_buf buf;
+ const nghttp3_nv reqnv[] = {
+ MAKE_NV(":authority", "localhost"),
+ MAKE_NV(":method", "GET"),
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":scheme", "https"),
+ };
+ const nghttp3_nv resnv[] = {
+ MAKE_NV(":status", "200"),
+ MAKE_NV("server", "nghttp3"),
+ };
+ nghttp3_frame fr;
+ nghttp3_ssize sconsumed;
+ size_t consumed_total;
+ userdata ud;
+ size_t indatalen;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.deferred_consume = deferred_consume;
+ nghttp3_settings_default(&settings);
+ settings.qpack_max_dtable_capacity = 4096;
+ settings.qpack_blocked_streams = 100;
+
+ /* Shutting down read-side stream when a stream is blocked by QPACK
+ dependency. */
+ nghttp3_buf_init(&ebuf);
+ nghttp3_buf_wrap_init(&buf, rawbuf, sizeof(rawbuf));
+
+ nghttp3_qpack_encoder_init(&qenc, settings.qpack_max_dtable_capacity, mem);
+ nghttp3_qpack_encoder_set_max_blocked_streams(&qenc,
+ settings.qpack_blocked_streams);
+ nghttp3_qpack_encoder_set_max_dtable_capacity(
+ &qenc, settings.qpack_max_dtable_capacity);
+
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, &ud);
+ nghttp3_conn_bind_qpack_streams(conn, 2, 6);
+
+ rv = nghttp3_conn_submit_request(conn, 0, reqnv, nghttp3_arraylen(reqnv),
+ NULL, NULL);
+
+ CU_ASSERT(0 == rv);
+
+ fr.hd.type = NGHTTP3_FRAME_HEADERS;
+ fr.headers.nva = (nghttp3_nv *)resnv;
+ fr.headers.nvlen = nghttp3_arraylen(resnv);
+
+ nghttp3_write_frame_qpack_dyn(&buf, &ebuf, &qenc, 0, &fr);
+
+ indatalen = nghttp3_buf_len(&buf);
+
+ ud.deferred_consume_cb.consumed_total = 0;
+ consumed_total = 0;
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(sconsumed > 0);
+ CU_ASSERT(sconsumed != (nghttp3_ssize)nghttp3_buf_len(&buf));
+
+ consumed_total += (size_t)sconsumed;
+
+ rv = nghttp3_conn_shutdown_stream_read(conn, 0);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == nghttp3_buf_len(&conn->qdec.dbuf));
+
+ /* Reading further stream data is discarded. */
+ nghttp3_buf_reset(&buf);
+ *buf.pos = 0;
+ ++buf.last;
+
+ ++indatalen;
+
+ sconsumed = nghttp3_conn_read_stream(conn, 0, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 1);
+
+ CU_ASSERT(1 == sconsumed);
+
+ consumed_total += (size_t)sconsumed;
+
+ nghttp3_buf_reset(&buf);
+ buf.last = nghttp3_put_varint(buf.last, NGHTTP3_STREAM_TYPE_QPACK_ENCODER);
+
+ sconsumed = nghttp3_conn_read_stream(conn, 7, buf.pos, nghttp3_buf_len(&buf),
+ /* fin = */ 0);
+
+ CU_ASSERT(sconsumed == (nghttp3_ssize)nghttp3_buf_len(&buf));
+
+ sconsumed = nghttp3_conn_read_stream(conn, 7, ebuf.pos,
+ nghttp3_buf_len(&ebuf), /* fin = */ 0);
+
+ CU_ASSERT(sconsumed == (nghttp3_ssize)nghttp3_buf_len(&ebuf));
+ /* Make sure that Section Acknowledgement is not written. */
+ CU_ASSERT(1 == nghttp3_buf_len(&conn->qdec.dbuf));
+ CU_ASSERT(indatalen ==
+ consumed_total + ud.deferred_consume_cb.consumed_total);
+
+ nghttp3_conn_del(conn);
+ nghttp3_qpack_encoder_free(&qenc);
+ nghttp3_buf_free(&ebuf, mem);
+}
+
+void test_nghttp3_conn_stream_data_overflow(void) {
+#if SIZE_MAX > UINT32_MAX
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_conn *conn;
+ nghttp3_callbacks callbacks;
+ nghttp3_settings settings;
+ const nghttp3_nv nva[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV(":authority", "example.com"),
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":method", "GET"),
+ };
+ nghttp3_vec vec[256];
+ nghttp3_ssize sveccnt;
+ int rv;
+ int64_t stream_id;
+ nghttp3_data_reader dr;
+ int fin;
+
+ memset(&callbacks, 0, sizeof(callbacks));
+ nghttp3_settings_default(&settings);
+
+ /* Specify NGHTTP3_MAX_VARINT + 1 bytes data in
+ nghttp3_read_data_callback. */
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_bind_qpack_streams(conn, 6, 10);
+
+ dr.read_data = stream_data_overflow_read_data;
+
+ /* QPACK decoder stream */
+ rv = nghttp3_conn_submit_request(conn, 0, nva, nghttp3_arraylen(nva), &dr,
+ NULL);
+
+ CU_ASSERT(0 == rv);
+
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(10 == stream_id);
+ CU_ASSERT(1 == sveccnt);
+
+ nghttp3_conn_add_write_offset(conn, 10, vec[0].len);
+
+ /* QPACK encoder stream */
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(6 == stream_id);
+ CU_ASSERT(1 == sveccnt);
+
+ nghttp3_conn_add_write_offset(conn, 6, vec[0].len);
+
+ /* Write request stream */
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(NGHTTP3_ERR_STREAM_DATA_OVERFLOW == sveccnt);
+
+ nghttp3_conn_del(conn);
+
+ /* nghttp3_stream_outq_add detects stream data overflow */
+ nghttp3_conn_client_new(&conn, &callbacks, &settings, mem, NULL);
+ nghttp3_conn_bind_qpack_streams(conn, 6, 10);
+
+ dr.read_data = stream_data_almost_overflow_read_data;
+
+ /* QPACK decoder stream */
+ rv = nghttp3_conn_submit_request(conn, 0, nva, nghttp3_arraylen(nva), &dr,
+ NULL);
+
+ CU_ASSERT(0 == rv);
+
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(10 == stream_id);
+ CU_ASSERT(1 == sveccnt);
+
+ nghttp3_conn_add_write_offset(conn, 10, vec[0].len);
+
+ /* QPACK encoder stream */
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(6 == stream_id);
+ CU_ASSERT(1 == sveccnt);
+
+ nghttp3_conn_add_write_offset(conn, 6, vec[0].len);
+
+ /* Write request stream */
+ sveccnt = nghttp3_conn_writev_stream(conn, &stream_id, &fin, vec,
+ nghttp3_arraylen(vec));
+
+ CU_ASSERT(NGHTTP3_ERR_STREAM_DATA_OVERFLOW == sveccnt);
+
+ nghttp3_conn_del(conn);
+#endif /* SIZE_MAX > UINT32_MAX */
+}
diff --git a/tests/nghttp3_conn_test.h b/tests/nghttp3_conn_test.h
new file mode 100644
index 0000000..f17327a
--- /dev/null
+++ b/tests/nghttp3_conn_test.h
@@ -0,0 +1,58 @@
+/*
+ * 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 NGTCP2_CONN_TEST_H
+#define NGTCP2_CONN_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_nghttp3_conn_read_control(void);
+void test_nghttp3_conn_write_control(void);
+void test_nghttp3_conn_submit_request(void);
+void test_nghttp3_conn_http_request(void);
+void test_nghttp3_conn_http_resp_header(void);
+void test_nghttp3_conn_http_req_header(void);
+void test_nghttp3_conn_http_content_length(void);
+void test_nghttp3_conn_http_content_length_mismatch(void);
+void test_nghttp3_conn_http_non_final_response(void);
+void test_nghttp3_conn_http_trailers(void);
+void test_nghttp3_conn_http_ignore_content_length(void);
+void test_nghttp3_conn_http_record_request_method(void);
+void test_nghttp3_conn_http_error(void);
+void test_nghttp3_conn_qpack_blocked_stream(void);
+void test_nghttp3_conn_just_fin(void);
+void test_nghttp3_conn_submit_response_read_blocked(void);
+void test_nghttp3_conn_recv_uni(void);
+void test_nghttp3_conn_recv_goaway(void);
+void test_nghttp3_conn_shutdown_server(void);
+void test_nghttp3_conn_shutdown_client(void);
+void test_nghttp3_conn_priority_update(void);
+void test_nghttp3_conn_request_priority(void);
+void test_nghttp3_conn_set_stream_priority(void);
+void test_nghttp3_conn_shutdown_stream_read(void);
+void test_nghttp3_conn_stream_data_overflow(void);
+
+#endif /* NGTCP2_CONN_TEST_H */
diff --git a/tests/nghttp3_conv_test.c b/tests/nghttp3_conv_test.c
new file mode 100644
index 0000000..6db014b
--- /dev/null
+++ b/tests/nghttp3_conv_test.c
@@ -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.
+ */
+#include "nghttp3_conv_test.h"
+
+#include <assert.h>
+
+#include <CUnit/CUnit.h>
+
+#include "nghttp3_conv.h"
+#include "nghttp3_test_helper.h"
+
+void test_nghttp3_pri_to_uint8(void) {
+ {
+ nghttp3_pri pri = {1, 0};
+ CU_ASSERT(1 == nghttp3_pri_to_uint8(&pri));
+ }
+ {
+ nghttp3_pri pri = {1, 1};
+ CU_ASSERT((0x80 | 1) == nghttp3_pri_to_uint8(&pri));
+ }
+ {
+ nghttp3_pri pri = {7, 1};
+ CU_ASSERT((0x80 | 7) == nghttp3_pri_to_uint8(&pri));
+ }
+ {
+ nghttp3_pri pri = {7, 0};
+ CU_ASSERT(7 == nghttp3_pri_to_uint8(&pri));
+ }
+}
diff --git a/tests/nghttp3_conv_test.h b/tests/nghttp3_conv_test.h
new file mode 100644
index 0000000..c1274c0
--- /dev/null
+++ b/tests/nghttp3_conv_test.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+#ifndef NGTCP2_CONV_TEST_H
+#define NGTCP2_CONV_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_nghttp3_pri_to_uint8(void);
+
+#endif /* NGTCP2_CONV_TEST_H */
diff --git a/tests/nghttp3_http_test.c b/tests/nghttp3_http_test.c
new file mode 100644
index 0000000..89aa2e8
--- /dev/null
+++ b/tests/nghttp3_http_test.c
@@ -0,0 +1,676 @@
+/*
+ * 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.
+ */
+#include "nghttp3_http_test.h"
+
+#include <assert.h>
+
+#include <CUnit/CUnit.h>
+
+#include "nghttp3_http.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_test_helper.h"
+
+void test_nghttp3_http_parse_priority(void) {
+ int rv;
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = "";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT((uint32_t)-1 == pri.urgency);
+ CU_ASSERT(-1 == pri.inc);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = "u=7,i";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT((uint32_t)7 == pri.urgency);
+ CU_ASSERT(1 == pri.inc);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = "u=0,i=?0";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT((uint32_t)0 == pri.urgency);
+ CU_ASSERT(0 == pri.inc);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = "u=3, i";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT((uint32_t)3 == pri.urgency);
+ CU_ASSERT(1 == pri.inc);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = "u=0, i, i=?0, u=6";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT((uint32_t)6 == pri.urgency);
+ CU_ASSERT(0 == pri.inc);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = "u=0,";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(NGHTTP3_ERR_INVALID_ARGUMENT == rv);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = "u=0, ";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(NGHTTP3_ERR_INVALID_ARGUMENT == rv);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = "u=";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(NGHTTP3_ERR_INVALID_ARGUMENT == rv);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = "u";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(NGHTTP3_ERR_INVALID_ARGUMENT == rv);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = "i=?1";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT((uint32_t)-1 == pri.urgency);
+ CU_ASSERT(1 == pri.inc);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = "i=?2";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(NGHTTP3_ERR_INVALID_ARGUMENT == rv);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = "i=?";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(NGHTTP3_ERR_INVALID_ARGUMENT == rv);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = "i=";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(NGHTTP3_ERR_INVALID_ARGUMENT == rv);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = "u=-1";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(NGHTTP3_ERR_INVALID_ARGUMENT == rv);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = "u=8";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(NGHTTP3_ERR_INVALID_ARGUMENT == rv);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] =
+ "i=?0, u=1, a=(x y z), u=2; i=?0;foo=\",,,\", i=?1;i=?0; u=6";
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v) - 1);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT((uint32_t)2 == pri.urgency);
+ CU_ASSERT(1 == pri.inc);
+ }
+
+ {
+ nghttp3_pri pri = {(uint32_t)-1, -1};
+ const uint8_t v[] = {'u', '='};
+
+ rv = nghttp3_http_parse_priority(&pri, v, sizeof(v));
+
+ CU_ASSERT(NGHTTP3_ERR_INVALID_ARGUMENT == rv);
+ }
+}
+
+void test_nghttp3_sf_parse_item(void) {
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "?1";
+ val.type = 0xff;
+
+ CU_ASSERT(2 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_BOOLEAN == val.type);
+ CU_ASSERT(1 == val.b);
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "?1 ";
+ val.type = 0xff;
+
+ CU_ASSERT(2 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_BOOLEAN == val.type);
+ CU_ASSERT(1 == val.b);
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "?1;foo=bar";
+ val.type = 0xff;
+
+ CU_ASSERT(10 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_BOOLEAN == val.type);
+ CU_ASSERT(1 == val.b);
+ }
+
+ {
+ const uint8_t s[] = {'?', '1', ';', 'f', 'o', 'o', '='};
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s)));
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "?0";
+ val.type = 0xff;
+
+ CU_ASSERT(2 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_BOOLEAN == val.type);
+ CU_ASSERT(0 == val.b);
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "?0 ";
+ val.type = 0xff;
+
+ CU_ASSERT(2 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_BOOLEAN == val.type);
+ CU_ASSERT(0 == val.b);
+ }
+
+ {
+ const uint8_t s[] = "?2";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = "?";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = "?1";
+
+ CU_ASSERT(2 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = ":cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==:";
+ val.type = 0xff;
+
+ CU_ASSERT(46 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_BYTESEQ == val.type);
+ CU_ASSERT(44 == val.s.len);
+ CU_ASSERT(0 == memcmp("cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==",
+ val.s.base, val.s.len));
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = ":cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==: ";
+ val.type = 0xff;
+
+ CU_ASSERT(46 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_BYTESEQ == val.type);
+ CU_ASSERT(44 == val.s.len);
+ CU_ASSERT(0 == memcmp("cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==",
+ val.s.base, val.s.len));
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "::";
+ val.type = 0xff;
+
+ CU_ASSERT(2 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_BYTESEQ == val.type);
+ CU_ASSERT(0 == val.s.len);
+ }
+
+ {
+ const uint8_t s[] = ":cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = ":";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = ":@:";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = ":foo:";
+
+ CU_ASSERT(5 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] =
+ ":abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=:";
+ val.type = 0xff;
+
+ CU_ASSERT(67 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_BYTESEQ == val.type);
+ CU_ASSERT(65 == val.s.len);
+ CU_ASSERT(
+ 0 ==
+ memcmp(
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=",
+ val.s.base, val.s.len));
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "foo123/456";
+ val.type = 0xff;
+
+ CU_ASSERT(10 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_TOKEN == val.type);
+ CU_ASSERT(10 == val.s.len);
+ CU_ASSERT(0 == memcmp(s, val.s.base, val.s.len));
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "foo123/456 ";
+ val.type = 0xff;
+
+ CU_ASSERT(10 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_TOKEN == val.type);
+ CU_ASSERT(10 == val.s.len);
+ CU_ASSERT(0 == memcmp(s, val.s.base, val.s.len));
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "*";
+ val.type = 0xff;
+
+ CU_ASSERT(1 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_TOKEN == val.type);
+ CU_ASSERT(1 == val.s.len);
+ CU_ASSERT(0 == memcmp(s, val.s.base, val.s.len));
+ }
+
+ {
+ const uint8_t s[] = "*";
+
+ CU_ASSERT(1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "\"hello world\"";
+ val.type = 0xff;
+
+ CU_ASSERT(13 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_STRING == val.type);
+ CU_ASSERT(11 == val.s.len);
+ CU_ASSERT(0 == memcmp("hello world", val.s.base, val.s.len));
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "\"hello world\" ";
+ val.type = 0xff;
+
+ CU_ASSERT(13 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_STRING == val.type);
+ CU_ASSERT(11 == val.s.len);
+ CU_ASSERT(0 == memcmp("hello world", val.s.base, val.s.len));
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "\"foo\\\"\\\\\"";
+ val.type = 0xff;
+
+ CU_ASSERT(9 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_STRING == val.type);
+ CU_ASSERT(7 == val.s.len);
+ CU_ASSERT(0 == memcmp("foo\\\"\\\\", val.s.base, val.s.len));
+ }
+
+ {
+ const uint8_t s[] = "\"foo\\x\"";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = "\"foo";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = "\"\x7f\"";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = "\"\x1f\"";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = "\"foo\"";
+
+ CU_ASSERT(5 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "4.5";
+ val.type = NGHTTP3_SF_VALUE_TYPE_DECIMAL;
+
+ CU_ASSERT(3 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_DECIMAL == val.type);
+ CU_ASSERT(fabs(4.5 - val.d) < 1e-9);
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "4.5 ";
+ val.type = NGHTTP3_SF_VALUE_TYPE_DECIMAL;
+
+ CU_ASSERT(3 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_DECIMAL == val.type);
+ CU_ASSERT(fabs(4.5 - val.d) < 1e-9);
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "-4.5";
+ val.type = NGHTTP3_SF_VALUE_TYPE_DECIMAL;
+
+ CU_ASSERT(4 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_DECIMAL == val.type);
+ CU_ASSERT(fabs(-4.5 - val.d) < 1e-9);
+ }
+
+ {
+ const uint8_t s[] = "4.5";
+
+ CU_ASSERT(3 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "123456789012.123";
+ val.type = NGHTTP3_SF_VALUE_TYPE_DECIMAL;
+
+ CU_ASSERT(16 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_DECIMAL == val.type);
+ CU_ASSERT(fabs(123456789012.123 - val.d) < 1e-9);
+ }
+
+ {
+ const uint8_t s[] = "1123456789012.123";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = "123456789012.1234";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = "1.";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "123456789012345";
+ val.type = NGHTTP3_SF_VALUE_TYPE_DECIMAL;
+
+ CU_ASSERT(15 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_INTEGER == val.type);
+ CU_ASSERT(123456789012345 == val.i);
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "1 ";
+ val.type = NGHTTP3_SF_VALUE_TYPE_DECIMAL;
+
+ CU_ASSERT(1 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_INTEGER == val.type);
+ CU_ASSERT(1 == val.i);
+ }
+
+ {
+ const uint8_t s[] = "1";
+
+ CU_ASSERT(1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = "1234567890123456";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "\"foo\";a; b=\"bar\";c=1.3;d=9;e=baz;f=:aaa:";
+ val.type = 0xff;
+
+ CU_ASSERT(41 == nghttp3_sf_parse_item(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_STRING == val.type);
+ CU_ASSERT(0 == memcmp("foo", val.s.base, val.s.len));
+ }
+
+ {
+ const uint8_t s[] = "\"foo\";a; b=\"bar";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = "foo;";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_item(NULL, s, s + sizeof(s) - 1));
+ }
+}
+
+void test_nghttp3_sf_parse_inner_list(void) {
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "()";
+ val.type = 0xff;
+
+ CU_ASSERT(2 == nghttp3_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_INNER_LIST == val.type);
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "( )";
+ val.type = 0xff;
+
+ CU_ASSERT(7 == nghttp3_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_INNER_LIST == val.type);
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "(a)";
+ val.type = 0xff;
+
+ CU_ASSERT(3 == nghttp3_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_INNER_LIST == val.type);
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "(a b)";
+ val.type = 0xff;
+
+ CU_ASSERT(5 == nghttp3_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_INNER_LIST == val.type);
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "( a b )";
+ val.type = 0xff;
+
+ CU_ASSERT(10 == nghttp3_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_INNER_LIST == val.type);
+ }
+
+ {
+ nghttp3_sf_value val;
+ const uint8_t s[] = "( a;foo=bar)";
+ val.type = 0xff;
+
+ CU_ASSERT(12 == nghttp3_sf_parse_inner_list(&val, s, s + sizeof(s) - 1));
+ CU_ASSERT(NGHTTP3_SF_VALUE_TYPE_INNER_LIST == val.type);
+ }
+
+ {
+ const uint8_t s[] = "(";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = "(a";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = "(a ";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
+ }
+
+ {
+ const uint8_t s[] = "(a;b";
+
+ CU_ASSERT(-1 == nghttp3_sf_parse_inner_list(NULL, s, s + sizeof(s) - 1));
+ }
+}
+
+#define check_header_value(S) \
+ nghttp3_check_header_value((const uint8_t *)S, sizeof(S) - 1)
+
+void test_nghttp3_check_header_value(void) {
+ uint8_t goodval[] = {'a', 'b', 0x80u, 'c', 0xffu, 'd'};
+ uint8_t badval1[] = {'a', 0x1fu, 'b'};
+ uint8_t badval2[] = {'a', 0x7fu, 'b'};
+
+ CU_ASSERT(check_header_value("!|}~"));
+ CU_ASSERT(!check_header_value(" !|}~"));
+ CU_ASSERT(!check_header_value("!|}~ "));
+ CU_ASSERT(!check_header_value("\t!|}~"));
+ CU_ASSERT(!check_header_value("!|}~\t"));
+ CU_ASSERT(check_header_value(goodval));
+ CU_ASSERT(!check_header_value(badval1));
+ CU_ASSERT(!check_header_value(badval2));
+ CU_ASSERT(check_header_value(""));
+ CU_ASSERT(!check_header_value(" "));
+ CU_ASSERT(!check_header_value("\t"));
+}
diff --git a/tests/nghttp3_http_test.h b/tests/nghttp3_http_test.h
new file mode 100644
index 0000000..47f902c
--- /dev/null
+++ b/tests/nghttp3_http_test.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+#ifndef NGTCP2_HTTP_TEST_H
+#define NGTCP2_HTTP_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_nghttp3_http_parse_priority(void);
+void test_nghttp3_sf_parse_item(void);
+void test_nghttp3_sf_parse_inner_list(void);
+void test_nghttp3_check_header_value(void);
+
+#endif /* NGTCP2_CONN_TEST_H */
diff --git a/tests/nghttp3_qpack_test.c b/tests/nghttp3_qpack_test.c
new file mode 100644
index 0000000..c8e1983
--- /dev/null
+++ b/tests/nghttp3_qpack_test.c
@@ -0,0 +1,779 @@
+/*
+ * 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_qpack_test.h"
+
+#include <stdlib.h>
+
+#include <CUnit/CUnit.h>
+
+#include "nghttp3_qpack.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_test_helper.h"
+
+static void check_decode_header(nghttp3_qpack_decoder *dec, nghttp3_buf *pbuf,
+ nghttp3_buf *rbuf, nghttp3_buf *ebuf,
+ int64_t stream_id, const nghttp3_nv *nva,
+ size_t nvlen, const nghttp3_mem *mem) {
+ nghttp3_ssize nread;
+ nghttp3_qpack_stream_context sctx;
+ nghttp3_qpack_nv qnv;
+ const nghttp3_nv *nv;
+ uint8_t flags;
+ size_t i = 0;
+
+ nread =
+ nghttp3_qpack_decoder_read_encoder(dec, ebuf->pos, nghttp3_buf_len(ebuf));
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(ebuf) == nread);
+
+ nghttp3_qpack_stream_context_init(&sctx, stream_id, mem);
+
+ nread = nghttp3_qpack_decoder_read_request(
+ dec, &sctx, &qnv, &flags, pbuf->pos, nghttp3_buf_len(pbuf), 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(pbuf) == nread);
+
+ for (; nghttp3_buf_len(rbuf);) {
+ nread = nghttp3_qpack_decoder_read_request(
+ dec, &sctx, &qnv, &flags, rbuf->pos, nghttp3_buf_len(rbuf), 1);
+
+ CU_ASSERT(nread > 0);
+
+ if (nread < 0) {
+ break;
+ }
+
+ rbuf->pos += nread;
+
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_FINAL) {
+ break;
+ }
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT) {
+ nv = &nva[i++];
+ CU_ASSERT(nv->namelen == qnv.name->len);
+ CU_ASSERT(0 == memcmp(nv->name, qnv.name->base, nv->namelen));
+ CU_ASSERT(nv->valuelen == qnv.value->len);
+ CU_ASSERT(0 == memcmp(nv->value, qnv.value->base, nv->valuelen));
+
+ nghttp3_rcbuf_decref(qnv.name);
+ nghttp3_rcbuf_decref(qnv.value);
+ }
+ }
+
+ CU_ASSERT(i == nvlen);
+
+ nghttp3_qpack_stream_context_free(&sctx);
+ nghttp3_buf_reset(pbuf);
+ nghttp3_buf_reset(rbuf);
+ nghttp3_buf_reset(ebuf);
+}
+
+static void decode_header_block(nghttp3_qpack_decoder *dec, nghttp3_buf *pbuf,
+ nghttp3_buf *rbuf, int64_t stream_id,
+ const nghttp3_mem *mem) {
+ nghttp3_ssize nread;
+ nghttp3_qpack_stream_context sctx;
+ nghttp3_qpack_nv qnv;
+ uint8_t flags;
+
+ nghttp3_qpack_stream_context_init(&sctx, stream_id, mem);
+
+ nread = nghttp3_qpack_decoder_read_request(
+ dec, &sctx, &qnv, &flags, pbuf->pos, nghttp3_buf_len(pbuf), 0);
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(pbuf) == nread);
+
+ for (;;) {
+ nread = nghttp3_qpack_decoder_read_request(
+ dec, &sctx, &qnv, &flags, rbuf->pos, nghttp3_buf_len(rbuf), 1);
+
+ CU_ASSERT(nread >= 0);
+
+ if (nread < 0) {
+ break;
+ }
+
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_FINAL) {
+ CU_ASSERT(nread == 0);
+ CU_ASSERT(!(flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT));
+ CU_ASSERT(0 == nghttp3_buf_len(rbuf));
+ CU_ASSERT(nghttp3_buf_len(&dec->dbuf) > 0);
+
+ break;
+ }
+
+ CU_ASSERT(nread > 0);
+ CU_ASSERT(flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT);
+
+ nghttp3_rcbuf_decref(qnv.name);
+ nghttp3_rcbuf_decref(qnv.value);
+
+ rbuf->pos += nread;
+ }
+
+ nghttp3_qpack_stream_context_free(&sctx);
+}
+
+void test_nghttp3_qpack_encoder_encode(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_qpack_encoder enc;
+ nghttp3_qpack_decoder dec;
+ nghttp3_nv nva[] = {
+ MAKE_NV(":path", "/rsrc.php/v3/yn/r/rIPZ9Qkrdd9.png"),
+ MAKE_NV(":authority", "static.xx.fbcdn.net"),
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":method", "GET"),
+ MAKE_NV("accept-encoding", "gzip, deflate, br"),
+ MAKE_NV("accept-language", "en-US,en;q=0.9"),
+ MAKE_NV(
+ "user-agent",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36(KHTML, "
+ "like Gecko) Chrome/63.0.3239.70 Safari/537.36"),
+ MAKE_NV("accept", "image/webp,image/apng,image/*,*/*;q=0.8"),
+ MAKE_NV("referer", "https://static.xx.fbcdn.net/rsrc.php/v3/yT/l/0,cross/"
+ "dzXGESIlGQQ.css"),
+ };
+ int rv;
+ nghttp3_buf pbuf, rbuf, ebuf;
+ nghttp3_qpack_stream *stream;
+ nghttp3_qpack_header_block_ref *ref;
+
+ nghttp3_buf_init(&pbuf);
+ nghttp3_buf_init(&rbuf);
+ nghttp3_buf_init(&ebuf);
+ rv = nghttp3_qpack_encoder_init(&enc, 4096, mem);
+
+ CU_ASSERT(0 == rv);
+
+ nghttp3_qpack_encoder_set_max_blocked_streams(&enc, 1);
+
+ nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 4096);
+
+ rv = nghttp3_qpack_decoder_init(&dec, 4096, 1, mem);
+
+ CU_ASSERT(0 == rv);
+
+ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 0, nva,
+ nghttp3_arraylen(nva));
+
+ CU_ASSERT(0 == rv);
+
+ stream = nghttp3_qpack_encoder_find_stream(&enc, 0);
+
+ CU_ASSERT(NULL != stream);
+ CU_ASSERT(nghttp3_qpack_encoder_stream_is_blocked(&enc, stream));
+ CU_ASSERT(1 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+
+ ref =
+ *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0);
+
+ CU_ASSERT(5 == ref->max_cnt);
+ CU_ASSERT(1 == ref->min_cnt);
+ CU_ASSERT(5 == nghttp3_qpack_stream_get_max_cnt(stream));
+ CU_ASSERT(1 == nghttp3_qpack_encoder_get_min_cnt(&enc));
+ CU_ASSERT(2 == nghttp3_buf_len(&pbuf));
+
+ check_decode_header(&dec, &pbuf, &rbuf, &ebuf, 0, nva, nghttp3_arraylen(nva),
+ mem);
+
+ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 4, nva,
+ nghttp3_arraylen(nva));
+
+ CU_ASSERT(0 == rv);
+
+ stream = nghttp3_qpack_encoder_find_stream(&enc, 4);
+
+ CU_ASSERT(NULL == stream);
+
+ check_decode_header(&dec, &pbuf, &rbuf, &ebuf, 4, nva, nghttp3_arraylen(nva),
+ mem);
+
+ nghttp3_qpack_encoder_ack_header(&enc, 0);
+
+ CU_ASSERT(5 == enc.krcnt);
+
+ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 8, nva,
+ nghttp3_arraylen(nva));
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+
+ check_decode_header(&dec, &pbuf, &rbuf, &ebuf, 8, nva, nghttp3_arraylen(nva),
+ mem);
+
+ nghttp3_qpack_decoder_free(&dec);
+ nghttp3_qpack_encoder_free(&enc);
+ nghttp3_buf_free(&ebuf, mem);
+ nghttp3_buf_free(&rbuf, mem);
+ nghttp3_buf_free(&pbuf, mem);
+}
+
+void test_nghttp3_qpack_encoder_still_blocked(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_qpack_encoder enc;
+ nghttp3_nv nva1[] = {
+ MAKE_NV(":status", "103"),
+ MAKE_NV("link", "foo"),
+ };
+ nghttp3_nv nva2[] = {
+ MAKE_NV(":status", "200"),
+ MAKE_NV("content-type", "text/foo"),
+ };
+ int rv;
+ nghttp3_buf pbuf, rbuf, ebuf;
+ nghttp3_qpack_stream *stream;
+ nghttp3_qpack_header_block_ref *ref;
+
+ nghttp3_buf_init(&pbuf);
+ nghttp3_buf_init(&rbuf);
+ nghttp3_buf_init(&ebuf);
+ rv = nghttp3_qpack_encoder_init(&enc, 4096, mem);
+
+ CU_ASSERT(0 == rv);
+
+ nghttp3_qpack_encoder_set_max_blocked_streams(&enc, 1);
+
+ nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 4096);
+
+ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 0, nva1,
+ nghttp3_arraylen(nva1));
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+
+ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 0, nva2,
+ nghttp3_arraylen(nva2));
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+
+ stream = nghttp3_qpack_encoder_find_stream(&enc, 0);
+
+ ref =
+ *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0);
+
+ CU_ASSERT(nghttp3_ringbuf_len(&stream->refs) > 1);
+ CU_ASSERT(ref->max_cnt != nghttp3_qpack_stream_get_max_cnt(stream));
+
+ ref =
+ *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 1);
+
+ CU_ASSERT(ref->max_cnt == nghttp3_qpack_stream_get_max_cnt(stream));
+
+ nghttp3_qpack_encoder_ack_header(&enc, 0);
+
+ CU_ASSERT(1 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+
+ stream = nghttp3_qpack_encoder_find_stream(&enc, 0);
+
+ ref =
+ *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0);
+
+ CU_ASSERT(1 == nghttp3_ringbuf_len(&stream->refs));
+ CU_ASSERT(ref->max_cnt == nghttp3_qpack_stream_get_max_cnt(stream));
+
+ nghttp3_qpack_encoder_ack_header(&enc, 0);
+
+ CU_ASSERT(0 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+ CU_ASSERT(NULL == nghttp3_qpack_encoder_find_stream(&enc, 0));
+
+ nghttp3_qpack_encoder_free(&enc);
+ nghttp3_buf_free(&ebuf, mem);
+ nghttp3_buf_free(&rbuf, mem);
+ nghttp3_buf_free(&pbuf, mem);
+}
+
+void test_nghttp3_qpack_encoder_set_dtable_cap(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_qpack_encoder enc;
+ nghttp3_qpack_decoder dec;
+ nghttp3_buf pbuf, rbuf, ebuf;
+ const nghttp3_nv nva1[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV("date", "bar1"),
+ };
+ const nghttp3_nv nva2[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV("vary", "bar2"),
+ };
+ int rv;
+ nghttp3_ssize nread;
+
+ nghttp3_buf_init(&pbuf);
+ nghttp3_buf_init(&rbuf);
+ nghttp3_buf_init(&ebuf);
+
+ rv = nghttp3_qpack_encoder_init(&enc, 4096, mem);
+
+ CU_ASSERT(0 == rv);
+
+ nghttp3_qpack_encoder_set_max_blocked_streams(&enc, 3);
+
+ nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 4096);
+
+ rv = nghttp3_qpack_decoder_init(&dec, 4096, 3, mem);
+
+ CU_ASSERT(0 == rv);
+
+ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 0, nva1,
+ nghttp3_arraylen(nva1));
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == enc.ctx.next_absidx);
+ CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD ==
+ enc.ctx.dtable_size);
+
+ nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos,
+ nghttp3_buf_len(&ebuf));
+
+ CU_ASSERT(nread == (nghttp3_ssize)nghttp3_buf_len(&ebuf));
+ CU_ASSERT(1 == dec.ctx.next_absidx);
+ CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD ==
+ dec.ctx.dtable_size);
+ CU_ASSERT(4096 == dec.ctx.max_dtable_capacity);
+
+ decode_header_block(&dec, &pbuf, &rbuf, 0, mem);
+ nghttp3_buf_reset(&pbuf);
+ nghttp3_buf_reset(&rbuf);
+ nghttp3_buf_reset(&ebuf);
+
+ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 4, nva2,
+ nghttp3_arraylen(nva2));
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD +
+ strlen("vary") + strlen("bar2") +
+ NGHTTP3_QPACK_ENTRY_OVERHEAD ==
+ enc.ctx.dtable_size);
+ CU_ASSERT(2 == enc.ctx.next_absidx);
+ CU_ASSERT(2 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+
+ nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos,
+ nghttp3_buf_len(&ebuf));
+
+ CU_ASSERT(nread == (nghttp3_ssize)nghttp3_buf_len(&ebuf));
+ CU_ASSERT(2 == dec.ctx.next_absidx);
+ CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD +
+ strlen("vary") + strlen("bar2") +
+ NGHTTP3_QPACK_ENTRY_OVERHEAD ==
+ dec.ctx.dtable_size);
+ CU_ASSERT(4096 == dec.ctx.max_dtable_capacity);
+
+ decode_header_block(&dec, &pbuf, &rbuf, 4, mem);
+ nghttp3_buf_reset(&pbuf);
+ nghttp3_buf_reset(&rbuf);
+ nghttp3_buf_reset(&ebuf);
+
+ nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 0);
+
+ CU_ASSERT(0 == enc.ctx.max_dtable_capacity);
+ CU_ASSERT(2 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+
+ /* Cannot index more headers because we set max_dtable_capacity to
+ 0. */
+ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 8, nva2,
+ nghttp3_arraylen(nva2));
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == enc.ctx.max_dtable_capacity);
+ CU_ASSERT(2 == enc.ctx.next_absidx);
+ CU_ASSERT(2 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+
+ nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos,
+ nghttp3_buf_len(&ebuf));
+
+ CU_ASSERT(nread == (nghttp3_ssize)nghttp3_buf_len(&ebuf));
+ CU_ASSERT(2 == dec.ctx.next_absidx);
+ CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD +
+ strlen("vary") + strlen("bar2") +
+ NGHTTP3_QPACK_ENTRY_OVERHEAD ==
+ dec.ctx.dtable_size);
+ CU_ASSERT(4096 == dec.ctx.max_dtable_capacity);
+
+ decode_header_block(&dec, &pbuf, &rbuf, 8, mem);
+ nghttp3_buf_reset(&pbuf);
+ nghttp3_buf_reset(&rbuf);
+ nghttp3_buf_reset(&ebuf);
+
+ /* Acking stream 0 will evict first entry */
+ nghttp3_qpack_encoder_ack_header(&enc, 0);
+
+ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 12, nva2,
+ nghttp3_arraylen(nva2));
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(strlen("vary") + strlen("bar2") + NGHTTP3_QPACK_ENTRY_OVERHEAD ==
+ enc.ctx.dtable_size);
+ CU_ASSERT(0 == enc.ctx.max_dtable_capacity);
+ CU_ASSERT(1 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+
+ nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos,
+ nghttp3_buf_len(&ebuf));
+
+ CU_ASSERT(nread == (nghttp3_ssize)nghttp3_buf_len(&ebuf));
+ /* decoder still has 2 entries because encoder does not emit Set
+ Dynamic Table Capacity. */
+ CU_ASSERT(2 == dec.ctx.next_absidx);
+ CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD +
+ strlen("vary") + strlen("bar2") +
+ NGHTTP3_QPACK_ENTRY_OVERHEAD ==
+ dec.ctx.dtable_size);
+ CU_ASSERT(4096 == dec.ctx.max_dtable_capacity);
+
+ decode_header_block(&dec, &pbuf, &rbuf, 12, mem);
+ nghttp3_buf_reset(&pbuf);
+ nghttp3_buf_reset(&rbuf);
+ nghttp3_buf_reset(&ebuf);
+
+ /* Acking stream 4 will evict another entry */
+ nghttp3_qpack_encoder_ack_header(&enc, 4);
+
+ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 16, nva2,
+ nghttp3_arraylen(nva2));
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == enc.ctx.dtable_size);
+ CU_ASSERT(0 == enc.ctx.max_dtable_capacity);
+ CU_ASSERT(0 == enc.last_max_dtable_update);
+ CU_ASSERT(SIZE_MAX == enc.min_dtable_update);
+ CU_ASSERT(0 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+
+ nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos,
+ nghttp3_buf_len(&ebuf));
+
+ CU_ASSERT(nread == (nghttp3_ssize)nghttp3_buf_len(&ebuf));
+ CU_ASSERT(2 == dec.ctx.next_absidx);
+ CU_ASSERT(0 == dec.ctx.dtable_size);
+ CU_ASSERT(0 == dec.ctx.max_dtable_capacity);
+
+ decode_header_block(&dec, &pbuf, &rbuf, 16, mem);
+ nghttp3_buf_reset(&pbuf);
+ nghttp3_buf_reset(&rbuf);
+ nghttp3_buf_reset(&ebuf);
+
+ nghttp3_qpack_decoder_free(&dec);
+ nghttp3_qpack_encoder_free(&enc);
+
+ /* Check that minimum size is emitted */
+ nghttp3_qpack_encoder_init(&enc, 4096, mem);
+ nghttp3_qpack_encoder_set_max_blocked_streams(&enc, 1);
+ nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 4096);
+ nghttp3_qpack_decoder_init(&dec, 4096, 1, mem);
+
+ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 0, nva1,
+ nghttp3_arraylen(nva1));
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == enc.ctx.next_absidx);
+ CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD ==
+ enc.ctx.dtable_size);
+
+ nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos,
+ nghttp3_buf_len(&ebuf));
+
+ CU_ASSERT(nread == (nghttp3_ssize)nghttp3_buf_len(&ebuf));
+ CU_ASSERT(1 == dec.ctx.next_absidx);
+ CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD ==
+ dec.ctx.dtable_size);
+ CU_ASSERT(4096 == dec.ctx.max_dtable_capacity);
+
+ decode_header_block(&dec, &pbuf, &rbuf, 0, mem);
+ nghttp3_buf_reset(&pbuf);
+ nghttp3_buf_reset(&rbuf);
+ nghttp3_buf_reset(&ebuf);
+
+ nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 0);
+ nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 1024);
+
+ CU_ASSERT(0 == enc.min_dtable_update);
+ CU_ASSERT(1024 == enc.last_max_dtable_update);
+
+ nghttp3_qpack_encoder_ack_header(&enc, 0);
+
+ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 4, nva1,
+ nghttp3_arraylen(nva1));
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(2 == enc.ctx.next_absidx);
+ CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD ==
+ enc.ctx.dtable_size);
+ CU_ASSERT(SIZE_MAX == enc.min_dtable_update);
+ CU_ASSERT(1024 == enc.last_max_dtable_update);
+ CU_ASSERT(1024 == enc.ctx.max_dtable_capacity);
+
+ nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos,
+ nghttp3_buf_len(&ebuf));
+
+ CU_ASSERT(nread == (nghttp3_ssize)nghttp3_buf_len(&ebuf));
+ CU_ASSERT(2 == dec.ctx.next_absidx);
+ CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD ==
+ dec.ctx.dtable_size);
+ CU_ASSERT(1024 == dec.ctx.max_dtable_capacity);
+
+ decode_header_block(&dec, &pbuf, &rbuf, 4, mem);
+ nghttp3_buf_reset(&pbuf);
+ nghttp3_buf_reset(&rbuf);
+ nghttp3_buf_reset(&ebuf);
+
+ nghttp3_qpack_decoder_free(&dec);
+ nghttp3_qpack_encoder_free(&enc);
+ nghttp3_buf_free(&ebuf, mem);
+ nghttp3_buf_free(&rbuf, mem);
+ nghttp3_buf_free(&pbuf, mem);
+}
+
+void test_nghttp3_qpack_decoder_feedback(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_qpack_encoder enc;
+ nghttp3_qpack_decoder dec;
+ nghttp3_buf pbuf1, rbuf1, pbuf2, rbuf2, pbuf3, rbuf3, ebuf, dbuf;
+ const nghttp3_nv nva1[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV("date", "bar1"),
+ };
+ const nghttp3_nv nva2[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV("vary", "bar2"),
+ };
+ const nghttp3_nv nva3[] = {
+ MAKE_NV(":path", "/"),
+ MAKE_NV("link", "bar3"),
+ };
+ int rv;
+ nghttp3_ssize nread;
+
+ nghttp3_buf_init(&pbuf1);
+ nghttp3_buf_init(&rbuf1);
+ nghttp3_buf_init(&pbuf2);
+ nghttp3_buf_init(&rbuf2);
+ nghttp3_buf_init(&pbuf3);
+ nghttp3_buf_init(&rbuf3);
+ nghttp3_buf_init(&ebuf);
+ nghttp3_buf_init(&dbuf);
+
+ nghttp3_buf_reserve(&dbuf, 4096, mem);
+
+ rv = nghttp3_qpack_encoder_init(&enc, 4096, mem);
+
+ CU_ASSERT(0 == rv);
+
+ nghttp3_qpack_encoder_set_max_blocked_streams(&enc, 2);
+
+ nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 4096);
+
+ rv = nghttp3_qpack_decoder_init(&dec, 4096, 2, mem);
+
+ CU_ASSERT(0 == rv);
+
+ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf1, &rbuf1, &ebuf, 0, nva1,
+ nghttp3_arraylen(nva1));
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+
+ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf2, &rbuf2, &ebuf, 4, nva2,
+ nghttp3_arraylen(nva2));
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(2 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+
+ nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos,
+ nghttp3_buf_len(&ebuf));
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&ebuf) == nread);
+
+ /* Process stream 4 first */
+ decode_header_block(&dec, &pbuf2, &rbuf2, 4, mem);
+
+ CU_ASSERT(2 == dec.written_icnt);
+
+ nghttp3_qpack_decoder_write_decoder(&dec, &dbuf);
+
+ nread = nghttp3_qpack_encoder_read_decoder(&enc, dbuf.pos,
+ nghttp3_buf_len(&dbuf));
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&dbuf) == nread);
+ /* This will unblock all streams because higher insert count is
+ acknowledged. */
+ CU_ASSERT(0 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+ CU_ASSERT(1 == nghttp3_map_size(&enc.streams));
+ CU_ASSERT(1 == nghttp3_pq_size(&enc.min_cnts));
+ CU_ASSERT(0 == nghttp3_ksl_len(&enc.blocked_streams));
+ CU_ASSERT(2 == enc.krcnt);
+
+ /* Process stream 0 */
+ decode_header_block(&dec, &pbuf1, &rbuf1, 0, mem);
+ nghttp3_buf_reset(&dbuf);
+ nghttp3_qpack_decoder_write_decoder(&dec, &dbuf);
+
+ nread = nghttp3_qpack_encoder_read_decoder(&enc, dbuf.pos,
+ nghttp3_buf_len(&dbuf));
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&dbuf) == nread);
+ CU_ASSERT(0 == nghttp3_map_size(&enc.streams));
+ CU_ASSERT(0 == nghttp3_pq_size(&enc.min_cnts));
+ CU_ASSERT(2 == enc.krcnt);
+
+ /* Encode another header, and then read encoder stream only. Write
+ decoder stream. */
+ nghttp3_buf_reset(&ebuf);
+ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf3, &rbuf3, &ebuf, 8, nva3,
+ nghttp3_arraylen(nva3));
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+
+ nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos,
+ nghttp3_buf_len(&ebuf));
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&ebuf) == nread);
+
+ nghttp3_buf_reset(&dbuf);
+ nghttp3_qpack_decoder_write_decoder(&dec, &dbuf);
+
+ CU_ASSERT(nghttp3_buf_len(&dbuf) > 0);
+ CU_ASSERT(3 == dec.written_icnt);
+
+ nread = nghttp3_qpack_encoder_read_decoder(&enc, dbuf.pos,
+ nghttp3_buf_len(&dbuf));
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&dbuf) == nread);
+ CU_ASSERT(0 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+ CU_ASSERT(1 == nghttp3_map_size(&enc.streams));
+ CU_ASSERT(3 == enc.krcnt);
+
+ /* Cancel stream 8 */
+ rv = nghttp3_qpack_decoder_cancel_stream(&dec, 8);
+
+ CU_ASSERT(0 == rv);
+
+ nghttp3_buf_reset(&dbuf);
+ nghttp3_qpack_decoder_write_decoder(&dec, &dbuf);
+
+ CU_ASSERT(nghttp3_buf_len(&dbuf) > 0);
+
+ nread = nghttp3_qpack_encoder_read_decoder(&enc, dbuf.pos,
+ nghttp3_buf_len(&dbuf));
+
+ CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&dbuf) == nread);
+ CU_ASSERT(0 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc));
+ CU_ASSERT(0 == nghttp3_ksl_len(&enc.blocked_streams));
+ CU_ASSERT(0 == nghttp3_pq_size(&enc.min_cnts));
+ CU_ASSERT(0 == nghttp3_map_size(&enc.streams));
+
+ nghttp3_qpack_decoder_free(&dec);
+ nghttp3_qpack_encoder_free(&enc);
+ nghttp3_buf_free(&dbuf, mem);
+ nghttp3_buf_free(&ebuf, mem);
+ nghttp3_buf_free(&rbuf3, mem);
+ nghttp3_buf_free(&pbuf3, mem);
+ nghttp3_buf_free(&rbuf2, mem);
+ nghttp3_buf_free(&pbuf2, mem);
+ nghttp3_buf_free(&rbuf1, mem);
+ nghttp3_buf_free(&pbuf1, mem);
+}
+
+void test_nghttp3_qpack_decoder_stream_overflow(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_qpack_decoder dec;
+ size_t i;
+ int rv;
+
+ nghttp3_qpack_decoder_init(&dec, 4096, 0, mem);
+
+ for (i = 0;; ++i) {
+ rv = nghttp3_qpack_decoder_cancel_stream(&dec, (int64_t)i);
+ if (rv == NGHTTP3_ERR_QPACK_FATAL) {
+ break;
+ }
+ }
+
+ nghttp3_qpack_decoder_free(&dec);
+}
+
+void test_nghttp3_qpack_huffman(void) {
+ size_t i, j;
+ uint8_t raw[100], ebuf[4096], dbuf[4096];
+ uint8_t *end;
+ nghttp3_qpack_huffman_decode_context ctx;
+ nghttp3_ssize nwrite;
+
+ srand(1000000007);
+
+ for (i = 0; i < 100000; ++i) {
+ for (j = 0; j < sizeof(raw); ++j) {
+ raw[j] = (uint8_t)round(((double)rand() / RAND_MAX * 255));
+ }
+ end = nghttp3_qpack_huffman_encode(ebuf, raw, sizeof(raw));
+
+ nghttp3_qpack_huffman_decode_context_init(&ctx);
+ nwrite =
+ nghttp3_qpack_huffman_decode(&ctx, dbuf, ebuf, (size_t)(end - ebuf), 1);
+ if (nwrite <= 0) {
+ CU_ASSERT(nwrite > 0);
+ continue;
+ }
+ CU_ASSERT((sizeof(raw) == (size_t)nwrite));
+ CU_ASSERT(0 == memcmp(raw, dbuf, sizeof(raw)));
+ }
+}
+
+void test_nghttp3_qpack_huffman_decode_failure_state(void) {
+ nghttp3_qpack_huffman_decode_context ctx;
+ const uint8_t data[] = {0xff, 0xff, 0xff, 0xff};
+ uint8_t buf[4096];
+ nghttp3_ssize nwrite;
+
+ nghttp3_qpack_huffman_decode_context_init(&ctx);
+ nwrite = nghttp3_qpack_huffman_decode(&ctx, buf, data, sizeof(data) - 1, 0);
+
+ CU_ASSERT(0 == nwrite);
+ CU_ASSERT(!nghttp3_qpack_huffman_decode_failure_state(&ctx));
+
+ nwrite = nghttp3_qpack_huffman_decode(&ctx, buf, data, 1, 0);
+
+ CU_ASSERT(0 == nwrite);
+ CU_ASSERT(nghttp3_qpack_huffman_decode_failure_state(&ctx));
+}
+
+void test_nghttp3_qpack_decoder_reconstruct_ricnt(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_qpack_decoder dec;
+ uint64_t ricnt;
+ int rv;
+
+ rv = nghttp3_qpack_decoder_init(&dec, 100, 1, mem);
+
+ CU_ASSERT(0 == rv);
+
+ dec.ctx.next_absidx = 10;
+
+ rv = nghttp3_qpack_decoder_reconstruct_ricnt(&dec, &ricnt, 3);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(8 == ricnt);
+
+ nghttp3_qpack_decoder_free(&dec);
+}
diff --git a/tests/nghttp3_qpack_test.h b/tests/nghttp3_qpack_test.h
new file mode 100644
index 0000000..8f3240c
--- /dev/null
+++ b/tests/nghttp3_qpack_test.h
@@ -0,0 +1,41 @@
+/*
+ * 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 NGTCP2_QPACK_TEST_H
+#define NGTCP2_QPACK_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_nghttp3_qpack_encoder_encode(void);
+void test_nghttp3_qpack_encoder_still_blocked(void);
+void test_nghttp3_qpack_encoder_set_dtable_cap(void);
+void test_nghttp3_qpack_decoder_feedback(void);
+void test_nghttp3_qpack_decoder_stream_overflow(void);
+void test_nghttp3_qpack_huffman(void);
+void test_nghttp3_qpack_huffman_decode_failure_state(void);
+void test_nghttp3_qpack_decoder_reconstruct_ricnt(void);
+
+#endif /* NGTCP2_QPCK_TEST_H */
diff --git a/tests/nghttp3_test_helper.c b/tests/nghttp3_test_helper.c
new file mode 100644
index 0000000..93ebfec
--- /dev/null
+++ b/tests/nghttp3_test_helper.c
@@ -0,0 +1,139 @@
+/*
+ * 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_test_helper.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "nghttp3_str.h"
+#include "nghttp3_conv.h"
+
+void nghttp3_write_frame(nghttp3_buf *dest, nghttp3_frame *fr) {
+ switch (fr->hd.type) {
+ case NGHTTP3_FRAME_SETTINGS:
+ nghttp3_frame_write_settings_len(&fr->hd.length, &fr->settings);
+ dest->last = nghttp3_frame_write_settings(dest->last, &fr->settings);
+ break;
+ case NGHTTP3_FRAME_GOAWAY:
+ nghttp3_frame_write_goaway_len(&fr->hd.length, &fr->goaway);
+ dest->last = nghttp3_frame_write_goaway(dest->last, &fr->goaway);
+ break;
+ case NGHTTP3_FRAME_PRIORITY_UPDATE:
+ case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID:
+ nghttp3_frame_write_priority_update_len(&fr->hd.length,
+ &fr->priority_update);
+ dest->last =
+ nghttp3_frame_write_priority_update(dest->last, &fr->priority_update);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+void nghttp3_write_frame_qpack(nghttp3_buf *dest, nghttp3_qpack_encoder *qenc,
+ int64_t stream_id, nghttp3_frame *fr) {
+ int rv;
+ const nghttp3_nv *nva;
+ size_t nvlen;
+ nghttp3_buf pbuf, rbuf, ebuf;
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ (void)rv;
+
+ switch (fr->hd.type) {
+ case NGHTTP3_FRAME_HEADERS:
+ nva = fr->headers.nva;
+ nvlen = fr->headers.nvlen;
+ break;
+ default:
+ assert(0);
+ abort();
+ }
+
+ nghttp3_buf_init(&pbuf);
+ nghttp3_buf_init(&rbuf);
+ nghttp3_buf_init(&ebuf);
+
+ rv = nghttp3_qpack_encoder_encode(qenc, &pbuf, &rbuf, &ebuf, stream_id, nva,
+ nvlen);
+ assert(0 == rv);
+ assert(0 == nghttp3_buf_len(&ebuf));
+
+ fr->hd.length = (int64_t)(nghttp3_buf_len(&pbuf) + nghttp3_buf_len(&rbuf));
+
+ dest->last = nghttp3_frame_write_hd(dest->last, &fr->hd);
+ dest->last = nghttp3_cpymem(dest->last, pbuf.pos, nghttp3_buf_len(&pbuf));
+ dest->last = nghttp3_cpymem(dest->last, rbuf.pos, nghttp3_buf_len(&rbuf));
+
+ nghttp3_buf_free(&rbuf, mem);
+ nghttp3_buf_free(&pbuf, mem);
+}
+
+void nghttp3_write_frame_qpack_dyn(nghttp3_buf *dest, nghttp3_buf *ebuf,
+ nghttp3_qpack_encoder *qenc,
+ int64_t stream_id, nghttp3_frame *fr) {
+ int rv;
+ const nghttp3_nv *nva;
+ size_t nvlen;
+ nghttp3_buf pbuf, rbuf;
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ (void)rv;
+
+ switch (fr->hd.type) {
+ case NGHTTP3_FRAME_HEADERS:
+ nva = fr->headers.nva;
+ nvlen = fr->headers.nvlen;
+ break;
+ default:
+ assert(0);
+ abort();
+ }
+
+ nghttp3_buf_init(&pbuf);
+ nghttp3_buf_init(&rbuf);
+
+ rv = nghttp3_qpack_encoder_encode(qenc, &pbuf, &rbuf, ebuf, stream_id, nva,
+ nvlen);
+ assert(0 == rv);
+
+ fr->hd.length = (int64_t)(nghttp3_buf_len(&pbuf) + nghttp3_buf_len(&rbuf));
+
+ dest->last = nghttp3_frame_write_hd(dest->last, &fr->hd);
+ dest->last = nghttp3_cpymem(dest->last, pbuf.pos, nghttp3_buf_len(&pbuf));
+ dest->last = nghttp3_cpymem(dest->last, rbuf.pos, nghttp3_buf_len(&rbuf));
+
+ nghttp3_buf_free(&rbuf, mem);
+ nghttp3_buf_free(&pbuf, mem);
+}
+
+void nghttp3_write_frame_data(nghttp3_buf *dest, size_t len) {
+ nghttp3_frame_data fr;
+
+ fr.hd.type = NGHTTP3_FRAME_DATA;
+ fr.hd.length = (int64_t)len;
+
+ dest->last = nghttp3_frame_write_hd(dest->last, &fr.hd);
+ memset(dest->last, 0, len);
+ dest->last += len;
+}
diff --git a/tests/nghttp3_test_helper.h b/tests/nghttp3_test_helper.h
new file mode 100644
index 0000000..6f6516b
--- /dev/null
+++ b/tests/nghttp3_test_helper.h
@@ -0,0 +1,72 @@
+/*
+ * 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_TEST_HELPER
+#define NGHTTP3_TEST_HELPER
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include "nghttp3_buf.h"
+#include "nghttp3_frame.h"
+#include "nghttp3_qpack.h"
+
+#define MAKE_NV(NAME, VALUE) \
+ { \
+ (uint8_t *)(NAME), (uint8_t *)(VALUE), sizeof((NAME)) - 1, \
+ sizeof((VALUE)) - 1, NGHTTP3_NV_FLAG_NONE \
+ }
+
+/*
+ * nghttp3_write_frame writes |fr| to |dest|. This function
+ * calculates the payload length and assigns it to fr->hd.length;
+ */
+void nghttp3_write_frame(nghttp3_buf *dest, nghttp3_frame *fr);
+
+/*
+ * nghttp3_write_frame_qpack writes |fr| to |dest|. |fr| is supposed
+ * to be a frame which uses QPACK encoder |qenc|. |qenc| must be
+ * configured so that it does not use dynamic table. This function
+ * calculates the payload length and assigns it to fr->hd.length;
+ */
+void nghttp3_write_frame_qpack(nghttp3_buf *dest, nghttp3_qpack_encoder *qenc,
+ int64_t stream_id, nghttp3_frame *fr);
+
+/*
+ * nghttp3_write_frame_qpack_dyn is similar to
+ * nghttp3_write_frame_qpack but it can use dynamic table. The it
+ * will write encoder stream to |ebuf|.
+ */
+void nghttp3_write_frame_qpack_dyn(nghttp3_buf *dest, nghttp3_buf *ebuf,
+ nghttp3_qpack_encoder *qenc,
+ int64_t stream_id, nghttp3_frame *fr);
+
+/*
+ * nghttp3_write_frame_data writes DATA frame which has |len| bytes of
+ * payload.
+ */
+void nghttp3_write_frame_data(nghttp3_buf *dest, size_t len);
+
+#endif /* NGHTTP3_TEST_HELPER */
diff --git a/tests/nghttp3_tnode_test.c b/tests/nghttp3_tnode_test.c
new file mode 100644
index 0000000..831a7d3
--- /dev/null
+++ b/tests/nghttp3_tnode_test.c
@@ -0,0 +1,164 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nghttp3_tnode_test.h"
+
+#include <CUnit/CUnit.h>
+
+#include "nghttp3_tnode.h"
+#include "nghttp3_macro.h"
+#include "nghttp3_test_helper.h"
+
+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;
+}
+
+void test_nghttp3_tnode_schedule(void) {
+ const nghttp3_mem *mem = nghttp3_mem_default();
+ nghttp3_tnode node, node2;
+ nghttp3_pq pq;
+ int rv;
+ nghttp3_pq_entry *ent;
+ nghttp3_tnode *p;
+
+ /* Schedule node with incremental enabled */
+ nghttp3_tnode_init(&node, 0, (1 << 7) | NGHTTP3_DEFAULT_URGENCY);
+
+ nghttp3_pq_init(&pq, cycle_less, mem);
+
+ rv = nghttp3_tnode_schedule(&node, &pq, 0);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == node.cycle);
+
+ /* Schedule another node */
+ nghttp3_tnode_init(&node2, 1, (1 << 7) | NGHTTP3_DEFAULT_URGENCY);
+
+ rv = nghttp3_tnode_schedule(&node2, &pq, 0);
+
+ CU_ASSERT(0 == rv);
+
+ /* Rescheduling node with nwrite > 0 */
+
+ rv = nghttp3_tnode_schedule(&node, &pq, 1000);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == node.cycle);
+
+ /* Rescheduling node with nwrite == 0 */
+
+ rv = nghttp3_tnode_schedule(&node, &pq, 0);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(1 == node.cycle);
+
+ nghttp3_pq_free(&pq);
+
+ /* Schedule node without incremental */
+ nghttp3_tnode_init(&node, 0, NGHTTP3_DEFAULT_URGENCY);
+
+ nghttp3_pq_init(&pq, cycle_less, mem);
+
+ rv = nghttp3_tnode_schedule(&node, &pq, 0);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == node.cycle);
+
+ /* Schedule another node */
+ nghttp3_tnode_init(&node2, 1, NGHTTP3_DEFAULT_URGENCY);
+
+ rv = nghttp3_tnode_schedule(&node2, &pq, 0);
+
+ CU_ASSERT(0 == rv);
+
+ /* Rescheduling node with nwrite > 0 */
+
+ rv = nghttp3_tnode_schedule(&node, &pq, 1000);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == node.cycle);
+
+ /* Rescheduling node with nwrit == 0 */
+
+ rv = nghttp3_tnode_schedule(&node, &pq, 0);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(0 == node.cycle);
+
+ nghttp3_pq_free(&pq);
+
+ /* Stream with lower stream ID takes precedence */
+ nghttp3_pq_init(&pq, cycle_less, mem);
+
+ nghttp3_tnode_init(&node2, 1, NGHTTP3_DEFAULT_URGENCY);
+
+ rv = nghttp3_tnode_schedule(&node2, &pq, 0);
+
+ CU_ASSERT(0 == rv);
+
+ nghttp3_tnode_init(&node, 0, NGHTTP3_DEFAULT_URGENCY);
+
+ rv = nghttp3_tnode_schedule(&node, &pq, 0);
+
+ CU_ASSERT(0 == rv);
+
+ ent = nghttp3_pq_top(&pq);
+
+ p = nghttp3_struct_of(ent, nghttp3_tnode, pe);
+
+ CU_ASSERT(0 == p->id);
+
+ nghttp3_pq_free(&pq);
+
+ /* Check the same reversing push order */
+ nghttp3_pq_init(&pq, cycle_less, mem);
+
+ nghttp3_tnode_init(&node, 0, NGHTTP3_DEFAULT_URGENCY);
+
+ rv = nghttp3_tnode_schedule(&node, &pq, 0);
+
+ CU_ASSERT(0 == rv);
+
+ nghttp3_tnode_init(&node2, 1, NGHTTP3_DEFAULT_URGENCY);
+
+ rv = nghttp3_tnode_schedule(&node2, &pq, 0);
+
+ CU_ASSERT(0 == rv);
+
+ ent = nghttp3_pq_top(&pq);
+
+ p = nghttp3_struct_of(ent, nghttp3_tnode, pe);
+
+ CU_ASSERT(0 == p->id);
+
+ nghttp3_pq_free(&pq);
+}
diff --git a/tests/nghttp3_tnode_test.h b/tests/nghttp3_tnode_test.h
new file mode 100644
index 0000000..7e4db02
--- /dev/null
+++ b/tests/nghttp3_tnode_test.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 NGTCP2_TNODE_TEST_H
+#define NGTCP2_TNODE_TEST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+void test_nghttp3_tnode_schedule(void);
+
+#endif /* NGTCP2_TNODE_TEST_H */