summaryrefslogtreecommitdiffstats
path: root/doc/sources
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 07:34:15 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 07:34:15 +0000
commitafea5f9539cbf1eeaa85ec77d79eb2f59724f470 (patch)
tree2a4eba394a6bc60d2eaa8304d91168a07225d51e /doc/sources
parentInitial commit. (diff)
downloadnghttp2-c470e86229244babcd48aa37cc3d46161df32321.tar.xz
nghttp2-c470e86229244babcd48aa37cc3d46161df32321.zip
Adding upstream version 1.52.0.upstream/1.52.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'doc/sources')
-rw-r--r--doc/sources/building-android-binary.rst127
-rw-r--r--doc/sources/contribute.rst56
-rw-r--r--doc/sources/h2load-howto.rst142
-rw-r--r--doc/sources/index.rst50
-rw-r--r--doc/sources/nghttpx-howto.rst642
-rw-r--r--doc/sources/security.rst38
-rw-r--r--doc/sources/tutorial-client.rst498
-rw-r--r--doc/sources/tutorial-hpack.rst126
-rw-r--r--doc/sources/tutorial-server.rst625
9 files changed, 2304 insertions, 0 deletions
diff --git a/doc/sources/building-android-binary.rst b/doc/sources/building-android-binary.rst
new file mode 100644
index 0000000..339ac44
--- /dev/null
+++ b/doc/sources/building-android-binary.rst
@@ -0,0 +1,127 @@
+Building Android binary
+=======================
+
+In this article, we briefly describe how to build Android binary using
+`Android NDK <https://developer.android.com/ndk>`_ cross-compiler on
+Debian Linux.
+
+The easiest way to build android binary is use Dockerfile.android.
+See Dockerfile.android for more details. If you cannot use
+Dockerfile.android for whatever reason, continue to read the rest of
+this article.
+
+We offer ``android-config`` script to make the build easier. To make
+the script work, NDK directory must be set to ``NDK`` environment
+variable. NDK directory is the directory where NDK is unpacked:
+
+.. code-block:: text
+
+ $ unzip android-ndk-$NDK_VERSION-linux.zip
+ $ cd android-ndk-$NDK_VERSION
+ $ export NDK=$PWD
+
+The dependent libraries, such as OpenSSL, libev, and c-ares should be
+built with the same NDK toolchain and installed under
+``$NDK/usr/local``. We recommend to build these libraries as static
+library to make the deployment easier. libxml2 support is currently
+disabled.
+
+Although zlib comes with Android NDK, it seems not to be a part of
+public API, so we have to built it for our own. That also provides us
+proper .pc file as a bonus.
+
+Before running ``android-config``, ``NDK`` environment variable must
+be set to point to the correct path.
+
+You need to set ``NGHTTP2`` environment variable to the absolute path
+to the source directory of nghttp2.
+
+To configure OpenSSL, use the following script:
+
+.. code-block:: sh
+
+ #!/bin/sh
+
+ . $NGHTTP2/android-env
+
+ export ANDROID_NDK_HOME=$NDK
+ export PATH=$TOOLCHAIN/bin:$PATH
+
+ ./Configure no-shared --prefix=$PREFIX android-arm64
+
+And run the following script to build and install without
+documentation:
+
+.. code-block:: sh
+
+ #!/bin/sh
+
+ . $NGHTTP2/android-env
+
+ export PATH=$TOOLCHAIN/bin:$PATH
+
+ make install_sw
+
+To configure libev, use the following script:
+
+.. code-block:: sh
+
+ #!/bin/sh
+
+ . $NGHTTP2/android-env
+
+ ./configure \
+ --host=$TARGET \
+ --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
+ --prefix=$PREFIX \
+ --disable-shared \
+ --enable-static \
+ CPPFLAGS=-I$PREFIX/include \
+ LDFLAGS=-L$PREFIX/lib
+
+And run ``make install`` to build and install.
+
+To configure c-ares, use the following script:
+
+.. code-block:: sh
+
+ #!/bin/sh -e
+
+ . $NGHTTP2/android-env
+
+ ./configure \
+ --host=$TARGET \
+ --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
+ --prefix=$PREFIX \
+ --disable-shared
+
+And run ``make install`` to build and install.
+
+To configure zlib, use the following script:
+
+.. code-block:: sh
+
+ #!/bin/sh -e
+
+ . $NGHTTP2/android-env
+
+ export HOST=$TARGET
+
+ ./configure \
+ --prefix=$PREFIX \
+ --libdir=$PREFIX/lib \
+ --includedir=$PREFIX/include \
+ --static
+
+And run ``make install`` to build and install.
+
+After prerequisite libraries are prepared, run ``android-config`` and
+then ``make`` to compile nghttp2 source files.
+
+If all went well, application binaries, such as nghttpx, are created
+under src directory. Strip debugging information from the binary
+using the following command:
+
+.. code-block:: text
+
+ $ $NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip src/nghttpx
diff --git a/doc/sources/contribute.rst b/doc/sources/contribute.rst
new file mode 100644
index 0000000..32f43c8
--- /dev/null
+++ b/doc/sources/contribute.rst
@@ -0,0 +1,56 @@
+Contribution Guidelines
+=======================
+
+[This text was composed based on 1.2. License section of curl/libcurl
+project.]
+
+When contributing with code, you agree to put your changes and new
+code under the same license nghttp2 is already using unless stated and
+agreed otherwise.
+
+When changing existing source code, you do not alter the copyright of
+the original file(s). The copyright will still be owned by the
+original creator(s) or those who have been assigned copyright by the
+original author(s).
+
+By submitting a patch to the nghttp2 project, you are assumed to have
+the right to the code and to be allowed by your employer or whatever
+to hand over that patch/code to us. We will credit you for your
+changes as far as possible, to give credit but also to keep a trace
+back to who made what changes. Please always provide us with your
+full real name when contributing!
+
+Coding style
+------------
+
+We use clang-format to format source code consistently. The
+clang-format configuration file .clang-format is located at the root
+directory. Since clang-format produces slightly different results
+between versions, we currently use clang-format-14.
+
+To detect any violation to the coding style, we recommend to setup git
+pre-commit hook to check coding style of the changes you introduced.
+The pre-commit file is located at the root directory. Copy it under
+.git/hooks and make sure that it is executable. The pre-commit script
+uses clang-format-diff.py to detect any style errors. If it is not in
+your PATH or it exists under different name (e.g.,
+clang-format-diff-14 in debian), either add it to PATH variable or add
+git option ``clangformatdiff.binary`` to point to the script.
+
+For emacs users, integrating clang-format to emacs is very easy.
+clang-format.el should come with clang distribution. If it is not
+found, download it from `here
+<https://github.com/llvm/llvm-project/blob/main/clang/tools/clang-format/clang-format.el>`_.
+And add these lines to your .emacs file:
+
+.. code-block:: lisp
+
+ ;; From
+ ;; https://code.google.com/p/chromium/wiki/Emacs#Use_Google's_C++_style!
+ (load "/<path/to>/clang-format.el")
+ (add-hook 'c-mode-common-hook
+ (function (lambda () (local-set-key (kbd "TAB")
+ 'clang-format-region))))
+
+You can find other editor integration in
+http://clang.llvm.org/docs/ClangFormat.html.
diff --git a/doc/sources/h2load-howto.rst b/doc/sources/h2load-howto.rst
new file mode 100644
index 0000000..2ee2754
--- /dev/null
+++ b/doc/sources/h2load-howto.rst
@@ -0,0 +1,142 @@
+.. program:: h2load
+
+h2load - HTTP/2 benchmarking tool - HOW-TO
+==========================================
+
+:doc:`h2load.1` is benchmarking tool for HTTP/2 and HTTP/1.1. It
+supports SSL/TLS and clear text for all supported protocols.
+
+Compiling from source
+---------------------
+
+h2load is compiled alongside nghttp2 and requires that the
+``--enable-app`` flag is passed to ``./configure`` and `required
+dependencies <https://github.com/nghttp2/nghttp2#requirements>`_ are
+available during compilation. For details on compiling, see `nghttp2:
+Building from Git
+<https://github.com/nghttp2/nghttp2#building-from-git>`_.
+
+Basic Usage
+-----------
+
+In order to set benchmark settings, specify following 3 options.
+
+:option:`-n`
+ The number of total requests. Default: 1
+
+:option:`-c`
+ The number of concurrent clients. Default: 1
+
+:option:`-m`
+ The max concurrent streams to issue per client. Default: 1
+
+For SSL/TLS connection, the protocol will be negotiated via ALPN/NPN.
+You can set specific protocols in :option:`--npn-list` option. For
+cleartext connection, the default protocol is HTTP/2. To change the
+protocol in cleartext connection, use :option:`--no-tls-proto` option.
+For convenience, :option:`--h1` option forces HTTP/1.1 for both
+cleartext and SSL/TLS connections.
+
+Here is a command-line to perform benchmark to URI \https://localhost
+using total 100000 requests, 100 concurrent clients and 10 max
+concurrent streams:
+
+.. code-block:: text
+
+ $ h2load -n100000 -c100 -m10 https://localhost
+
+The benchmarking result looks like this:
+
+.. code-block:: text
+
+ finished in 7.08s, 141164.80 req/s, 555.33MB/s
+ requests: 1000000 total, 1000000 started, 1000000 done, 1000000 succeeded, 0 failed, 0 errored, 0 timeout
+ status codes: 1000000 2xx, 0 3xx, 0 4xx, 0 5xx
+ traffic: 4125025824 bytes total, 11023424 bytes headers (space savings 93.07%), 4096000000 bytes data
+ min max mean sd +/- sd
+ time for request: 15.31ms 146.85ms 69.78ms 9.26ms 92.43%
+ time for connect: 1.08ms 25.04ms 10.71ms 9.80ms 64.00%
+ time to 1st byte: 25.36ms 184.96ms 79.11ms 53.97ms 78.00%
+ req/s (client) : 1412.04 1447.84 1426.52 10.57 63.00%
+
+See the h2load manual page :ref:`h2load-1-output` section for the
+explanation of the above numbers.
+
+Timing-based load-testing
+-------------------------
+
+As of v1.26.0, h2load supports timing-based load-testing. This method
+performs load-testing in terms of a given duration instead of a
+pre-defined number of requests. The new option :option:`--duration`
+specifies how long the load-testing takes. For example,
+``--duration=10`` makes h2load perform load-testing against a server
+for 10 seconds. You can also specify a “warming-up” period with
+:option:`--warm-up-time`. If :option:`--duration` is used,
+:option:`-n` option is ignored.
+
+The following command performs load-testing for 10 seconds after 5
+seconds warming up period:
+
+.. code-block:: text
+
+ $ h2load -c100 -m100 --duration=10 --warm-up-time=5 https://localhost
+
+Flow Control
+------------
+
+HTTP/2 has flow control and it may affect benchmarking results. By
+default, h2load uses large enough flow control window, which
+effectively disables flow control. To adjust receiver flow control
+window size, there are following options:
+
+:option:`-w`
+ Sets the stream level initial window size to
+ (2**<N>)-1.
+
+:option:`-W`
+ Sets the connection level initial window size to
+ (2**<N>)-1.
+
+Multi-Threading
+---------------
+
+Sometimes benchmarking client itself becomes a bottleneck. To remedy
+this situation, use :option:`-t` option to specify the number of native
+thread to use.
+
+:option:`-t`
+ The number of native threads. Default: 1
+
+Selecting protocol for clear text
+---------------------------------
+
+By default, if \http:// URI is given, HTTP/2 protocol is used. To
+change the protocol to use for clear text, use :option:`-p` option.
+
+Multiple URIs
+-------------
+
+If multiple URIs are specified, they are used in round robin manner.
+
+.. note::
+
+ Please note that h2load uses scheme, host and port in the first URI
+ and ignores those parts in the rest of the URIs.
+
+UNIX domain socket
+------------------
+
+To request against UNIX domain socket, use :option:`--base-uri`, and
+specify ``unix:`` followed by the path to UNIX domain socket. For
+example, if UNIX domain socket is ``/tmp/nghttpx.sock``, use
+``--base-uri=unix:/tmp/nghttpx.sock``. h2load uses scheme, host and
+port in the first URI in command-line or input file.
+
+HTTP/3
+------
+
+h2load supports HTTP/3 if it is built with HTTP/3 enabled. HTTP/3
+support is experimental.
+
+In order to send HTTP/3 request, specify ``h3`` to
+:option:`--npn-list`.
diff --git a/doc/sources/index.rst b/doc/sources/index.rst
new file mode 100644
index 0000000..b03c348
--- /dev/null
+++ b/doc/sources/index.rst
@@ -0,0 +1,50 @@
+.. nghttp2 documentation main file, created by
+ sphinx-quickstart on Sun Mar 11 22:57:49 2012.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+nghttp2 - HTTP/2 C Library
+============================
+
+This is an implementation of Hypertext Transfer Protocol version 2.
+
+The project is hosted at `github.com/nghttp2/nghttp2
+<https://github.com/nghttp2/nghttp2>`_.
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+ package_README
+ contribute
+ security
+ building-android-binary
+ tutorial-client
+ tutorial-server
+ tutorial-hpack
+ nghttp.1
+ nghttpd.1
+ nghttpx.1
+ h2load.1
+ nghttpx-howto
+ h2load-howto
+ programmers-guide
+ apiref
+ nghttp2.h
+ nghttp2ver.h
+ Source <https://github.com/nghttp2/nghttp2>
+ Issues <https://github.com/nghttp2/nghttp2/issues>
+ nghttp2.org <https://nghttp2.org/>
+
+Released Versions
+=================
+
+https://github.com/nghttp2/nghttp2/releases
+
+Resources
+---------
+
+* HTTP/2 https://tools.ietf.org/html/rfc7540
+* HPACK https://tools.ietf.org/html/rfc7541
+* HTTP Alternative Services https://tools.ietf.org/html/rfc7838
diff --git a/doc/sources/nghttpx-howto.rst b/doc/sources/nghttpx-howto.rst
new file mode 100644
index 0000000..a979504
--- /dev/null
+++ b/doc/sources/nghttpx-howto.rst
@@ -0,0 +1,642 @@
+.. program:: nghttpx
+
+nghttpx - HTTP/2 proxy - HOW-TO
+===============================
+
+:doc:`nghttpx.1` is a proxy translating protocols between HTTP/2 and
+other protocols (e.g., HTTP/1). It operates in several modes and each
+mode may require additional programs to work with. This article
+describes each operation mode and explains the intended use-cases. It
+also covers some useful options later.
+
+Default mode
+------------
+
+If nghttpx is invoked without :option:`--http2-proxy`, it operates in
+default mode. In this mode, it works as reverse proxy (gateway) for
+HTTP/3, HTTP/2 and HTTP/1 clients to backend servers. This is also
+known as "HTTP/2 router".
+
+By default, frontend connection is encrypted using SSL/TLS. So
+server's private key and certificate must be supplied to the command
+line (or through configuration file). In this case, the frontend
+protocol selection will be done via ALPN or NPN.
+
+To turn off encryption on frontend connection, use ``no-tls`` keyword
+in :option:`--frontend` option. HTTP/2 and HTTP/1 are available on
+the frontend, and an HTTP/1 connection can be upgraded to HTTP/2 using
+HTTP Upgrade. Starting HTTP/2 connection by sending HTTP/2 connection
+preface is also supported.
+
+In order to receive HTTP/3 traffic, use ``quic`` parameter in
+:option:`--frontend` option (.e.g, ``--frontend='*,443;quic'``)
+
+nghttpx can listen on multiple frontend addresses. This is achieved
+by using multiple :option:`--frontend` options. For each frontend
+address, TLS can be enabled or disabled.
+
+By default, backend connections are not encrypted. To enable TLS
+encryption on backend connections, use ``tls`` keyword in
+:option:`--backend` option. Using patterns and ``proto`` keyword in
+:option:`--backend` option, backend application protocol can be
+specified per host/request path pattern. It means that you can use
+both HTTP/2 and HTTP/1 in backend connections at the same time. Note
+that default backend protocol is HTTP/1.1. To use HTTP/2 in backend,
+you have to specify ``h2`` in ``proto`` keyword in :option:`--backend`
+explicitly.
+
+The backend is supposed to be a Web server. For example, to make
+nghttpx listen to encrypted HTTP/2 requests at port 8443, and a
+backend Web server is configured to listen to HTTP requests at port
+8080 on the same host, run nghttpx command-line like this:
+
+.. code-block:: text
+
+ $ nghttpx -f0.0.0.0,8443 -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt
+
+Then an HTTP/2 enabled client can access the nghttpx server using HTTP/2. For
+example, you can send a GET request using nghttp:
+
+.. code-block:: text
+
+ $ nghttp -nv https://localhost:8443/
+
+HTTP/2 proxy mode
+-----------------
+
+If nghttpx is invoked with :option:`--http2-proxy` (or its shorthand
+:option:`-s`) option, it operates in HTTP/2 proxy mode. The supported
+protocols in frontend and backend connections are the same as in `default
+mode`_. The difference is that this mode acts like a forward proxy and
+assumes the backend is an HTTP proxy server (e.g., Squid, Apache Traffic
+Server). HTTP/1 requests must include an absolute URI in request line.
+
+By default, the frontend connection is encrypted. So this mode is
+also called secure proxy.
+
+To turn off encryption on the frontend connection, use ``no-tls`` keyword
+in :option:`--frontend` option.
+
+The backend must be an HTTP proxy server. nghttpx supports multiple
+backend server addresses. It translates incoming requests to HTTP
+request to backend server. The backend server performs real proxy
+work for each request, for example, dispatching requests to the origin
+server and caching contents.
+
+The backend connection is not encrypted by default. To enable
+encryption, use ``tls`` keyword in :option:`--backend` option. The
+default backend protocol is HTTP/1.1. To use HTTP/2 in backend
+connection, use :option:`--backend` option, and specify ``h2`` in
+``proto`` keyword explicitly.
+
+For example, to make nghttpx listen to encrypted HTTP/2 requests at
+port 8443, and a backend HTTP proxy server is configured to listen to
+HTTP/1 requests at port 8080 on the same host, run nghttpx command-line
+like this:
+
+.. code-block:: text
+
+ $ nghttpx -s -f'*,8443' -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt
+
+At the time of this writing, Firefox 41 and Chromium v46 can use
+nghttpx as HTTP/2 proxy.
+
+To make Firefox or Chromium use nghttpx as HTTP/2 proxy, user has to
+create proxy.pac script file like this:
+
+.. code-block:: javascript
+
+ function FindProxyForURL(url, host) {
+ return "HTTPS SERVERADDR:PORT";
+ }
+
+``SERVERADDR`` and ``PORT`` is the hostname/address and port of the
+machine nghttpx is running. Please note that both Firefox and
+Chromium require valid certificate for secure proxy.
+
+For Firefox, open Preference window and select Advanced then click
+Network tab. Clicking Connection Settings button will show the
+dialog. Select "Automatic proxy configuration URL" and enter the path
+to proxy.pac file, something like this:
+
+.. code-block:: text
+
+ file:///path/to/proxy.pac
+
+For Chromium, use following command-line:
+
+.. code-block:: text
+
+ $ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn
+
+As HTTP/1 proxy server, Squid may work as out-of-box. Traffic server
+requires to be configured as forward proxy. Here is the minimum
+configuration items to edit:
+
+.. code-block:: text
+
+ CONFIG proxy.config.reverse_proxy.enabled INT 0
+ CONFIG proxy.config.url_remap.remap_required INT 0
+
+Consult Traffic server `documentation
+<http://trafficserver.readthedocs.org/en/latest/admin-guide/configuration/transparent-forward-proxying.en.html>`_
+to know how to configure traffic server as forward proxy and its
+security implications.
+
+ALPN support
+------------
+
+ALPN support requires OpenSSL >= 1.0.2.
+
+Disable frontend SSL/TLS
+------------------------
+
+The frontend connections are encrypted with SSL/TLS by default. To
+turn off SSL/TLS, use ``no-tls`` keyword in :option:`--frontend`
+option. If this option is used, the private key and certificate are
+not required to run nghttpx.
+
+Enable backend SSL/TLS
+----------------------
+
+The backend connections are not encrypted by default. To enable
+SSL/TLS encryption, use ``tls`` keyword in :option:`--backend` option.
+
+Enable SSL/TLS on memcached connection
+--------------------------------------
+
+By default, memcached connection is not encrypted. To enable
+encryption, use ``tls`` keyword in
+:option:`--tls-ticket-key-memcached` for TLS ticket key, and
+:option:`--tls-session-cache-memcached` for TLS session cache.
+
+Specifying additional server certificates
+-----------------------------------------
+
+nghttpx accepts additional server private key and certificate pairs
+using :option:`--subcert` option. It can be used multiple times.
+
+Specifying additional CA certificate
+------------------------------------
+
+By default, nghttpx tries to read CA certificate from system. But
+depending on the system you use, this may fail or is not supported.
+To specify CA certificate manually, use :option:`--cacert` option.
+The specified file must be PEM format and can contain multiple
+certificates.
+
+By default, nghttpx validates server's certificate. If you want to
+turn off this validation, knowing this is really insecure and what you
+are doing, you can use :option:`--insecure` option to disable
+certificate validation.
+
+Read/write rate limit
+---------------------
+
+nghttpx supports transfer rate limiting on frontend connections. You
+can do rate limit per frontend connection for reading and writing
+individually.
+
+To perform rate limit for reading, use :option:`--read-rate` and
+:option:`--read-burst` options. For writing, use
+:option:`--write-rate` and :option:`--write-burst`.
+
+Please note that rate limit is performed on top of TCP and nothing to
+do with HTTP/2 flow control.
+
+Rewriting location header field
+-------------------------------
+
+nghttpx automatically rewrites location response header field if the
+following all conditions satisfy:
+
+* In the default mode (:option:`--http2-proxy` is not used)
+* :option:`--no-location-rewrite` is not used
+* URI in location header field is an absolute URI
+* URI in location header field includes non empty host component.
+* host (without port) in URI in location header field must match the
+ host appearing in ``:authority`` or ``host`` header field.
+
+When rewrite happens, URI scheme is replaced with the ones used in
+frontend, and authority is replaced with which appears in
+``:authority``, or ``host`` request header field. ``:authority``
+header field has precedence over ``host``.
+
+Hot swapping
+------------
+
+nghttpx supports hot swapping using signals. The hot swapping in
+nghttpx is multi step process. First send USR2 signal to nghttpx
+process. It will do fork and execute new executable, using same
+command-line arguments and environment variables.
+
+As of nghttpx version 1.20.0, that is all you have to do. The new
+main process sends QUIT signal to the original process, when it is
+ready to serve requests, to shut it down gracefully.
+
+For earlier versions of nghttpx, you have to do one more thing. At
+this point, both current and new processes can accept requests. To
+gracefully shutdown current process, send QUIT signal to current
+nghttpx process. When all existing frontend connections are done, the
+current process will exit. At this point, only new nghttpx process
+exists and serves incoming requests.
+
+If you want to just reload configuration file without executing new
+binary, send SIGHUP to nghttpx main process.
+
+Re-opening log files
+--------------------
+
+When rotating log files, it is desirable to re-open log files after
+log rotation daemon renamed existing log files. To tell nghttpx to
+re-open log files, send USR1 signal to nghttpx process. It will
+re-open files specified by :option:`--accesslog-file` and
+:option:`--errorlog-file` options.
+
+Multiple frontend addresses
+---------------------------
+
+nghttpx can listen on multiple frontend addresses. To specify them,
+just use :option:`--frontend` (or its shorthand :option:`-f`) option
+repeatedly. TLS can be enabled or disabled per frontend address
+basis. For example, to listen on port 443 with TLS enabled, and on
+port 80 without TLS:
+
+.. code-block:: text
+
+ frontend=*,443
+ frontend=*,80;no-tls
+
+
+Multiple backend addresses
+--------------------------
+
+nghttpx supports multiple backend addresses. To specify them, just
+use :option:`--backend` (or its shorthand :option:`-b`) option
+repeatedly. For example, to use ``192.168.0.10:8080`` and
+``192.168.0.11:8080``, use command-line like this:
+``-b192.168.0.10,8080 -b192.168.0.11,8080``. In configuration file,
+this looks like:
+
+.. code-block:: text
+
+ backend=192.168.0.10,8080
+ backend=192.168.0.11,8008
+
+nghttpx can route request to different backend according to request
+host and path. For example, to route request destined to host
+``doc.example.com`` to backend server ``docserv:3000``, you can write
+like so:
+
+.. code-block:: text
+
+ backend=docserv,3000;doc.example.com/
+
+When you write this option in command-line, you should enclose
+argument with single or double quotes, since the character ``;`` has a
+special meaning in shell.
+
+To route, request to request path ``/foo`` to backend server
+``[::1]:8080``, you can write like so:
+
+.. code-block:: text
+
+ backend=::1,8080;/foo
+
+If the last character of path pattern is ``/``, all request paths
+which start with that pattern match:
+
+.. code-block:: text
+
+ backend=::1,8080;/bar/
+
+The request path ``/bar/buzz`` matches the ``/bar/``.
+
+You can use ``*`` at the end of the path pattern to make it wildcard
+pattern. ``*`` must match at least one character:
+
+.. code-block:: text
+
+ backend=::1,8080;/sample*
+
+The request path ``/sample1/foo`` matches the ``/sample*`` pattern.
+
+Of course, you can specify both host and request path at the same
+time:
+
+.. code-block:: text
+
+ backend=192.168.0.10,8080;example.com/foo
+
+We can use ``*`` in the left most position of host to achieve wildcard
+suffix match. If ``*`` is the left most character, then the remaining
+string should match the request host suffix. ``*`` must match at
+least one character. For example, ``*.example.com`` matches
+``www.example.com`` and ``dev.example.com``, and does not match
+``example.com`` and ``nghttp2.org``. The exact match (without ``*``)
+always takes precedence over wildcard match.
+
+One important thing you have to remember is that we have to specify
+default routing pattern for so called "catch all" pattern. To write
+"catch all" pattern, just specify backend server address, without
+pattern.
+
+Usually, host is the value of ``Host`` header field. In HTTP/2, the
+value of ``:authority`` pseudo header field is used.
+
+When you write multiple backend addresses sharing the same routing
+pattern, they are used as load balancing. For example, to use 2
+servers ``serv1:3000`` and ``serv2:3000`` for request host
+``example.com`` and path ``/myservice``, you can write like so:
+
+.. code-block:: text
+
+ backend=serv1,3000;example.com/myservice
+ backend=serv2,3000;example.com/myservice
+
+You can also specify backend application protocol in
+:option:`--backend` option using ``proto`` keyword after pattern.
+Utilizing this allows ngttpx to route certain request to HTTP/2, other
+requests to HTTP/1. For example, to route requests to ``/ws/`` in
+backend HTTP/1.1 connection, and use backend HTTP/2 for other
+requests, do this:
+
+.. code-block:: text
+
+ backend=serv1,3000;/;proto=h2
+ backend=serv1,3000;/ws/;proto=http/1.1
+
+The default backend protocol is HTTP/1.1.
+
+TLS can be enabled per pattern basis:
+
+.. code-block:: text
+
+ backend=serv1,8443;/;proto=h2;tls
+ backend=serv2,8080;/ws/;proto=http/1.1
+
+In the above case, connection to serv1 will be encrypted by TLS. On
+the other hand, connection to serv2 will not be encrypted by TLS.
+
+Dynamic hostname lookup
+-----------------------
+
+By default, nghttpx performs backend hostname lookup at start up, or
+configuration reload, and keeps using them in its entire session. To
+make nghttpx perform hostname lookup dynamically, use ``dns``
+parameter in :option:`--backend` option, like so:
+
+.. code-block:: text
+
+ backend=foo.example.com,80;;dns
+
+nghttpx will cache resolved addresses for certain period of time. To
+change this cache period, use :option:`--dns-cache-timeout`.
+
+Enable PROXY protocol
+---------------------
+
+PROXY protocol can be enabled per frontend. In order to enable PROXY
+protocol, use ``proxyproto`` parameter in :option:`--frontend` option,
+like so:
+
+.. code-block:: text
+
+ frontend=*,443;proxyproto
+
+nghttpx supports both PROXY protocol v1 and v2. AF_UNIX in PROXY
+protocol version 2 is ignored.
+
+Session affinity
+----------------
+
+Two kinds of session affinity are available: client IP, and HTTP
+Cookie.
+
+To enable client IP based affinity, specify ``affinity=ip`` parameter
+in :option:`--backend` option. If PROXY protocol is enabled, then an
+address obtained from PROXY protocol is taken into consideration.
+
+To enable HTTP Cookie based affinity, specify ``affinity=cookie``
+parameter, and specify a name of cookie in ``affinity-cookie-name``
+parameter. Optionally, a Path attribute can be specified in
+``affinity-cookie-path`` parameter:
+
+.. code-block:: text
+
+ backend=127.0.0.1,3000;;affinity=cookie;affinity-cookie-name=nghttpxlb;affinity-cookie-path=/
+
+Secure attribute of cookie is set if client connection is protected by
+TLS. ``affinity-cookie-stickiness`` specifies the stickiness of this
+affinity. If ``loose`` is given, which is the default, removing or
+adding a backend server might break affinity. While ``strict`` is
+given, removing the designated backend server breaks affinity, but
+adding new backend server does not cause breakage.
+
+PSK cipher suites
+-----------------
+
+nghttpx supports pre-shared key (PSK) cipher suites for both frontend
+and backend TLS connections. For frontend connection, use
+:option:`--psk-secrets` option to specify a file which contains PSK
+identity and secrets. The format of the file is
+``<identity>:<hex-secret>``, where ``<identity>`` is PSK identity, and
+``<hex-secret>`` is PSK secret in hex, like so:
+
+.. code-block:: text
+
+ client1:9567800e065e078085c241d54a01c6c3f24b3bab71a606600f4c6ad2c134f3b9
+ client2:b1376c3f8f6dcf7c886c5bdcceecd1e6f1d708622b6ddd21bda26ebd0c0bca99
+
+nghttpx server accepts any of the identity and secret pairs in the
+file. The default cipher suite list does not contain PSK cipher
+suites. In order to use PSK, PSK cipher suite must be enabled by
+using :option:`--ciphers` option. The desired PSK cipher suite may be
+listed in `HTTP/2 cipher block list
+<https://tools.ietf.org/html/rfc7540#appendix-A>`_. In order to use
+such PSK cipher suite with HTTP/2, disable HTTP/2 cipher block list by
+using :option:`--no-http2-cipher-block-list` option. But you should
+understand its implications.
+
+At the time of writing, even if only PSK cipher suites are specified
+in :option:`--ciphers` option, certificate and private key are still
+required.
+
+For backend connection, use :option:`--client-psk-secrets` option to
+specify a file which contains single PSK identity and secret. The
+format is the same as the file used by :option:`--psk-secrets`
+described above, but only first identity and secret pair is solely
+used, like so:
+
+.. code-block:: text
+
+ client2:b1376c3f8f6dcf7c886c5bdcceecd1e6f1d708622b6ddd21bda26ebd0c0bca99
+
+The default cipher suite list does not contain PSK cipher suites. In
+order to use PSK, PSK cipher suite must be enabled by using
+:option:`--client-ciphers` option. The desired PSK cipher suite may
+be listed in `HTTP/2 cipher block list
+<https://tools.ietf.org/html/rfc7540#appendix-A>`_. In order to use
+such PSK cipher suite with HTTP/2, disable HTTP/2 cipher block list by
+using :option:`--client-no-http2-cipher-block-list` option. But you
+should understand its implications.
+
+TLSv1.3
+-------
+
+As of nghttpx v1.34.0, if it is built with OpenSSL 1.1.1 or later, it
+supports TLSv1.3. 0-RTT data is supported, but by default its
+processing is postponed until TLS handshake completes to mitigate
+replay attack. This costs extra round trip and reduces effectiveness
+of 0-RTT data. :option:`--tls-no-postpone-early-data` makes nghttpx
+not wait for handshake to complete before forwarding request included
+in 0-RTT to get full potential of 0-RTT data. In this case, nghttpx
+adds ``Early-Data: 1`` header field when forwarding a request to a
+backend server. All backend servers should recognize this header
+field and understand that there is a risk for replay attack. See `RFC
+8470 <https://tools.ietf.org/html/rfc8470>`_ for ``Early-Data`` header
+field.
+
+nghttpx disables anti replay protection provided by OpenSSL. The anti
+replay protection of OpenSSL requires that a resumed request must hit
+the same server which generates the session ticket. Therefore it
+might not work nicely in a deployment where there are multiple nghttpx
+instances sharing ticket encryption keys via memcached.
+
+Because TLSv1.3 completely changes the semantics of cipher suite
+naming scheme and structure, nghttpx provides the new option
+:option:`--tls13-ciphers` and :option:`--tls13-client-ciphers` to
+change preferred cipher list for TLSv1.3.
+
+WebSockets over HTTP/2
+----------------------
+
+nghttpx supports `RFC 8441 <https://tools.ietf.org/html/rfc8441>`_
+Bootstrapping WebSockets with HTTP/2 for both frontend and backend
+connections. This feature is enabled by default and no configuration
+is required.
+
+WebSockets over HTTP/3 is also supported.
+
+HTTP/3
+------
+
+nghttpx supports HTTP/3 if it is built with HTTP/3 support enabled.
+HTTP/3 support is experimental.
+
+In order to listen UDP port to receive HTTP/3 traffic,
+:option:`--frontend` option must have ``quic`` parameter:
+
+.. code-block:: text
+
+ frontend=*,443;quic
+
+The above example makes nghttpx receive HTTP/3 traffic on UDP
+port 443.
+
+nghttpx does not support HTTP/3 on backend connection.
+
+Hot swapping (SIGUSR2) or configuration reload (SIGHUP) require eBPF
+program. Without eBPF, old worker processes keep getting HTTP/3
+traffic and do not work as intended. The QUIC keying material to
+encrypt Connection ID must be set with
+:option:`--frontend-quic-secret-file` and must provide the existing
+keys in order to keep the existing connections alive during reload.
+
+The construction of Connection ID closely follows Block Cipher CID
+Algorithm described in `QUIC-LB draft
+<https://datatracker.ietf.org/doc/html/draft-ietf-quic-load-balancers>`_.
+A Connection ID that nghttpx generates is always 20 bytes long. It
+uses first 2 bits as a configuration ID. The remaining bits in the
+first byte are reserved and random. The next 4 bytes are server ID.
+The next 4 bytes are used to route UDP datagram to a correct
+``SO_REUSEPORT`` socket. The remaining bytes are randomly generated.
+The server ID and the next 12 bytes are encrypted with AES-ECB. The
+key is derived from the keying materials stored in a file specified by
+:option:`--frontend-quic-secret-file`. The first 2 bits of keying
+material in the file is used as a configuration ID. The remaining
+bits and following 3 bytes are reserved and unused. The next 32 bytes
+are used as an initial secret. The remaining 32 bytes are used as a
+salt. The encryption key is generated by `HKDF
+<https://datatracker.ietf.org/doc/html/rfc5869>`_ with SHA256 and
+these keying materials and ``connection id encryption key`` as info.
+
+In order announce that HTTP/3 endpoint is available, you should
+specify alt-svc header field. For example, the following options send
+alt-svc header field in HTTP/1.1 and HTTP/2 response:
+
+.. code-block:: text
+
+ altsvc=h3,443,,,ma=3600
+ http2-altsvc=h3,443,,,ma=3600
+
+Migration from nghttpx v1.18.x or earlier
+-----------------------------------------
+
+As of nghttpx v1.19.0, :option:`--ciphers` option only changes cipher
+list for frontend TLS connection. In order to change cipher list for
+backend connection, use :option:`--client-ciphers` option.
+
+Similarly, :option:`--no-http2-cipher-block-list` option only disables
+HTTP/2 cipher block list for frontend connection. In order to disable
+HTTP/2 cipher block list for backend connection, use
+:option:`--client-no-http2-cipher-block-list` option.
+
+``--accept-proxy-protocol`` option was deprecated. Instead, use
+``proxyproto`` parameter in :option:`--frontend` option to enable
+PROXY protocol support per frontend.
+
+Migration from nghttpx v1.8.0 or earlier
+----------------------------------------
+
+As of nghttpx 1.9.0, ``--frontend-no-tls`` and ``--backend-no-tls``
+have been removed.
+
+To disable encryption on frontend connection, use ``no-tls`` keyword
+in :option:`--frontend` potion:
+
+.. code-block:: text
+
+ frontend=*,3000;no-tls
+
+The TLS encryption is now disabled on backend connection in all modes
+by default. To enable encryption on backend connection, use ``tls``
+keyword in :option:`--backend` option:
+
+.. code-block:: text
+
+ backend=127.0.0.1,8080;tls
+
+As of nghttpx 1.9.0, ``--http2-bridge``, ``--client`` and
+``--client-proxy`` options have been removed. These functionality can
+be used using combinations of options.
+
+Use following option instead of ``--http2-bridge``:
+
+.. code-block:: text
+
+ backend=<ADDR>,<PORT>;;proto=h2;tls
+
+Use following options instead of ``--client``:
+
+.. code-block:: text
+
+ frontend=<ADDR>,<PORT>;no-tls
+ backend=<ADDR>,<PORT>;;proto=h2;tls
+
+Use following options instead of ``--client-proxy``:
+
+.. code-block:: text
+
+ http2-proxy=yes
+ frontend=<ADDR>,<PORT>;no-tls
+ backend=<ADDR>,<PORT>;;proto=h2;tls
+
+We also removed ``--backend-http2-connections-per-worker`` option. It
+was present because previously the number of backend h2 connection was
+statically configured, and defaulted to 1. Now the number of backend
+h2 connection is increased on demand. We know the maximum number of
+concurrent streams per connection. When we push as many request as
+the maximum concurrency to the one connection, we create another new
+connection so that we can distribute load and avoid delay the request
+processing. This is done automatically without any configuration.
diff --git a/doc/sources/security.rst b/doc/sources/security.rst
new file mode 100644
index 0000000..ab27eab
--- /dev/null
+++ b/doc/sources/security.rst
@@ -0,0 +1,38 @@
+Security Process
+================
+
+If you find a vulnerability in our software, please send the email to
+"tatsuhiro.t at gmail dot com" about its details instead of submitting
+issues on github issue page. It is a standard practice not to
+disclose vulnerability information publicly until a fixed version is
+released, or mitigation is worked out. In the future, we may setup a
+dedicated mail address for this purpose.
+
+If we identify that the reported issue is really a vulnerability, we
+open a new security advisory draft using `GitHub security feature
+<https://github.com/nghttp2/nghttp2/security>`_ and discuss the
+mitigation and bug fixes there. The fixes are committed to the
+private repository.
+
+We write the security advisory and get CVE number from GitHub
+privately. We also discuss the disclosure date to the public.
+
+We make a new release with the fix at the same time when the
+vulnerability is disclosed to public.
+
+At least 7 days before the public disclosure date, we will post
+security advisory (which includes all the details of the vulnerability
+and the possible mitigation strategies) and the patches to fix the
+issue to `distros@openwall
+<https://oss-security.openwall.org/wiki/mailing-lists/distros>`_
+mailing list. We also open a new issue on `nghttp2 issue tracker
+<https://github.com/nghttp2/nghttp2/issues>`_ which notifies that the
+upcoming release will have a security fix. The ``SECURITY`` label is
+attached to this kind of issue.
+
+Before few hours of new release, we merge the fixes to the master
+branch (and/or a release branch if necessary) and make a new release.
+Security advisory is disclosed on GitHub. We also post the
+vulnerability information to `oss-security
+<https://oss-security.openwall.org/wiki/mailing-lists/oss-security>`_
+mailing list.
diff --git a/doc/sources/tutorial-client.rst b/doc/sources/tutorial-client.rst
new file mode 100644
index 0000000..7c086a8
--- /dev/null
+++ b/doc/sources/tutorial-client.rst
@@ -0,0 +1,498 @@
+Tutorial: HTTP/2 client
+=========================
+
+In this tutorial, we are going to write a very primitive HTTP/2
+client. The complete source code, `libevent-client.c`_, is attached at
+the end of this page. It also resides in the examples directory in
+the archive or repository.
+
+This simple client takes a single HTTPS URI and retrieves the resource
+at the URI. The synopsis is:
+
+.. code-block:: text
+
+ $ libevent-client HTTPS_URI
+
+We use libevent in this tutorial to handle networking I/O. Please
+note that nghttp2 itself does not depend on libevent.
+
+The client starts with some libevent and OpenSSL setup in the
+``main()`` and ``run()`` functions. This setup isn't specific to
+nghttp2, but one thing you should look at is setup of the NPN
+callback. The NPN callback is used by the client to select the next
+application protocol over TLS. In this tutorial, we use the
+`nghttp2_select_next_protocol()` helper function to select the HTTP/2
+protocol the library supports::
+
+ static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
+ unsigned char *outlen, const unsigned char *in,
+ unsigned int inlen, void *arg _U_) {
+ if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
+ errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
+ }
+ return SSL_TLSEXT_ERR_OK;
+ }
+
+If you are following TLS related RFC, you know that NPN is not the
+standardized way to negotiate HTTP/2. NPN itself is not event
+published as RFC. The standard way to negotiate HTTP/2 is ALPN,
+Application-Layer Protocol Negotiation Extension, defined in `RFC 7301
+<https://tools.ietf.org/html/rfc7301>`_. The one caveat of ALPN is
+that OpenSSL >= 1.0.2 is required. We use macro to enable/disable
+ALPN support depending on OpenSSL version. OpenSSL's ALPN
+implementation does not require callback function like the above. But
+we have to instruct OpenSSL SSL_CTX to use ALPN, which we'll talk
+about soon.
+
+The callback is added to the SSL_CTX object using
+``SSL_CTX_set_next_proto_select_cb()``::
+
+ static SSL_CTX *create_ssl_ctx(void) {
+ SSL_CTX *ssl_ctx;
+ ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+ if (!ssl_ctx) {
+ errx(1, "Could not create SSL/TLS context: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ }
+ SSL_CTX_set_options(ssl_ctx,
+ SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+ SSL_OP_NO_COMPRESSION |
+ SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
+ SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
+
+ #if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3);
+ #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+
+ return ssl_ctx;
+ }
+
+Here we see ``SSL_CTX_get_alpn_protos()`` function call. We instructs
+OpenSSL to notify the server that we support h2, ALPN identifier for
+HTTP/2.
+
+The example client defines a couple of structs:
+
+We define and use a ``http2_session_data`` structure to store data
+related to the HTTP/2 session::
+
+ typedef struct {
+ nghttp2_session *session;
+ struct evdns_base *dnsbase;
+ struct bufferevent *bev;
+ http2_stream_data *stream_data;
+ } http2_session_data;
+
+Since this program only handles one URI, it uses only one stream. We
+store the single stream's data in a ``http2_stream_data`` structure
+and the ``stream_data`` points to it. The ``http2_stream_data``
+structure is defined as follows::
+
+ typedef struct {
+ /* The NULL-terminated URI string to retrieve. */
+ const char *uri;
+ /* Parsed result of the |uri| */
+ struct http_parser_url *u;
+ /* The authority portion of the |uri|, not NULL-terminated */
+ char *authority;
+ /* The path portion of the |uri|, including query, not
+ NULL-terminated */
+ char *path;
+ /* The length of the |authority| */
+ size_t authoritylen;
+ /* The length of the |path| */
+ size_t pathlen;
+ /* The stream ID of this stream */
+ int32_t stream_id;
+ } http2_stream_data;
+
+We create and initialize these structures in
+``create_http2_session_data()`` and ``create_http2_stream_data()``
+respectively.
+
+``initiate_connection()`` is called to start the connection to the
+remote server. It's defined as::
+
+ static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
+ const char *host, uint16_t port,
+ http2_session_data *session_data) {
+ int rv;
+ struct bufferevent *bev;
+ SSL *ssl;
+
+ ssl = create_ssl(ssl_ctx);
+ bev = bufferevent_openssl_socket_new(
+ evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
+ BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
+ bufferevent_enable(bev, EV_READ | EV_WRITE);
+ bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
+ rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
+ AF_UNSPEC, host, port);
+
+ if (rv != 0) {
+ errx(1, "Could not connect to the remote host %s", host);
+ }
+ session_data->bev = bev;
+ }
+
+``initiate_connection()`` creates a bufferevent for the connection and
+sets up three callbacks: ``readcb``, ``writecb``, and ``eventcb``.
+
+The ``eventcb()`` is invoked by the libevent event loop when an event
+(e.g. connection has been established, timeout, etc.) occurs on the
+underlying network socket::
+
+ static void eventcb(struct bufferevent *bev, short events, void *ptr) {
+ http2_session_data *session_data = (http2_session_data *)ptr;
+ if (events & BEV_EVENT_CONNECTED) {
+ int fd = bufferevent_getfd(bev);
+ int val = 1;
+ const unsigned char *alpn = NULL;
+ unsigned int alpnlen = 0;
+ SSL *ssl;
+
+ fprintf(stderr, "Connected\n");
+
+ ssl = bufferevent_openssl_get_ssl(session_data->bev);
+
+ SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
+ #if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ if (alpn == NULL) {
+ SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
+ }
+ #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+
+ if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
+ fprintf(stderr, "h2 is not negotiated\n");
+ delete_http2_session_data(session_data);
+ return;
+ }
+
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
+ initialize_nghttp2_session(session_data);
+ send_client_connection_header(session_data);
+ submit_request(session_data);
+ if (session_send(session_data) != 0) {
+ delete_http2_session_data(session_data);
+ }
+ return;
+ }
+ if (events & BEV_EVENT_EOF) {
+ warnx("Disconnected from the remote host");
+ } else if (events & BEV_EVENT_ERROR) {
+ warnx("Network error");
+ } else if (events & BEV_EVENT_TIMEOUT) {
+ warnx("Timeout");
+ }
+ delete_http2_session_data(session_data);
+ }
+
+Here we validate that HTTP/2 is negotiated, and if not, drop
+connection.
+
+For ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR``, and ``BEV_EVENT_TIMEOUT``
+events, we just simply tear down the connection.
+
+The ``BEV_EVENT_CONNECTED`` event is invoked when the SSL/TLS
+handshake has completed successfully. After this we're ready to begin
+communicating via HTTP/2.
+
+The ``initialize_nghttp2_session()`` function initializes the nghttp2
+session object and several callbacks::
+
+ static void initialize_nghttp2_session(http2_session_data *session_data) {
+ nghttp2_session_callbacks *callbacks;
+
+ nghttp2_session_callbacks_new(&callbacks);
+
+ nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
+
+ nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
+ on_frame_recv_callback);
+
+ nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
+ callbacks, on_data_chunk_recv_callback);
+
+ nghttp2_session_callbacks_set_on_stream_close_callback(
+ callbacks, on_stream_close_callback);
+
+ nghttp2_session_callbacks_set_on_header_callback(callbacks,
+ on_header_callback);
+
+ nghttp2_session_callbacks_set_on_begin_headers_callback(
+ callbacks, on_begin_headers_callback);
+
+ nghttp2_session_client_new(&session_data->session, callbacks, session_data);
+
+ nghttp2_session_callbacks_del(callbacks);
+ }
+
+Since we are creating a client, we use `nghttp2_session_client_new()`
+to initialize the nghttp2 session object. The callbacks setup are
+explained later.
+
+The `delete_http2_session_data()` function destroys ``session_data``
+and frees its bufferevent, so the underlying connection is closed. It
+also calls `nghttp2_session_del()` to delete the nghttp2 session
+object.
+
+A HTTP/2 connection begins by sending the client connection preface,
+which is a 24 byte magic byte string (:macro:`NGHTTP2_CLIENT_MAGIC`),
+followed by a SETTINGS frame. The 24 byte magic string is sent
+automatically by nghttp2. We send the SETTINGS frame in
+``send_client_connection_header()``::
+
+ static void send_client_connection_header(http2_session_data *session_data) {
+ nghttp2_settings_entry iv[1] = {
+ {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
+ int rv;
+
+ /* client 24 bytes magic string will be sent by nghttp2 library */
+ rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
+ ARRLEN(iv));
+ if (rv != 0) {
+ errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
+ }
+ }
+
+Here we specify SETTINGS_MAX_CONCURRENT_STREAMS as 100. This is not
+needed for this tiny example program, it just demonstrates use of the
+SETTINGS frame. To queue the SETTINGS frame for transmission, we call
+`nghttp2_submit_settings()`. Note that `nghttp2_submit_settings()`
+only queues the frame for transmission, and doesn't actually send it.
+All ``nghttp2_submit_*()`` family functions have this property. To
+actually send the frame, `nghttp2_session_send()` has to be called,
+which is described (and called) later.
+
+After the transmission of the client connection header, we enqueue the
+HTTP request in the ``submit_request()`` function::
+
+ static void submit_request(http2_session_data *session_data) {
+ int32_t stream_id;
+ http2_stream_data *stream_data = session_data->stream_data;
+ const char *uri = stream_data->uri;
+ const struct http_parser_url *u = stream_data->u;
+ nghttp2_nv hdrs[] = {
+ MAKE_NV2(":method", "GET"),
+ MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off],
+ u->field_data[UF_SCHEMA].len),
+ MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
+ MAKE_NV(":path", stream_data->path, stream_data->pathlen)};
+ fprintf(stderr, "Request headers:\n");
+ print_headers(stderr, hdrs, ARRLEN(hdrs));
+ stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs,
+ ARRLEN(hdrs), NULL, stream_data);
+ if (stream_id < 0) {
+ errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
+ }
+
+ stream_data->stream_id = stream_id;
+ }
+
+We build the HTTP request header fields in ``hdrs``, which is an array
+of :type:`nghttp2_nv`. There are four header fields to be sent:
+``:method``, ``:scheme``, ``:authority``, and ``:path``. To queue the
+HTTP request, we call `nghttp2_submit_request()`. The ``stream_data``
+is passed via the *stream_user_data* parameter, which is helpfully
+later passed back to callback functions.
+
+`nghttp2_submit_request()` returns the newly assigned stream ID for
+the request.
+
+The next bufferevent callback is ``readcb()``, which is invoked when
+data is available to read from the bufferevent input buffer::
+
+ static void readcb(struct bufferevent *bev, void *ptr) {
+ http2_session_data *session_data = (http2_session_data *)ptr;
+ ssize_t readlen;
+ struct evbuffer *input = bufferevent_get_input(bev);
+ size_t datalen = evbuffer_get_length(input);
+ unsigned char *data = evbuffer_pullup(input, -1);
+
+ readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
+ if (readlen < 0) {
+ warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
+ delete_http2_session_data(session_data);
+ return;
+ }
+ if (evbuffer_drain(input, (size_t)readlen) != 0) {
+ warnx("Fatal error: evbuffer_drain failed");
+ delete_http2_session_data(session_data);
+ return;
+ }
+ if (session_send(session_data) != 0) {
+ delete_http2_session_data(session_data);
+ return;
+ }
+ }
+
+In this function we feed all unprocessed, received data to the nghttp2
+session object using the `nghttp2_session_mem_recv()` function.
+`nghttp2_session_mem_recv()` processes the received data and may
+invoke nghttp2 callbacks and queue frames for transmission. Since
+there may be pending frames for transmission, we call immediately
+``session_send()`` to send them. ``session_send()`` is defined as
+follows::
+
+ static int session_send(http2_session_data *session_data) {
+ int rv;
+
+ rv = nghttp2_session_send(session_data->session);
+ if (rv != 0) {
+ warnx("Fatal error: %s", nghttp2_strerror(rv));
+ return -1;
+ }
+ return 0;
+ }
+
+The `nghttp2_session_send()` function serializes pending frames into
+wire format and calls the ``send_callback()`` function to send them.
+``send_callback()`` has type :type:`nghttp2_send_callback` and is
+defined as::
+
+ static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
+ size_t length, int flags _U_, void *user_data) {
+ http2_session_data *session_data = (http2_session_data *)user_data;
+ struct bufferevent *bev = session_data->bev;
+ bufferevent_write(bev, data, length);
+ return (ssize_t)length;
+ }
+
+Since we use bufferevent to abstract network I/O, we just write the
+data to the bufferevent object. Note that `nghttp2_session_send()`
+continues to write all frames queued so far. If we were writing the
+data to the non-blocking socket directly using the ``write()`` system
+call, we'd soon receive an ``EAGAIN`` or ``EWOULDBLOCK`` error, since
+sockets have a limited send buffer. If that happens, it's possible to
+return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the nghttp2 library
+to stop sending further data. When writing to a bufferevent, you
+should regulate the amount of data written, to avoid possible huge
+memory consumption. In this example client however we don't implement
+a limit. To see how to regulate the amount of buffered data, see the
+``send_callback()`` in the server tutorial.
+
+The third bufferevent callback is ``writecb()``, which is invoked when
+all data written in the bufferevent output buffer has been sent::
+
+ static void writecb(struct bufferevent *bev _U_, void *ptr) {
+ http2_session_data *session_data = (http2_session_data *)ptr;
+ if (nghttp2_session_want_read(session_data->session) == 0 &&
+ nghttp2_session_want_write(session_data->session) == 0 &&
+ evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
+ delete_http2_session_data(session_data);
+ }
+ }
+
+As described earlier, we just write off all data in `send_callback()`,
+so there is no data to write in this function. All we have to do is
+check if the connection should be dropped or not. The nghttp2 session
+object keeps track of reception and transmission of GOAWAY frames and
+other error conditions. Using this information, the nghttp2 session
+object can state whether the connection should be dropped or not.
+More specifically, when both `nghttp2_session_want_read()` and
+`nghttp2_session_want_write()` return 0, the connection is no-longer
+required and can be closed. Since we're using bufferevent and its
+deferred callback option, the bufferevent output buffer may still
+contain pending data when the ``writecb()`` is called. To handle this
+situation, we also check whether the output buffer is empty or not. If
+all of these conditions are met, then we drop the connection.
+
+Now let's look at the remaining nghttp2 callbacks setup in the
+``initialize_nghttp2_setup()`` function.
+
+A server responds to the request by first sending a HEADERS frame.
+The HEADERS frame consists of response header name/value pairs, and
+the ``on_header_callback()`` is called for each name/value pair::
+
+ static int on_header_callback(nghttp2_session *session _U_,
+ const nghttp2_frame *frame, const uint8_t *name,
+ size_t namelen, const uint8_t *value,
+ size_t valuelen, uint8_t flags _U_,
+ void *user_data) {
+ http2_session_data *session_data = (http2_session_data *)user_data;
+ switch (frame->hd.type) {
+ case NGHTTP2_HEADERS:
+ if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
+ session_data->stream_data->stream_id == frame->hd.stream_id) {
+ /* Print response headers for the initiated request. */
+ print_header(stderr, name, namelen, value, valuelen);
+ break;
+ }
+ }
+ return 0;
+ }
+
+In this tutorial, we just print the name/value pairs on stderr.
+
+After the HEADERS frame has been fully received (and thus all response
+header name/value pairs have been received), the
+``on_frame_recv_callback()`` function is called::
+
+ static int on_frame_recv_callback(nghttp2_session *session _U_,
+ const nghttp2_frame *frame, void *user_data) {
+ http2_session_data *session_data = (http2_session_data *)user_data;
+ switch (frame->hd.type) {
+ case NGHTTP2_HEADERS:
+ if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
+ session_data->stream_data->stream_id == frame->hd.stream_id) {
+ fprintf(stderr, "All headers received\n");
+ }
+ break;
+ }
+ return 0;
+ }
+
+``on_frame_recv_callback()`` is called for other frame types too.
+
+In this tutorial, we are just interested in the HTTP response HEADERS
+frame. We check the frame type and its category (it should be
+:macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). We also
+check its stream ID.
+
+Next, zero or more DATA frames can be received. The
+``on_data_chunk_recv_callback()`` function is invoked when a chunk of
+data is received from the remote peer::
+
+ static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
+ uint8_t flags _U_, int32_t stream_id,
+ const uint8_t *data, size_t len,
+ void *user_data) {
+ http2_session_data *session_data = (http2_session_data *)user_data;
+ if (session_data->stream_data->stream_id == stream_id) {
+ fwrite(data, len, 1, stdout);
+ }
+ return 0;
+ }
+
+In our case, a chunk of data is HTTP response body. After checking the
+stream ID, we just write the received data to stdout. Note the output
+in the terminal may be corrupted if the response body contains some
+binary data.
+
+The ``on_stream_close_callback()`` function is invoked when the stream
+is about to close::
+
+ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
+ nghttp2_error_code error_code,
+ void *user_data) {
+ http2_session_data *session_data = (http2_session_data *)user_data;
+ int rv;
+
+ if (session_data->stream_data->stream_id == stream_id) {
+ fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
+ error_code);
+ rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
+ if (rv != 0) {
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+ }
+ return 0;
+ }
+
+If the stream ID matches the one we initiated, it means that its
+stream is going to be closed. Since we have finished receiving
+resource we wanted (or the stream was reset by RST_STREAM from the
+remote peer), we call `nghttp2_session_terminate_session()` to
+commence closure of the HTTP/2 session gracefully. If you have
+some data associated for the stream to be closed, you may delete it
+here.
diff --git a/doc/sources/tutorial-hpack.rst b/doc/sources/tutorial-hpack.rst
new file mode 100644
index 0000000..36e82d9
--- /dev/null
+++ b/doc/sources/tutorial-hpack.rst
@@ -0,0 +1,126 @@
+Tutorial: HPACK API
+===================
+
+In this tutorial, we describe basic use of nghttp2's HPACK API. We
+briefly describe the APIs for deflating and inflating header fields.
+The full example of using these APIs, `deflate.c`_, is attached at the
+end of this page. It also resides in the examples directory in the
+archive or repository.
+
+Deflating (encoding) headers
+----------------------------
+
+First we need to initialize a :type:`nghttp2_hd_deflater` object using
+the `nghttp2_hd_deflate_new()` function::
+
+ int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr,
+ size_t deflate_hd_table_bufsize_max);
+
+This function allocates a :type:`nghttp2_hd_deflater` object,
+initializes it, and assigns its pointer to ``*deflater_ptr``. The
+*deflate_hd_table_bufsize_max* is the upper bound of header table size
+the deflater will use. This will limit the memory usage by the
+deflater object for the dynamic header table. If in doubt, just
+specify 4096 here, which is the default upper bound of dynamic header
+table buffer size.
+
+To encode header fields, use the `nghttp2_hd_deflate_hd()` function::
+
+ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,
+ uint8_t *buf, size_t buflen,
+ const nghttp2_nv *nva, size_t nvlen);
+
+The *deflater* is the deflater object initialized by
+`nghttp2_hd_deflate_new()` described above. The encoded byte string is
+written to the buffer *buf*, which has length *buflen*. The *nva* is
+a pointer to an array of headers fields, each of type
+:type:`nghttp2_nv`. *nvlen* is the number of header fields which
+*nva* contains.
+
+It is important to initialize and assign all members of
+:type:`nghttp2_nv`. For security sensitive header fields (such as
+cookies), set the :macro:`NGHTTP2_NV_FLAG_NO_INDEX` flag in
+:member:`nghttp2_nv.flags`. Setting this flag prevents recovery of
+sensitive header fields by compression based attacks: This is achieved
+by not inserting the header field into the dynamic header table.
+
+`nghttp2_hd_deflate_hd()` processes all headers given in *nva*. The
+*nva* must include all request or response header fields to be sent in
+one HEADERS (or optionally following (multiple) CONTINUATION
+frame(s)). The *buf* must have enough space to store the encoded
+result, otherwise the function will fail. To estimate the upper bound
+of the encoded result length, use `nghttp2_hd_deflate_bound()`::
+
+ size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
+ const nghttp2_nv *nva, size_t nvlen);
+
+Pass this function the same parameters (*deflater*, *nva*, and
+*nvlen*) which will be passed to `nghttp2_hd_deflate_hd()`.
+
+Subsequent calls to `nghttp2_hd_deflate_hd()` will use the current
+encoder state and perform differential encoding, which yields HPAC's
+fundamental compression gain.
+
+If `nghttp2_hd_deflate_hd()` fails, the failure is fatal and any
+further calls with the same deflater object will fail. Thus it's very
+important to use `nghttp2_hd_deflate_bound()` to determine the
+required size of the output buffer.
+
+To delete a :type:`nghttp2_hd_deflater` object, use the
+`nghttp2_hd_deflate_del()` function.
+
+Inflating (decoding) headers
+----------------------------
+
+A :type:`nghttp2_hd_inflater` object is used to inflate compressed
+header data. To initialize the object, use
+`nghttp2_hd_inflate_new()`::
+
+ int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr);
+
+To inflate header data, use `nghttp2_hd_inflate_hd2()`::
+
+ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
+ nghttp2_nv *nv_out, int *inflate_flags,
+ const uint8_t *in, size_t inlen,
+ int in_final);
+
+`nghttp2_hd_inflate_hd2()` reads a stream of bytes and outputs a
+single header field at a time. Multiple calls are normally required to
+read a full stream of bytes and output all of the header fields.
+
+The *inflater* is the inflater object initialized above. The *nv_out*
+is a pointer to a :type:`nghttp2_nv` into which one header field may
+be stored. The *in* is a pointer to input data, and *inlen* is its
+length. The caller is not required to specify the whole deflated
+header data via *in* at once: Instead it can call this function
+multiple times as additional data bytes become available. If
+*in_final* is nonzero, it tells the function that the passed data is
+the final sequence of deflated header data.
+
+The *inflate_flags* is an output parameter; on success the function
+sets it to a bitset of flags. It will be described later.
+
+This function returns when each header field is inflated. When this
+happens, the function sets the :macro:`NGHTTP2_HD_INFLATE_EMIT` flag
+in *inflate_flags*, and a header field is stored in *nv_out*. The
+return value indicates the number of bytes read from *in* processed so
+far, which may be less than *inlen*. The caller should call the
+function repeatedly until all bytes are processed. Processed bytes
+should be removed from *in*, and *inlen* should be adjusted
+appropriately.
+
+If *in_final* is nonzero and all given data was processed, the
+function sets the :macro:`NGHTTP2_HD_INFLATE_FINAL` flag in
+*inflate_flags*. When you see this flag set, call the
+`nghttp2_hd_inflate_end_headers()` function.
+
+If *in_final* is zero and the :macro:`NGHTTP2_HD_INFLATE_EMIT` flag is
+not set, it indicates that all given data was processed. The caller
+is required to pass additional data.
+
+Example usage of `nghttp2_hd_inflate_hd2()` is shown in the
+`inflate_header_block()` function in `deflate.c`_.
+
+Finally, to delete a :type:`nghttp2_hd_inflater` object, use
+`nghttp2_hd_inflate_del()`.
diff --git a/doc/sources/tutorial-server.rst b/doc/sources/tutorial-server.rst
new file mode 100644
index 0000000..3142837
--- /dev/null
+++ b/doc/sources/tutorial-server.rst
@@ -0,0 +1,625 @@
+Tutorial: HTTP/2 server
+=========================
+
+In this tutorial, we are going to write a single-threaded, event-based
+HTTP/2 web server, which supports HTTPS only. It can handle concurrent
+multiple requests, but only the GET method is supported. The complete
+source code, `libevent-server.c`_, is attached at the end of this
+page. The source also resides in the examples directory in the
+archive or repository.
+
+This simple server takes 3 arguments: The port number to listen on,
+the path to your SSL/TLS private key file, and the path to your
+certificate file. The synopsis is:
+
+.. code-block:: text
+
+ $ libevent-server PORT /path/to/server.key /path/to/server.crt
+
+We use libevent in this tutorial to handle networking I/O. Please
+note that nghttp2 itself does not depend on libevent.
+
+The server starts with some libevent and OpenSSL setup in the
+``main()`` and ``run()`` functions. This setup isn't specific to
+nghttp2, but one thing you should look at is setup of the NPN
+callback. The NPN callback is used by the server to advertise which
+application protocols the server supports to a client. In this
+example program, when creating the ``SSL_CTX`` object, we store the
+application protocol name in the wire format of NPN in a statically
+allocated buffer. This is safe because we only create one ``SSL_CTX``
+object in the program's entire lifetime.
+
+If you are following TLS related RFC, you know that NPN is not the
+standardized way to negotiate HTTP/2. NPN itself is not even
+published as RFC. The standard way to negotiate HTTP/2 is ALPN,
+Application-Layer Protocol Negotiation Extension, defined in `RFC 7301
+<https://tools.ietf.org/html/rfc7301>`_. The one caveat of ALPN is
+that OpenSSL >= 1.0.2 is required. We use macro to enable/disable
+ALPN support depending on OpenSSL version. In ALPN, client sends the
+list of supported application protocols, and server selects one of
+them. We provide the callback for it::
+
+ static unsigned char next_proto_list[256];
+ static size_t next_proto_list_len;
+
+ static int next_proto_cb(SSL *s _U_, const unsigned char **data,
+ unsigned int *len, void *arg _U_) {
+ *data = next_proto_list;
+ *len = (unsigned int)next_proto_list_len;
+ return SSL_TLSEXT_ERR_OK;
+ }
+
+ #if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ static int alpn_select_proto_cb(SSL *ssl _U_, const unsigned char **out,
+ unsigned char *outlen, const unsigned char *in,
+ unsigned int inlen, void *arg _U_) {
+ int rv;
+
+ rv = nghttp2_select_next_protocol((unsigned char **)out, outlen, in, inlen);
+
+ if (rv != 1) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+ }
+ #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+
+ static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
+ SSL_CTX *ssl_ctx;
+ EC_KEY *ecdh;
+
+ ssl_ctx = SSL_CTX_new(SSLv23_server_method());
+
+ ...
+
+ next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN;
+ memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID,
+ NGHTTP2_PROTO_VERSION_ID_LEN);
+ next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN;
+
+ SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL);
+
+ #if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, NULL);
+ #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+
+ return ssl_ctx;
+ }
+
+The wire format of NPN is a sequence of length prefixed strings, with
+exactly one byte used to specify the length of each protocol
+identifier. In this tutorial, we advertise the specific HTTP/2
+protocol version the current nghttp2 library supports, which is
+exported in the identifier :macro:`NGHTTP2_PROTO_VERSION_ID`. The
+``next_proto_cb()`` function is the server-side NPN callback. In the
+OpenSSL implementation, we just assign the pointer to the NPN buffers
+we filled in earlier. The NPN callback function is set to the
+``SSL_CTX`` object using ``SSL_CTX_set_next_protos_advertised_cb()``.
+
+In ``alpn_select_proto_cb()``, we use `nghttp2_select_next_protocol()`
+to select application protocol. The `nghttp2_select_next_protocol()`
+returns 1 only if it selected h2 (ALPN identifier for HTTP/2), and out
+parameters were assigned accordingly.
+
+Next, let's take a look at the main structures used by the example
+application:
+
+We use the ``app_context`` structure to store application-wide data::
+
+ struct app_context {
+ SSL_CTX *ssl_ctx;
+ struct event_base *evbase;
+ };
+
+We use the ``http2_session_data`` structure to store session-level
+(which corresponds to one HTTP/2 connection) data::
+
+ typedef struct http2_session_data {
+ struct http2_stream_data root;
+ struct bufferevent *bev;
+ app_context *app_ctx;
+ nghttp2_session *session;
+ char *client_addr;
+ } http2_session_data;
+
+We use the ``http2_stream_data`` structure to store stream-level data::
+
+ typedef struct http2_stream_data {
+ struct http2_stream_data *prev, *next;
+ char *request_path;
+ int32_t stream_id;
+ int fd;
+ } http2_stream_data;
+
+A single HTTP/2 session can have multiple streams. To manage them, we
+use a doubly linked list: The first element of this list is pointed
+to by the ``root->next`` in ``http2_session_data``. Initially,
+``root->next`` is ``NULL``.
+
+libevent's bufferevent structure is used to perform network I/O, with
+the pointer to the bufferevent stored in the ``http2_session_data``
+structure. Note that the bufferevent object is kept in
+``http2_session_data`` and not in ``http2_stream_data``. This is
+because ``http2_stream_data`` is just a logical stream multiplexed
+over the single connection managed by the bufferevent in
+``http2_session_data``.
+
+We first create a listener object to accept incoming connections.
+libevent's ``struct evconnlistener`` is used for this purpose::
+
+ static void start_listen(struct event_base *evbase, const char *service,
+ app_context *app_ctx) {
+ int rv;
+ struct addrinfo hints;
+ struct addrinfo *res, *rp;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ #ifdef AI_ADDRCONFIG
+ hints.ai_flags |= AI_ADDRCONFIG;
+ #endif /* AI_ADDRCONFIG */
+
+ rv = getaddrinfo(NULL, service, &hints, &res);
+ if (rv != 0) {
+ errx(1, NULL);
+ }
+ for (rp = res; rp; rp = rp->ai_next) {
+ struct evconnlistener *listener;
+ listener = evconnlistener_new_bind(
+ evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
+ 16, rp->ai_addr, (int)rp->ai_addrlen);
+ if (listener) {
+ freeaddrinfo(res);
+
+ return;
+ }
+ }
+ errx(1, "Could not start listener");
+ }
+
+We specify the ``acceptcb`` callback, which is called when a new connection is
+accepted::
+
+ static void acceptcb(struct evconnlistener *listener _U_, int fd,
+ struct sockaddr *addr, int addrlen, void *arg) {
+ app_context *app_ctx = (app_context *)arg;
+ http2_session_data *session_data;
+
+ session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
+
+ bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
+ }
+
+Here we create the ``http2_session_data`` object. The connection's
+bufferevent is initialized at the same time. We specify three
+callbacks for the bufferevent: ``readcb``, ``writecb``, and
+``eventcb``.
+
+The ``eventcb()`` callback is invoked by the libevent event loop when an event
+(e.g. connection has been established, timeout, etc.) occurs on the
+underlying network socket::
+
+ static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
+ http2_session_data *session_data = (http2_session_data *)ptr;
+ if (events & BEV_EVENT_CONNECTED) {
+ const unsigned char *alpn = NULL;
+ unsigned int alpnlen = 0;
+ SSL *ssl;
+
+ fprintf(stderr, "%s connected\n", session_data->client_addr);
+
+ ssl = bufferevent_openssl_get_ssl(session_data->bev);
+
+ SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
+ #if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ if (alpn == NULL) {
+ SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
+ }
+ #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+
+ if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
+ fprintf(stderr, "%s h2 is not negotiated\n", session_data->client_addr);
+ delete_http2_session_data(session_data);
+ return;
+ }
+
+ initialize_nghttp2_session(session_data);
+
+ if (send_server_connection_header(session_data) != 0 ||
+ session_send(session_data) != 0) {
+ delete_http2_session_data(session_data);
+ return;
+ }
+
+ return;
+ }
+ if (events & BEV_EVENT_EOF) {
+ fprintf(stderr, "%s EOF\n", session_data->client_addr);
+ } else if (events & BEV_EVENT_ERROR) {
+ fprintf(stderr, "%s network error\n", session_data->client_addr);
+ } else if (events & BEV_EVENT_TIMEOUT) {
+ fprintf(stderr, "%s timeout\n", session_data->client_addr);
+ }
+ delete_http2_session_data(session_data);
+ }
+
+Here we validate that HTTP/2 is negotiated, and if not, drop
+connection.
+
+For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR``, and
+``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection.
+The ``delete_http2_session_data()`` function destroys the
+``http2_session_data`` object and its associated bufferevent member.
+As a result, the underlying connection is closed.
+
+The
+``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake has
+completed successfully. After this we are ready to begin communicating
+via HTTP/2.
+
+The ``initialize_nghttp2_session()`` function initializes the nghttp2
+session object and several callbacks::
+
+ static void initialize_nghttp2_session(http2_session_data *session_data) {
+ nghttp2_session_callbacks *callbacks;
+
+ nghttp2_session_callbacks_new(&callbacks);
+
+ nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
+
+ nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
+ on_frame_recv_callback);
+
+ nghttp2_session_callbacks_set_on_stream_close_callback(
+ callbacks, on_stream_close_callback);
+
+ nghttp2_session_callbacks_set_on_header_callback(callbacks,
+ on_header_callback);
+
+ nghttp2_session_callbacks_set_on_begin_headers_callback(
+ callbacks, on_begin_headers_callback);
+
+ nghttp2_session_server_new(&session_data->session, callbacks, session_data);
+
+ nghttp2_session_callbacks_del(callbacks);
+ }
+
+Since we are creating a server, we use `nghttp2_session_server_new()`
+to initialize the nghttp2 session object. We also setup 5 callbacks
+for the nghttp2 session, these are explained later.
+
+The server now begins by sending the server connection preface, which
+always consists of a SETTINGS frame.
+``send_server_connection_header()`` configures and submits it::
+
+ static int send_server_connection_header(http2_session_data *session_data) {
+ nghttp2_settings_entry iv[1] = {
+ {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
+ int rv;
+
+ rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
+ ARRLEN(iv));
+ if (rv != 0) {
+ warnx("Fatal error: %s", nghttp2_strerror(rv));
+ return -1;
+ }
+ return 0;
+ }
+
+In the example SETTINGS frame we've set
+SETTINGS_MAX_CONCURRENT_STREAMS to 100. `nghttp2_submit_settings()`
+is used to queue the frame for transmission, but note it only queues
+the frame for transmission, and doesn't actually send it. All
+functions in the ``nghttp2_submit_*()`` family have this property. To
+actually send the frame, `nghttp2_session_send()` should be used, as
+described later.
+
+Since bufferevent may buffer more than the first 24 bytes from the client, we
+have to process them here since libevent won't invoke callback functions for
+this pending data. To process the received data, we call the
+``session_recv()`` function::
+
+ static int session_recv(http2_session_data *session_data) {
+ ssize_t readlen;
+ struct evbuffer *input = bufferevent_get_input(session_data->bev);
+ size_t datalen = evbuffer_get_length(input);
+ unsigned char *data = evbuffer_pullup(input, -1);
+
+ readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
+ if (readlen < 0) {
+ warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
+ return -1;
+ }
+ if (evbuffer_drain(input, (size_t)readlen) != 0) {
+ warnx("Fatal error: evbuffer_drain failed");
+ return -1;
+ }
+ if (session_send(session_data) != 0) {
+ return -1;
+ }
+ return 0;
+ }
+
+In this function, we feed all unprocessed but already received data to
+the nghttp2 session object using the `nghttp2_session_mem_recv()`
+function. The `nghttp2_session_mem_recv()` function processes the data
+and may both invoke the previously setup callbacks and also queue
+outgoing frames. To send any pending outgoing frames, we immediately
+call ``session_send()``.
+
+The ``session_send()`` function is defined as follows::
+
+ static int session_send(http2_session_data *session_data) {
+ int rv;
+ rv = nghttp2_session_send(session_data->session);
+ if (rv != 0) {
+ warnx("Fatal error: %s", nghttp2_strerror(rv));
+ return -1;
+ }
+ return 0;
+ }
+
+The `nghttp2_session_send()` function serializes the frame into wire
+format and calls the ``send_callback()``, which is of type
+:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
+follows::
+
+ static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
+ size_t length, int flags _U_, void *user_data) {
+ http2_session_data *session_data = (http2_session_data *)user_data;
+ struct bufferevent *bev = session_data->bev;
+ /* Avoid excessive buffering in server side. */
+ if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
+ OUTPUT_WOULDBLOCK_THRESHOLD) {
+ return NGHTTP2_ERR_WOULDBLOCK;
+ }
+ bufferevent_write(bev, data, length);
+ return (ssize_t)length;
+ }
+
+Since we use bufferevent to abstract network I/O, we just write the
+data to the bufferevent object. Note that `nghttp2_session_send()`
+continues to write all frames queued so far. If we were writing the
+data to a non-blocking socket directly using the ``write()`` system
+call in the ``send_callback()``, we'd soon receive an ``EAGAIN`` or
+``EWOULDBLOCK`` error since sockets have a limited send buffer. If
+that happens, it's possible to return :macro:`NGHTTP2_ERR_WOULDBLOCK`
+to signal the nghttp2 library to stop sending further data. But here,
+when writing to the bufferevent, we have to regulate the amount data
+to buffered ourselves to avoid using huge amounts of memory. To
+achieve this, we check the size of the output buffer and if it reaches
+more than or equal to ``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop
+writing data and return :macro:`NGHTTP2_ERR_WOULDBLOCK`.
+
+The next bufferevent callback is ``readcb()``, which is invoked when
+data is available to read in the bufferevent input buffer::
+
+ static void readcb(struct bufferevent *bev _U_, void *ptr) {
+ http2_session_data *session_data = (http2_session_data *)ptr;
+ if (session_recv(session_data) != 0) {
+ delete_http2_session_data(session_data);
+ return;
+ }
+ }
+
+In this function, we just call ``session_recv()`` to process incoming
+data.
+
+The third bufferevent callback is ``writecb()``, which is invoked when all
+data in the bufferevent output buffer has been sent::
+
+ static void writecb(struct bufferevent *bev, void *ptr) {
+ http2_session_data *session_data = (http2_session_data *)ptr;
+ if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
+ return;
+ }
+ if (nghttp2_session_want_read(session_data->session) == 0 &&
+ nghttp2_session_want_write(session_data->session) == 0) {
+ delete_http2_session_data(session_data);
+ return;
+ }
+ if (session_send(session_data) != 0) {
+ delete_http2_session_data(session_data);
+ return;
+ }
+ }
+
+First we check whether we should drop the connection or not. The
+nghttp2 session object keeps track of reception and transmission of
+GOAWAY frames and other error conditions as well. Using this
+information, the nghttp2 session object can state whether the
+connection should be dropped or not. More specifically, if both
+`nghttp2_session_want_read()` and `nghttp2_session_want_write()`
+return 0, the connection is no-longer required and can be closed.
+Since we are using bufferevent and its deferred callback option, the
+bufferevent output buffer may still contain pending data when the
+``writecb()`` is called. To handle this, we check whether the output
+buffer is empty or not. If all of these conditions are met, we drop
+connection.
+
+Otherwise, we call ``session_send()`` to process the pending output
+data. Remember that in ``send_callback()``, we must not write all data to
+bufferevent to avoid excessive buffering. We continue processing pending data
+when the output buffer becomes empty.
+
+We have already described the nghttp2 callback ``send_callback()``. Let's
+learn about the remaining nghttp2 callbacks setup in
+``initialize_nghttp2_setup()`` function.
+
+The ``on_begin_headers_callback()`` function is invoked when the reception of
+a header block in HEADERS or PUSH_PROMISE frame is started::
+
+ static int on_begin_headers_callback(nghttp2_session *session,
+ const nghttp2_frame *frame,
+ void *user_data) {
+ http2_session_data *session_data = (http2_session_data *)user_data;
+ http2_stream_data *stream_data;
+
+ if (frame->hd.type != NGHTTP2_HEADERS ||
+ frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
+ return 0;
+ }
+ stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
+ nghttp2_session_set_stream_user_data(session, frame->hd.stream_id,
+ stream_data);
+ return 0;
+ }
+
+We are only interested in the HEADERS frame in this function. Since
+the HEADERS frame has several roles in the HTTP/2 protocol, we check
+that it is a request HEADERS, which opens new stream. If the frame is
+a request HEADERS, we create a ``http2_stream_data`` object to store
+the stream related data. We associate the created
+``http2_stream_data`` object with the stream in the nghttp2 session
+object using `nghttp2_set_stream_user_data()`. The
+``http2_stream_data`` object can later be easily retrieved from the
+stream, without searching through the doubly linked list.
+
+In this example server, we want to serve files relative to the current working
+directory in which the program was invoked. Each header name/value pair is
+emitted via ``on_header_callback`` function, which is called after
+``on_begin_headers_callback()``::
+
+ static int on_header_callback(nghttp2_session *session,
+ const nghttp2_frame *frame, const uint8_t *name,
+ size_t namelen, const uint8_t *value,
+ size_t valuelen, uint8_t flags _U_,
+ void *user_data _U_) {
+ http2_stream_data *stream_data;
+ const char PATH[] = ":path";
+ switch (frame->hd.type) {
+ case NGHTTP2_HEADERS:
+ if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
+ break;
+ }
+ stream_data =
+ nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
+ if (!stream_data || stream_data->request_path) {
+ break;
+ }
+ if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
+ size_t j;
+ for (j = 0; j < valuelen && value[j] != '?'; ++j)
+ ;
+ stream_data->request_path = percent_decode(value, j);
+ }
+ break;
+ }
+ return 0;
+ }
+
+We search for the ``:path`` header field among the request headers and
+store the requested path in the ``http2_stream_data`` object. In this
+example program, we ignore the ``:method`` header field and always
+treat the request as a GET request.
+
+The ``on_frame_recv_callback()`` function is invoked when a frame is
+fully received::
+
+ static int on_frame_recv_callback(nghttp2_session *session,
+ const nghttp2_frame *frame, void *user_data) {
+ http2_session_data *session_data = (http2_session_data *)user_data;
+ http2_stream_data *stream_data;
+ switch (frame->hd.type) {
+ case NGHTTP2_DATA:
+ case NGHTTP2_HEADERS:
+ /* Check that the client request has finished */
+ if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
+ stream_data =
+ nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
+ /* For DATA and HEADERS frame, this callback may be called after
+ on_stream_close_callback. Check that stream still alive. */
+ if (!stream_data) {
+ return 0;
+ }
+ return on_request_recv(session, session_data, stream_data);
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+ }
+
+First we retrieve the ``http2_stream_data`` object associated with the
+stream in ``on_begin_headers_callback()`` using
+`nghttp2_session_get_stream_user_data()`. If the requested path
+cannot be served for some reason (e.g. file is not found), we send a
+404 response using ``error_reply()``. Otherwise, we open
+the requested file and send its content. We send the header field
+``:status`` as a single response header.
+
+Sending the file content is performed by the ``send_response()`` function::
+
+ static int send_response(nghttp2_session *session, int32_t stream_id,
+ nghttp2_nv *nva, size_t nvlen, int fd) {
+ int rv;
+ nghttp2_data_provider data_prd;
+ data_prd.source.fd = fd;
+ data_prd.read_callback = file_read_callback;
+
+ rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
+ if (rv != 0) {
+ warnx("Fatal error: %s", nghttp2_strerror(rv));
+ return -1;
+ }
+ return 0;
+ }
+
+nghttp2 uses the :type:`nghttp2_data_provider` structure to send the
+entity body to the remote peer. The ``source`` member of this
+structure is a union, which can be either a void pointer or an int
+(which is intended to be used as file descriptor). In this example
+server, we use it as a file descriptor. We also set the
+``file_read_callback()`` callback function to read the contents of the
+file::
+
+ static ssize_t file_read_callback(nghttp2_session *session _U_,
+ int32_t stream_id _U_, uint8_t *buf,
+ size_t length, uint32_t *data_flags,
+ nghttp2_data_source *source,
+ void *user_data _U_) {
+ int fd = source->fd;
+ ssize_t r;
+ while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
+ ;
+ if (r == -1) {
+ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
+ }
+ if (r == 0) {
+ *data_flags |= NGHTTP2_DATA_FLAG_EOF;
+ }
+ return r;
+ }
+
+If an error occurs while reading the file, we return
+:macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. This tells the
+library to send RST_STREAM to the stream. When all data has been
+read, the :macro:`NGHTTP2_DATA_FLAG_EOF` flag is set to signal nghttp2
+that we have finished reading the file.
+
+The `nghttp2_submit_response()` function is used to send the response to the
+remote peer.
+
+The ``on_stream_close_callback()`` function is invoked when the stream
+is about to close::
+
+ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
+ uint32_t error_code _U_, void *user_data) {
+ http2_session_data *session_data = (http2_session_data *)user_data;
+ http2_stream_data *stream_data;
+
+ stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
+ if (!stream_data) {
+ return 0;
+ }
+ remove_stream(session_data, stream_data);
+ delete_http2_stream_data(stream_data);
+ return 0;
+ }
+
+Lastly, we destroy the ``http2_stream_data`` object in this function,
+since the stream is about to close and we no longer need the object.