summaryrefslogtreecommitdiffstats
path: root/doc
diff options
context:
space:
mode:
Diffstat (limited to 'doc')
-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
9 files changed, 858 insertions, 0 deletions
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.