mirror of
https://github.com/nghttp2/nghttp2.git
synced 2025-12-07 18:48:54 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e24e29ff14 | ||
|
|
2f23eac179 | ||
|
|
b3f6664bc6 |
@@ -1,57 +0,0 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -2
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
AlignEscapedNewlinesLeft: false
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
BreakBeforeBinaryOperators: false
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BinPackParameters: true
|
||||
ColumnLimit: 80
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
DerivePointerAlignment: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
IndentCaseLabels: false
|
||||
IndentWrappedFunctionNames: false
|
||||
IndentFunctionDeclarationAfterType: false
|
||||
MaxEmptyLinesToKeep: 1
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
NamespaceIndentation: None
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Right
|
||||
SpacesBeforeTrailingComments: 1
|
||||
Cpp11BracedListStyle: true
|
||||
Standard: Cpp11
|
||||
IndentWidth: 2
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
BreakBeforeBraces: Attach
|
||||
SpacesInParentheses: false
|
||||
SpacesInAngles: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
ContinuationIndentWidth: 4
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
||||
SpaceBeforeParens: ControlStatements
|
||||
DisableFormat: false
|
||||
...
|
||||
|
||||
38
.gitignore
vendored
38
.gitignore
vendored
@@ -1,18 +1,14 @@
|
||||
# emacs backup file
|
||||
*~
|
||||
|
||||
# autotools
|
||||
*.la
|
||||
*.lo
|
||||
*.m4
|
||||
*.o
|
||||
.deps/
|
||||
.libs/
|
||||
INSTALL
|
||||
*.lo
|
||||
*.la
|
||||
depcomp
|
||||
*.m4
|
||||
Makefile
|
||||
Makefile.in
|
||||
libtool
|
||||
missing
|
||||
autom4te.cache/
|
||||
compile
|
||||
config.guess
|
||||
config.h
|
||||
config.h.in
|
||||
@@ -20,14 +16,22 @@ config.log
|
||||
config.status
|
||||
config.sub
|
||||
configure
|
||||
depcomp
|
||||
install-sh
|
||||
libtool
|
||||
.deps/
|
||||
.libs
|
||||
lib/includes/nghttp2/nghttp2ver.h
|
||||
lib/libnghttp2.pc
|
||||
ltmain.sh
|
||||
missing
|
||||
stamp-h1
|
||||
.deps/
|
||||
INSTALL
|
||||
.DS_STORE
|
||||
compile
|
||||
test-driver
|
||||
|
||||
# test logs generated by `make check`
|
||||
*.log
|
||||
*.trs
|
||||
.dirstamp
|
||||
doc/index.rst
|
||||
doc/nghttp2.h.rst
|
||||
doc/nghttp2ver.h.rst
|
||||
doc/package_README.rst
|
||||
doc/tutorial-client.rst
|
||||
doc/tutorial-server.rst
|
||||
|
||||
37
.travis.yml
37
.travis.yml
@@ -1,37 +0,0 @@
|
||||
language: cpp
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
sudo: false
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.9
|
||||
- libstdc++-4.9-dev
|
||||
- autoconf
|
||||
- automake
|
||||
- autotools-dev
|
||||
- libtool
|
||||
- pkg-config
|
||||
- zlib1g-dev
|
||||
- libcunit1-dev
|
||||
- libssl-dev
|
||||
- libxml2-dev
|
||||
- libev-dev
|
||||
- libevent-dev
|
||||
- libjansson-dev
|
||||
- libjemalloc-dev
|
||||
before_install:
|
||||
- $CC --version
|
||||
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.9" CC="gcc-4.9"; fi
|
||||
- $CC --version
|
||||
before_script:
|
||||
- autoreconf -i
|
||||
- automake
|
||||
- autoconf
|
||||
- ./configure --enable-werror
|
||||
script:
|
||||
- make
|
||||
- make check
|
||||
18
CONTRIBUTION
18
CONTRIBUTION
@@ -1,18 +0,0 @@
|
||||
[The text below 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!
|
||||
2
COPYING
2
COPYING
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2012, 2014, 2015 Tatsuhiro Tsujikawa
|
||||
Copyright (c) 2012, 2014 Tatsuhiro Tsujikawa
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
# vim: ft=dockerfile:
|
||||
# Dockerfile to build nghttp2 android binary
|
||||
#
|
||||
# $ sudo docker build -t nghttp2-android - < Dockerfile.android
|
||||
#
|
||||
# After successful build, android binaries are located under
|
||||
# /root/build/nghttp2. You can copy the binary using docker cp. For
|
||||
# example, to copy nghttpx binary to host file system location
|
||||
# /path/to/dest, do this:
|
||||
#
|
||||
# $ sudo docker run -v /path/to/dest:/out nghttp2-android cp /root/build/nghttp2/src/nghttpx /out
|
||||
|
||||
FROM ubuntu:trusty
|
||||
|
||||
MAINTAINER Tatsuhiro Tsujikawa
|
||||
|
||||
ENV ANDROID_HOME /root/android
|
||||
ENV PREFIX $ANDROID_HOME/usr/local
|
||||
ENV TOOLCHAIN $ANDROID_HOME/toolchain
|
||||
ENV PATH $TOOLCHAIN/bin:$PATH
|
||||
|
||||
# It would be better to use nearest ubuntu archive mirror for faster
|
||||
# downloads.
|
||||
# RUN sed -ie 's/archive\.ubuntu/jp.archive.ubuntu/g' /etc/apt/sources.list
|
||||
|
||||
RUN apt-get update
|
||||
# genisoimage, libc6-i386 and lib32stdc++6 are required to decompress ndk.
|
||||
RUN apt-get install -y make binutils autoconf automake autotools-dev libtool \
|
||||
pkg-config git curl dpkg-dev libxml2-dev \
|
||||
genisoimage libc6-i386 lib32stdc++6
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN curl -L -O http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin && \
|
||||
chmod a+x android-ndk-r10c-linux-x86_64.bin && \
|
||||
./android-ndk-r10c-linux-x86_64.bin && \
|
||||
rm android-ndk-r10c-linux-x86_64.bin
|
||||
|
||||
WORKDIR /root/build/android-ndk-r10c
|
||||
RUN /bin/bash build/tools/make-standalone-toolchain.sh \
|
||||
--install-dir=$ANDROID_HOME/toolchain \
|
||||
--toolchain=arm-linux-androideabi-4.9 --llvm-version=3.5 \
|
||||
--system=linux-x86_64
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN git clone https://github.com/tatsuhiro-t/spdylay
|
||||
WORKDIR /root/build/spdylay
|
||||
RUN autoreconf -i && \
|
||||
./configure \
|
||||
--disable-shared \
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--prefix=$PREFIX \
|
||||
--without-libxml2 \
|
||||
--disable-src \
|
||||
--disable-examples \
|
||||
CPPFLAGS="-I$PREFIX/include" \
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-L$PREFIX/lib" && \
|
||||
make install
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN curl -L -O https://www.openssl.org/source/openssl-1.0.2a.tar.gz && \
|
||||
tar xf openssl-1.0.2a.tar.gz && \
|
||||
rm openssl-1.0.2a.tar.gz
|
||||
|
||||
WORKDIR /root/build/openssl-1.0.2a
|
||||
RUN export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi- && \
|
||||
./Configure --prefix=$PREFIX android && \
|
||||
make && make install_sw
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN curl -L -O http://dist.schmorp.de/libev/libev-4.19.tar.gz && \
|
||||
curl -L -O https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed/raw/80a8f003b5d1091eae497c5995bbaa68096e739b/libev-4.19-android.patch && \
|
||||
tar xf libev-4.19.tar.gz && \
|
||||
rm libev-4.19.tar.gz
|
||||
|
||||
WORKDIR /root/build/libev-4.19
|
||||
RUN patch -p1 < ../libev-4.19-android.patch && \
|
||||
./configure \
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--prefix=$PREFIX \
|
||||
--disable-shared \
|
||||
--enable-static \
|
||||
CPPFLAGS=-I$PREFIX/include \
|
||||
LDFLAGS=-L$PREFIX/lib && \
|
||||
make install
|
||||
|
||||
WORKDIR /root/build
|
||||
RUN git clone https://github.com/tatsuhiro-t/nghttp2
|
||||
WORKDIR /root/build/nghttp2
|
||||
RUN autoreconf -i && \
|
||||
./configure \
|
||||
--disable-shared \
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--with-xml-prefix="$PREFIX" \
|
||||
--without-libxml2 \
|
||||
--disable-python-bindings \
|
||||
--disable-examples \
|
||||
--disable-threads \
|
||||
LIBSPDYLAY_CFLAGS=-I$PREFIX/usr/local/include \
|
||||
LIBSPDYLAY_LIBS="-L$PREFIX/usr/local/lib -lspdylay" \
|
||||
CPPFLAGS="-fPIE -I$PREFIX/include" \
|
||||
CXXFLAGS="-fno-strict-aliasing" \
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-fPIE -pie -L$PREFIX/lib" && \
|
||||
make && \
|
||||
arm-linux-androideabi-strip src/nghttpx src/nghttpd src/nghttp
|
||||
20
Makefile.am
20
Makefile.am
@@ -1,4 +1,4 @@
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -20,24 +20,10 @@
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
SUBDIRS = lib third-party src examples python tests integration-tests \
|
||||
doc contrib
|
||||
SUBDIRS = lib third-party src examples python tests doc
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
dist_doc_DATA = README.rst
|
||||
|
||||
EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \
|
||||
Dockerfile.android
|
||||
|
||||
.PHONY: clang-format
|
||||
|
||||
# Format source files using clang-format. Don't format source files
|
||||
# under third-party directory since we are not responsible for thier
|
||||
# coding style.
|
||||
clang-format:
|
||||
CLANGFORMAT=`git config --get clangformat.binary`; \
|
||||
test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \
|
||||
$${CLANGFORMAT} -i lib/*.{c,h} lib/includes/nghttp2/*.h \
|
||||
src/*.{c,cc,h} src/includes/nghttp2/*.h examples/*.{c,cc} \
|
||||
tests/*.{c,h}
|
||||
EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make
|
||||
|
||||
952
README.rst
952
README.rst
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
#
|
||||
# Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
#
|
||||
@@ -36,12 +36,6 @@ PATH=$TOOLCHAIN/bin:$PATH
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--with-xml-prefix="$PREFIX" \
|
||||
--without-libxml2 \
|
||||
--disable-python-bindings \
|
||||
--disable-examples \
|
||||
--enable-werror \
|
||||
CC=clang \
|
||||
CXX=clang++ \
|
||||
CPPFLAGS="-fPIE -I$PREFIX/include" \
|
||||
CPPFLAGS="-I$PREFIX/include" \
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-fPIE -pie -L$PREFIX/lib"
|
||||
LDFLAGS="-L$PREFIX/lib"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
#
|
||||
# Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
#
|
||||
|
||||
452
configure.ac
452
configure.ac
@@ -1,6 +1,6 @@
|
||||
dnl nghttp2 - HTTP/2 C Library
|
||||
dnl nghttp2 - HTTP/2.0 C Library
|
||||
|
||||
dnl Copyright (c) 2012, 2013, 2014, 2015 Tatsuhiro Tsujikawa
|
||||
dnl Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa
|
||||
|
||||
dnl Permission is hereby granted, free of charge, to any person obtaining
|
||||
dnl a copy of this software and associated documentation files (the
|
||||
@@ -20,36 +20,15 @@ dnl NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
dnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
dnl Do not change user variables!
|
||||
dnl http://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT([nghttp2], [0.7.15], [t-tujikawa@users.sourceforge.net])
|
||||
AC_USE_SYSTEM_EXTENSIONS
|
||||
|
||||
AC_INIT([nghttp2], [0.3.2], [t-tujikawa@users.sourceforge.net])
|
||||
LT_PREREQ([2.2.6])
|
||||
LT_INIT()
|
||||
|
||||
AC_CANONICAL_BUILD
|
||||
AC_CANONICAL_HOST
|
||||
AC_CANONICAL_TARGET
|
||||
|
||||
AM_INIT_AUTOMAKE([subdir-objects])
|
||||
# comment out for now since this requires automake 1.13 or higher and
|
||||
# travis has older one.
|
||||
# AM_EXTRA_RECURSIVE_TARGETS([it])
|
||||
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
dnl See versioning rule:
|
||||
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
AC_SUBST(LT_CURRENT, 13)
|
||||
AC_SUBST(LT_REVISION, 4)
|
||||
AC_SUBST(LT_AGE, 8)
|
||||
AC_SUBST(LT_CURRENT, 2)
|
||||
AC_SUBST(LT_REVISION, 2)
|
||||
AC_SUBST(LT_AGE, 0)
|
||||
|
||||
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
|
||||
minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
|
||||
@@ -59,21 +38,20 @@ PACKAGE_VERSION_NUM=`printf "0x%02x%02x%02x" "$major" "$minor" "$patch"`
|
||||
|
||||
AC_SUBST(PACKAGE_VERSION_NUM)
|
||||
|
||||
AC_CANONICAL_BUILD
|
||||
AC_CANONICAL_HOST
|
||||
AC_CANONICAL_TARGET
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AM_INIT_AUTOMAKE([subdir-objects])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
dnl Checks for command-line options
|
||||
AC_ARG_ENABLE([werror],
|
||||
[AS_HELP_STRING([--enable-werror],
|
||||
AC_ARG_ENABLE([maintainer-mode],
|
||||
[AS_HELP_STRING([--enable-maintainer-mode],
|
||||
[Turn on compile time warnings])],
|
||||
[werror=$enableval], [werror=no])
|
||||
|
||||
AC_ARG_ENABLE([debug],
|
||||
[AS_HELP_STRING([--enable-debug],
|
||||
[Turn on debug output])],
|
||||
[debug=$enableval], [debug=no])
|
||||
|
||||
AC_ARG_ENABLE([threads],
|
||||
[AS_HELP_STRING([--disable-threads],
|
||||
[Turn off threading in apps])],
|
||||
[threads=$enableval], [threads=yes])
|
||||
[maintainer_mode=$enableval], [maintainer_mode=no])
|
||||
|
||||
AC_ARG_ENABLE([app],
|
||||
[AS_HELP_STRING([--enable-app],
|
||||
@@ -85,11 +63,6 @@ AC_ARG_ENABLE([hpack-tools],
|
||||
[Build HPACK tools [default=check]])],
|
||||
[request_hpack_tools=$enableval], [request_hpack_tools=check])
|
||||
|
||||
AC_ARG_ENABLE([asio-lib],
|
||||
[AS_HELP_STRING([--enable-asio-lib],
|
||||
[Build C++ libnghttp2_asio library [default=no]])],
|
||||
[request_asio_lib=$enableval], [request_asio_lib=no])
|
||||
|
||||
AC_ARG_ENABLE([examples],
|
||||
[AS_HELP_STRING([--enable-examples],
|
||||
[Build examples [default=check]])],
|
||||
@@ -101,25 +74,15 @@ AC_ARG_ENABLE([python-bindings],
|
||||
[request_python_bindings=$enableval], [request_python_bindings=check])
|
||||
|
||||
AC_ARG_ENABLE([failmalloc],
|
||||
[AS_HELP_STRING([--disable-failmalloc],
|
||||
[Do not build failmalloc test program])],
|
||||
[request_failmalloc=$enableval], [request_failmalloc=yes])
|
||||
[AS_HELP_STRING([--enable-failmalloc],
|
||||
[Build failmalloc test program [default=no]])],
|
||||
[request_failmalloc=$enableval], [request_failmalloc=no])
|
||||
|
||||
AC_ARG_WITH([libxml2],
|
||||
[AS_HELP_STRING([--with-libxml2],
|
||||
[Use libxml2 [default=check]])],
|
||||
[request_libxml2=$withval], [request_libxml2=check])
|
||||
|
||||
AC_ARG_WITH([jemalloc],
|
||||
[AS_HELP_STRING([--with-jemalloc],
|
||||
[Use jemalloc [default=check]])],
|
||||
[request_jemalloc=$withval], [request_jemalloc=check])
|
||||
|
||||
AC_ARG_WITH([spdylay],
|
||||
[AS_HELP_STRING([--with-spdylay],
|
||||
[Use spdylay [default=check]])],
|
||||
[request_spdylay=$withval], [request_spdylay=check])
|
||||
|
||||
AC_ARG_WITH([cython],
|
||||
[AS_HELP_STRING([--with-cython=PATH],
|
||||
[Use cython in given PATH])],
|
||||
@@ -131,12 +94,10 @@ AC_ARG_VAR([CYTHON], [the Cython executable])
|
||||
dnl Checks for programs
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AC_PROG_CPP
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_LN_S
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_MKDIR_P
|
||||
|
||||
AM_PROG_CC_C_O
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
if [test "x$request_python_bindings" != "xno"]; then
|
||||
@@ -151,29 +112,14 @@ else
|
||||
AC_SUBST([CYTHON])
|
||||
fi
|
||||
|
||||
#
|
||||
# If we're running GCC or clang define _U_ to be "__attribute__((unused))"
|
||||
# so we can use _U_ to flag unused function parameters and not get warnings
|
||||
# about them. Otherwise, define _U_ to be an empty string so that _U_ used
|
||||
# to flag an unused function parameters will compile with other compilers.
|
||||
#
|
||||
# XXX - similar hints for other compilers?
|
||||
#
|
||||
if test "x$GCC" = "xyes" -o "x$CC" = "xclang" ; then
|
||||
AC_DEFINE([_U_], [__attribute__((unused))], [Hint to the compiler that a function parameters is not used])
|
||||
else
|
||||
AC_DEFINE([_U_], , [Hint to the compiler that a function parameters is not used])
|
||||
fi
|
||||
|
||||
AX_CXX_COMPILE_STDCXX_11([noext], [optional])
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
|
||||
# Check that std::chrono::steady_clock is available. In particular,
|
||||
# gcc 4.6 does not have one, but has monotonic_clock which is the old
|
||||
# name existed in the pre-standard draft. If steady_clock is not
|
||||
# available, don't define HAVE_STEADY_CLOCK and replace steady_clock
|
||||
# with monotonic_clock.
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_MSG_CHECKING([whether std::chrono::steady_clock is available])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
[[
|
||||
@@ -186,87 +132,49 @@ auto tp = std::chrono::steady_clock::now();
|
||||
[Define to 1 if you have the `std::chrono::steady_clock`.])
|
||||
AC_MSG_RESULT([yes])],
|
||||
[AC_MSG_RESULT([no])])
|
||||
|
||||
# Check that std::future is available.
|
||||
AC_MSG_CHECKING([whether std::future is available])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
[[
|
||||
#include <vector>
|
||||
#include <future>
|
||||
]],
|
||||
[[
|
||||
std::vector<std::future<int>> v;
|
||||
]])],
|
||||
[AC_DEFINE([HAVE_STD_FUTURE], [1],
|
||||
[Define to 1 if you have the `std::future`.])
|
||||
have_std_future=yes
|
||||
AC_MSG_RESULT([yes])],
|
||||
[have_std_future=no
|
||||
AC_MSG_RESULT([no])])
|
||||
|
||||
# Check that std::map::emplace is available for g++-4.7.
|
||||
AC_MSG_CHECKING([whether std::map::emplace is available])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
[[
|
||||
#include <map>
|
||||
]],
|
||||
[[
|
||||
std::map<int, int>().emplace(1, 2);
|
||||
]])],
|
||||
[AC_DEFINE([HAVE_STD_MAP_EMPLACE], [1],
|
||||
[Define to 1 if you have the `std::map::emplace`.])
|
||||
have_std_map_emplace=yes
|
||||
AC_MSG_RESULT([yes])],
|
||||
[have_std_map_emplace=no
|
||||
AC_MSG_RESULT([no])])
|
||||
|
||||
AC_LANG_POP()
|
||||
|
||||
# Checks for libraries.
|
||||
|
||||
# Additional libraries required for tests.
|
||||
TESTLDADD=
|
||||
TESTS_LIBS=
|
||||
|
||||
# Additional libraries required for programs under src directory.
|
||||
APPLDFLAGS=
|
||||
SRC_LIBS=
|
||||
|
||||
LIBS_OLD=$LIBS
|
||||
# Search for dlsym function, which is used in tests. Linux needs -ldl,
|
||||
# but netbsd does not need it.
|
||||
AC_SEARCH_LIBS([dlsym], [dl])
|
||||
TESTLDADD="$LIBS $TESTLDADD"
|
||||
TESTS_LIBS="$LIBS $TESTS_LIBS"
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
LIBS_OLD=$LIBS
|
||||
AC_SEARCH_LIBS([clock_gettime], [rt],
|
||||
[AC_DEFINE([HAVE_CLOCK_GETTIME], [1],
|
||||
[Define to 1 if you have the `clock_gettime`.])])
|
||||
APPLDFLAGS="$LIBS $APPLDFLAGS"
|
||||
SRC_LIBS="$LIBS $SRC_LIBS"
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
case "$host" in
|
||||
*android*)
|
||||
android_build=yes
|
||||
# android does not need -pthread, but needs followng 3 libs for C++
|
||||
APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++"
|
||||
# android does not need -pthread, but needs followng 2 libs for C++
|
||||
SRC_LIBS="$SRC_LIBS -lstdc++ -lsupc++"
|
||||
;;
|
||||
*)
|
||||
PTHREAD_LDFLAGS="-pthread"
|
||||
APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS"
|
||||
SRC_LIBS="$SRC_LIBS -pthread"
|
||||
;;
|
||||
esac
|
||||
|
||||
# zlib
|
||||
if test "x$android_build" = "xyes"; then
|
||||
# Use zlib provided by NDK
|
||||
APPLDFLAGS="-lz $APPLDFLAGS"
|
||||
have_zlib=yes
|
||||
LIBS="-lz $LIBS"
|
||||
else
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no])
|
||||
|
||||
if test "x${have_zlib}" = "xno"; then
|
||||
AC_MSG_NOTICE($ZLIB_PKG_ERRORS)
|
||||
fi
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3])
|
||||
LIBS="$ZLIB_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $ZLIB_CFLAGS"
|
||||
fi
|
||||
|
||||
# cunit
|
||||
@@ -298,22 +206,6 @@ fi
|
||||
|
||||
AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ])
|
||||
|
||||
# libev (for src)
|
||||
# libev does not have pkg-config file. Check it in an old way.
|
||||
LIBS_OLD=$LIBS
|
||||
# android requires -lm for floor
|
||||
AC_CHECK_LIB([ev], [ev_time], [have_libev=yes], [have_libev=no], [-lm])
|
||||
if test "x${have_libev}" = "xyes"; then
|
||||
AC_CHECK_HEADER([ev.h], [have_libev=yes], [have_libev=no])
|
||||
if test "x${have_libev}" = "xyes"; then
|
||||
LIBEV_LIBS=-lev
|
||||
LIBEV_CFLAGS=
|
||||
AC_SUBST([LIBEV_LIBS])
|
||||
AC_SUBST([LIBEV_CFLAGS])
|
||||
fi
|
||||
fi
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
# openssl (for src)
|
||||
PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1],
|
||||
[have_openssl=yes], [have_openssl=no])
|
||||
@@ -321,7 +213,7 @@ if test "x${have_openssl}" = "xno"; then
|
||||
AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
|
||||
fi
|
||||
|
||||
# libevent_openssl (for examples)
|
||||
# libevent_openssl (for src)
|
||||
# 2.0.8 is required because we use evconnlistener_set_error_cb()
|
||||
PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],
|
||||
[have_libevent_openssl=yes], [have_libevent_openssl=no])
|
||||
@@ -329,13 +221,10 @@ if test "x${have_libevent_openssl}" = "xno"; then
|
||||
AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS)
|
||||
fi
|
||||
|
||||
# jansson (for src/nghttp, src/deflatehd and src/inflatehd)
|
||||
# jansson (for hdtest/deflatehd and hdtest/inflatehd)
|
||||
PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5],
|
||||
[have_jansson=yes], [have_jansson=no])
|
||||
if test "x${have_jansson}" == "xyes"; then
|
||||
AC_DEFINE([HAVE_JANSSON], [1],
|
||||
[Define to 1 if you have `libjansson` library.])
|
||||
else
|
||||
if test "x${have_jansson}" == "xno"; then
|
||||
AC_MSG_NOTICE($JANSSON_PKG_ERRORS)
|
||||
fi
|
||||
|
||||
@@ -355,85 +244,23 @@ fi
|
||||
|
||||
AM_CONDITIONAL([HAVE_LIBXML2], [ test "x${have_libxml2}" = "xyes" ])
|
||||
|
||||
# jemalloc
|
||||
have_jemalloc=no
|
||||
if test "x${request_jemalloc}" != "xno"; then
|
||||
LIBS_OLD=$LIBS
|
||||
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||
[$PTHREAD_LDFLAGS])
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes"; then
|
||||
jemalloc_libs=${ac_cv_search_malloc_stats_print}
|
||||
else
|
||||
# On Darwin, malloc_stats_print is je_malloc_stats_print
|
||||
AC_SEARCH_LIBS([je_malloc_stats_print], [jemalloc], [have_jemalloc=yes], [],
|
||||
[$PTHREAD_LDFLAGS])
|
||||
LIBS=$LIBS_OLD
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes"; then
|
||||
jemalloc_libs=${ac_cv_search_je_malloc_stats_print}
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${have_jemalloc}" = "xyes" &&
|
||||
test "x${jemalloc_libs}" != "xnone required"; then
|
||||
JEMALLOC_LIBS=${jemalloc_libs}
|
||||
AC_SUBST([JEMALLOC_LIBS])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_jemalloc}" = "xyes" &&
|
||||
test "x${have_jemalloc}" != "xyes"; then
|
||||
AC_MSG_ERROR([jemalloc was requested (--with-jemalloc) but not found])
|
||||
fi
|
||||
|
||||
# spdylay (for src/nghttpx and src/h2load)
|
||||
have_spdylay=no
|
||||
if test "x${request_spdylay}" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.3.2],
|
||||
# spdylay (for src/nghttpx)
|
||||
PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.2.3],
|
||||
[have_spdylay=yes], [have_spdylay=no])
|
||||
if test "x${have_spdylay}" = "xyes"; then
|
||||
AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.])
|
||||
else
|
||||
AC_MSG_NOTICE($LIBSPDYLAY_PKG_ERRORS)
|
||||
AC_MSG_NOTICE([The SPDY support in nghttpx and h2load will be disabled.])
|
||||
AC_MSG_NOTICE([The SPDY support in nghttpx will be disabled.])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x${request_spdylay}" = "xyes" &&
|
||||
test "x${have_spdylay}" != "xyes"; then
|
||||
AC_MSG_ERROR([spdylay was requested (--with-spdylay) but not found])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([HAVE_SPDYLAY], [ test "x${have_spdylay}" = "xyes" ])
|
||||
|
||||
# Check Boost Asio library
|
||||
have_asio_lib=no
|
||||
|
||||
if test "x${request_asio_lib}" = "xyes"; then
|
||||
AX_BOOST_BASE([1.54.0], [have_boost_base=yes], [have_boost_base=no])
|
||||
|
||||
if test "x${have_boost_base}" = "xyes"; then
|
||||
AX_BOOST_ASIO()
|
||||
AX_BOOST_SYSTEM()
|
||||
AX_BOOST_THREAD()
|
||||
|
||||
if test "x${ax_cv_boost_asio}" = "xyes" &&
|
||||
test "x${ax_cv_boost_system}" = "xyes" &&
|
||||
test "x${ax_cv_boost_thread}" = "xyes"; then
|
||||
have_asio_lib=yes
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL
|
||||
# and libev
|
||||
# The nghttp, nghttpd and nghttpx under src depend on OpenSSL and
|
||||
# libevent_openssl
|
||||
enable_app=no
|
||||
if test "x${request_app}" != "xno" &&
|
||||
test "x${have_zlib}" = "xyes" &&
|
||||
test "x${have_openssl}" = "xyes" &&
|
||||
test "x${have_libev}" = "xyes"; then
|
||||
test "x${have_libevent_openssl}" = "xyes"; then
|
||||
enable_app=yes
|
||||
fi
|
||||
|
||||
@@ -458,16 +285,6 @@ fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_HPACK_TOOLS], [ test "x${enable_hpack_tools}" = "xyes" ])
|
||||
|
||||
# C++ library libnghttp2_asio
|
||||
|
||||
enable_asio_lib=no
|
||||
if test "x${request_asio_lib}" != "xno" &&
|
||||
test "x${have_asio_lib}" = "xyes"; then
|
||||
enable_asio_lib=yes
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_ASIO_LIB], [ test "x${enable_asio_lib}" = "xyes" ])
|
||||
|
||||
# The example programs depend on OpenSSL and libevent_openssl
|
||||
enable_examples=no
|
||||
if test "x${request_examples}" != "xno" &&
|
||||
@@ -500,39 +317,30 @@ fi
|
||||
AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS],
|
||||
[test "x${enable_python_bindings}" = "xyes"])
|
||||
|
||||
# Produce cython conditional, so that we can distribute generated C
|
||||
# source
|
||||
AM_CONDITIONAL([HAVE_CYTHON], [test "x${CYTHON}" != "x"])
|
||||
|
||||
# failmalloc tests
|
||||
enable_failmalloc=no
|
||||
if test "x${request_failmalloc}" = "xyes"; then
|
||||
enable_failmalloc=yes
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ])
|
||||
|
||||
# Checks for header files.
|
||||
AC_HEADER_ASSERT
|
||||
AC_CHECK_HEADERS([ \
|
||||
arpa/inet.h \
|
||||
fcntl.h \
|
||||
inttypes.h \
|
||||
limits.h \
|
||||
netdb.h \
|
||||
netinet/in.h \
|
||||
pwd.h \
|
||||
stddef.h \
|
||||
stdint.h \
|
||||
stdlib.h \
|
||||
string.h \
|
||||
sys/socket.h \
|
||||
sys/time.h \
|
||||
syslog.h \
|
||||
time.h \
|
||||
unistd.h \
|
||||
])
|
||||
|
||||
case "${host}" in
|
||||
*mingw*)
|
||||
# For ntohl, ntohs in Windows
|
||||
AC_CHECK_HEADERS([winsock2.h])
|
||||
;;
|
||||
esac
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_TYPE_SIZE_T
|
||||
AC_TYPE_SSIZE_T
|
||||
@@ -540,133 +348,42 @@ AC_TYPE_UINT8_T
|
||||
AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT32_T
|
||||
AC_TYPE_UINT64_T
|
||||
AC_TYPE_INT8_T
|
||||
AC_TYPE_INT16_T
|
||||
AC_TYPE_INT32_T
|
||||
AC_TYPE_INT64_T
|
||||
AC_TYPE_OFF_T
|
||||
AC_TYPE_PID_T
|
||||
AC_TYPE_UID_T
|
||||
AC_CHECK_TYPES([ptrdiff_t])
|
||||
AC_C_BIGENDIAN
|
||||
AC_C_INLINE
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
AC_CHECK_MEMBER([struct tm.tm_gmtoff], [have_struct_tm_tm_gmtoff=yes],
|
||||
[have_struct_tm_tm_gmtoff=no], [[#include <time.h>]])
|
||||
|
||||
if test "x$have_struct_tm_tm_gmtoff" = "xyes"; then
|
||||
AC_DEFINE([HAVE_STRUCT_TM_TM_GMTOFF], [1],
|
||||
[Define to 1 if you have `struct tm.tm_gmtoff` member.])
|
||||
fi
|
||||
|
||||
# Check size of pointer to decide we need 8 bytes alignment
|
||||
# adjustment.
|
||||
AC_CHECK_SIZEOF([int *])
|
||||
|
||||
# Checks for library functions.
|
||||
if test "x$cross_compiling" != "xyes"; then
|
||||
AC_FUNC_MALLOC
|
||||
fi
|
||||
|
||||
AC_FUNC_CHOWN
|
||||
AC_FUNC_ERROR_AT_LINE
|
||||
AC_FUNC_FORK
|
||||
# Don't check realloc, since LeakSanitizer detects memory leak during check
|
||||
# AC_FUNC_REALLOC
|
||||
AC_FUNC_STRERROR_R
|
||||
AC_FUNC_STRNLEN
|
||||
|
||||
AC_CHECK_FUNCS([ \
|
||||
_Exit \
|
||||
accept4 \
|
||||
dup2 \
|
||||
getcwd \
|
||||
getpwnam \
|
||||
localtime_r \
|
||||
memchr \
|
||||
memmove \
|
||||
memset \
|
||||
socket \
|
||||
sqrt \
|
||||
strchr \
|
||||
strdup \
|
||||
strerror \
|
||||
strndup \
|
||||
strstr \
|
||||
strtol \
|
||||
strtoul \
|
||||
timegm \
|
||||
])
|
||||
|
||||
# timerfd_create was added in linux kernel 2.6.25
|
||||
dnl Windows library for winsock2
|
||||
case "${host}" in
|
||||
*mingw*)
|
||||
LIBS="$LIBS -lws2_32"
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_CHECK_FUNC([timerfd_create],
|
||||
[have_timerfd_create=yes], [have_timerfd_create=no])
|
||||
|
||||
|
||||
# Checks for epoll availability, primarily for examples/tiny-nghttpd
|
||||
AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no])
|
||||
|
||||
AM_CONDITIONAL([ENABLE_TINY_NGHTTPD],
|
||||
[ test "x${have_epoll}" = "xyes" &&
|
||||
test "x${have_timerfd_create}" = "xyes"])
|
||||
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
CFLAGS=
|
||||
|
||||
if test "x$werror" != "xno"; then
|
||||
AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wextra], [CFLAGS="$CFLAGS -Wextra"])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror], [CFLAGS="$CFLAGS -Werror"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wmissing-prototypes], [CFLAGS="$CFLAGS -Wmissing-prototypes"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wstrict-prototypes], [CFLAGS="$CFLAGS -Wstrict-prototypes"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wmissing-declarations], [CFLAGS="$CFLAGS -Wmissing-declarations"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wpointer-arith], [CFLAGS="$CFLAGS -Wpointer-arith"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wdeclaration-after-statement], [CFLAGS="$CFLAGS -Wdeclaration-after-statement"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wformat-security], [CFLAGS="$CFLAGS -Wformat-security"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wwrite-strings], [CFLAGS="$CFLAGS -Wwrite-strings"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wshadow], [CFLAGS="$CFLAGS -Wshadow"])
|
||||
AX_CHECK_COMPILE_FLAG([-Winline], [CFLAGS="$CFLAGS -Winline"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wnested-externs], [CFLAGS="$CFLAGS -Wnested-externs"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wfloat-equal], [CFLAGS="$CFLAGS -Wfloat-equal"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wundef], [CFLAGS="$CFLAGS -Wundef"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wendif-labels], [CFLAGS="$CFLAGS -Wendif-labels"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wempty-body], [CFLAGS="$CFLAGS -Wempty-body"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wcast-align], [CFLAGS="$CFLAGS -Wcast-align"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wclobbered], [CFLAGS="$CFLAGS -Wclobbered"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wvla], [CFLAGS="$CFLAGS -Wvla"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wpragmas], [CFLAGS="$CFLAGS -Wpragmas"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wunreachable-code], [CFLAGS="$CFLAGS -Wunreachable-code"])
|
||||
AX_CHECK_COMPILE_FLAG([-Waddress], [CFLAGS="$CFLAGS -Waddress"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wattributes], [CFLAGS="$CFLAGS -Wattributes"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wdiv-by-zero], [CFLAGS="$CFLAGS -Wdiv-by-zero"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wshorten-64-to-32], [CFLAGS="$CFLAGS -Wshorten-64-to-32"])
|
||||
|
||||
# Only work with Clang for the moment
|
||||
AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS="$CFLAGS -Wheader-guard"])
|
||||
if test "x$maintainer_mode" != "xno"; then
|
||||
CFLAGS="$CFLAGS -Wall -Wextra -Werror"
|
||||
CFLAGS="$CFLAGS -Wmissing-prototypes -Wstrict-prototypes"
|
||||
CFLAGS="$CFLAGS -Wmissing-declarations -Wpointer-arith"
|
||||
CFLAGS="$CFLAGS -Wdeclaration-after-statement"
|
||||
CFLAGS="$CFLAGS -Wformat-security"
|
||||
CFLAGS="$CFLAGS -Wwrite-strings -Wshadow -Winline -Wnested-externs"
|
||||
CFLAGS="$CFLAGS -Wfloat-equal -Wundef -Wendif-labels -Wempty-body"
|
||||
CFLAGS="$CFLAGS -Wcast-align -Wclobbered -Wvla"
|
||||
CFLAGS="$CFLAGS -Wno-unused-parameter"
|
||||
fi
|
||||
|
||||
WARNCFLAGS=$CFLAGS
|
||||
CFLAGS=$ac_save_CFLAGS
|
||||
|
||||
AC_SUBST([WARNCFLAGS])
|
||||
|
||||
if test "x$debug" != "xno"; then
|
||||
AC_DEFINE([DEBUGBUILD], [1], [Define to 1 to enable debug output.])
|
||||
fi
|
||||
|
||||
enable_threads=yes
|
||||
# Some platform does not have working std::future. We disable
|
||||
# threading for those platforms.
|
||||
if test "x$threads" != "xyes" ||
|
||||
test "x$have_std_future" != "xyes"; then
|
||||
enable_threads=no
|
||||
AC_DEFINE([NOTHREADS], [1], [Define to 1 if you want to disable threads.])
|
||||
fi
|
||||
|
||||
AC_SUBST([TESTLDADD])
|
||||
AC_SUBST([APPLDFLAGS])
|
||||
AC_SUBST([TESTS_LIBS])
|
||||
AC_SUBST([SRC_LIBS])
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
@@ -678,33 +395,16 @@ AC_CONFIG_FILES([
|
||||
tests/testdata/Makefile
|
||||
third-party/Makefile
|
||||
src/Makefile
|
||||
src/includes/Makefile
|
||||
src/libnghttp2_asio.pc
|
||||
examples/Makefile
|
||||
python/Makefile
|
||||
python/setup.py
|
||||
integration-tests/Makefile
|
||||
integration-tests/config.go
|
||||
integration-tests/setenv
|
||||
doc/Makefile
|
||||
doc/conf.py
|
||||
doc/index.rst
|
||||
doc/package_README.rst
|
||||
doc/tutorial-client.rst
|
||||
doc/tutorial-server.rst
|
||||
doc/tutorial-hpack.rst
|
||||
doc/nghttpx-howto.rst
|
||||
doc/h2load-howto.rst
|
||||
doc/libnghttp2_asio.rst
|
||||
doc/python-apiref.rst
|
||||
doc/building-android-binary.rst
|
||||
doc/nghttp2.h.rst
|
||||
doc/nghttp2ver.h.rst
|
||||
doc/asio_http2.h.rst
|
||||
doc/asio_http2_server.h.rst
|
||||
doc/asio_http2_client.h.rst
|
||||
doc/contribute.rst
|
||||
contrib/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -715,7 +415,6 @@ AC_MSG_NOTICE([summary of build options:
|
||||
Install prefix: ${prefix}
|
||||
C compiler: ${CC}
|
||||
CFLAGS: ${CFLAGS}
|
||||
WARNCFLAGS: ${WARNCFLAGS}
|
||||
LDFLAGS: ${LDFLAGS}
|
||||
LIBS: ${LIBS}
|
||||
CPPFLAGS: ${CPPFLAGS}
|
||||
@@ -724,7 +423,6 @@ AC_MSG_NOTICE([summary of build options:
|
||||
CXXFLAGS: ${CXXFLAGS}
|
||||
CXXCPP: ${CXXCPP}
|
||||
Library types: Shared=${enable_shared}, Static=${enable_static}
|
||||
Python:
|
||||
Python: ${PYTHON}
|
||||
PYTHON_VERSION: ${PYTHON_VERSION}
|
||||
pyexecdir: ${pyexecdir}
|
||||
@@ -732,27 +430,15 @@ AC_MSG_NOTICE([summary of build options:
|
||||
PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS}
|
||||
PYTHON_LDFLAGS: ${PYTHON_LDFLAGS}
|
||||
Cython: ${CYTHON}
|
||||
Test:
|
||||
CUnit: ${have_cunit}
|
||||
Failmalloc: ${enable_failmalloc}
|
||||
Libs:
|
||||
OpenSSL: ${have_openssl}
|
||||
Libxml2: ${have_libxml2}
|
||||
Libev: ${have_libev}
|
||||
Libevent(SSL): ${have_libevent_openssl}
|
||||
Spdylay: ${have_spdylay}
|
||||
Jansson: ${have_jansson}
|
||||
Jemalloc: ${have_jemalloc}
|
||||
Boost CPPFLAGS: ${BOOST_CPPFLAGS}
|
||||
Boost LDFLAGS: ${BOOST_LDFLAGS}
|
||||
Boost::ASIO: ${BOOST_ASIO_LIB}
|
||||
Boost::System: ${BOOST_SYSTEM_LIB}
|
||||
Boost::Thread: ${BOOST_THREAD_LIB}
|
||||
Features:
|
||||
Applications: ${enable_app}
|
||||
HPACK tools: ${enable_hpack_tools}
|
||||
Libnghttp2_asio:${enable_asio_lib}
|
||||
Examples: ${enable_examples}
|
||||
Python bindings:${enable_python_bindings}
|
||||
Threading: ${enable_threads}
|
||||
Failmalloc: ${request_failmalloc}
|
||||
])
|
||||
|
||||
3
contrib/.gitignore
vendored
3
contrib/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
nghttpx-init
|
||||
nghttpx.service
|
||||
nghttpx-upstart.conf
|
||||
@@ -1,44 +0,0 @@
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
configfiles = nghttpx-init nghttpx.service nghttpx-upstart.conf
|
||||
|
||||
EXTRA_DIST = $(configfiles:%=%.in) nghttpx-logrotate
|
||||
|
||||
edit = sed -e 's|@bindir[@]|$(bindir)|g'
|
||||
|
||||
nghttpx-init: %: $(srcdir)/%.in
|
||||
rm -f $@ $@.tmp
|
||||
$(edit) $< > $@.tmp
|
||||
chmod +x $@.tmp
|
||||
mv $@.tmp $@
|
||||
|
||||
nghttpx.service nghttpx-upstart.conf: %: $(srcdir)/%.in
|
||||
$(edit) $< > $@
|
||||
|
||||
$(configfiles): Makefile
|
||||
|
||||
all-local: $(configfiles)
|
||||
|
||||
clean-local:
|
||||
-rm -f nghttpx-init.tmp $(configfiles)
|
||||
@@ -1,173 +0,0 @@
|
||||
#! /bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: nghttpx
|
||||
# Required-Start: $remote_fs $syslog
|
||||
# Required-Stop: $remote_fs $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: nghttpx initscript
|
||||
# Description: nghttpx initscript
|
||||
### END INIT INFO
|
||||
|
||||
# Author: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
|
||||
#
|
||||
# Do NOT "set -e"
|
||||
|
||||
# PATH should only include /usr/* if it runs after the mountnfs.sh script
|
||||
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
|
||||
DESC="HTTP/2 reverse proxy"
|
||||
NAME=nghttpx
|
||||
# Depending on the configuration, binary may be located under @sbindir@
|
||||
DAEMON=@bindir@/$NAME
|
||||
PIDFILE=/var/run/$NAME.pid
|
||||
DAEMON_ARGS="--conf /etc/nghttpx/nghttpx.conf --pid-file=$PIDFILE"
|
||||
SCRIPTNAME=/etc/init.d/$NAME
|
||||
|
||||
# Exit if the package is not installed
|
||||
[ -x "$DAEMON" ] || exit 0
|
||||
|
||||
# Read configuration variable file if it is present
|
||||
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
|
||||
|
||||
# Load the VERBOSE setting and other rcS variables
|
||||
. /lib/init/vars.sh
|
||||
|
||||
# Define LSB log_* functions.
|
||||
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
|
||||
# and status_of_proc is working.
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
#
|
||||
# Function that starts the daemon/service
|
||||
#
|
||||
do_start()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been started
|
||||
# 1 if daemon was already running
|
||||
# 2 if daemon could not be started
|
||||
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|
||||
|| return 1
|
||||
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
|
||||
$DAEMON_ARGS \
|
||||
|| return 2
|
||||
# Add code here, if necessary, that waits for the process to be ready
|
||||
# to handle requests from services started subsequently which depend
|
||||
# on this one. As a last resort, sleep for some time.
|
||||
}
|
||||
|
||||
#
|
||||
# Function that stops the daemon/service
|
||||
#
|
||||
do_stop()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been stopped
|
||||
# 1 if daemon was already stopped
|
||||
# 2 if daemon could not be stopped
|
||||
# other if a failure occurred
|
||||
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE
|
||||
RETVAL="$?"
|
||||
[ "$RETVAL" = 2 ] && return 2
|
||||
|
||||
# Wait for children to finish too if this is a daemon that forks
|
||||
# and if the daemon is only ever run from this initscript.
|
||||
# If the above conditions are not satisfied then add some other code
|
||||
# that waits for the process to drop all resources that could be
|
||||
# needed by services started subsequently. A last resort is to
|
||||
# sleep for some time.
|
||||
#start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
|
||||
#[ "$?" = 2 ] && return 2
|
||||
# Many daemons don't delete their pidfiles when they exit.
|
||||
rm -f $PIDFILE
|
||||
return "$RETVAL"
|
||||
}
|
||||
|
||||
#
|
||||
# Function that sends a SIGHUP to the daemon/service
|
||||
#
|
||||
do_reload() {
|
||||
#
|
||||
# If the daemon can reload its configuration without
|
||||
# restarting (for example, when it is sent a SIGHUP),
|
||||
# then implement that here.
|
||||
#
|
||||
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
|
||||
return 0
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
|
||||
do_start
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
stop)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
status)
|
||||
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
||||
;;
|
||||
upgrade)
|
||||
log_daemon_msg "Upgrade $DESC" "$NAME"
|
||||
pid=`pidofproc -p $PIDFILE $NAME`
|
||||
case "$?" in
|
||||
0) echo "Sending USR2 signal to $pid"
|
||||
kill -USR2 $pid
|
||||
echo "Waiting for new binary..."
|
||||
sleep 5
|
||||
echo "Sending QUIT signal to $pid"
|
||||
kill -QUIT $pid
|
||||
log_end_msg 0
|
||||
;;
|
||||
*) echo "pidofproc() failed"
|
||||
log_end_msg 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
#reload|force-reload)
|
||||
#
|
||||
# If do_reload() is not implemented then leave this commented out
|
||||
# and leave 'force-reload' as an alias for 'restart'.
|
||||
#
|
||||
#log_daemon_msg "Reloading $DESC" "$NAME"
|
||||
#do_reload
|
||||
#log_end_msg $?
|
||||
#;;
|
||||
restart|force-reload)
|
||||
#
|
||||
# If the "reload" option is implemented then remove the
|
||||
# 'force-reload' alias
|
||||
#
|
||||
log_daemon_msg "Restarting $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1)
|
||||
do_start
|
||||
case "$?" in
|
||||
0) log_end_msg 0 ;;
|
||||
1) log_end_msg 1 ;; # Old process is still running
|
||||
*) log_end_msg 1 ;; # Failed to start
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
# Failed to stop
|
||||
log_end_msg 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload|upgrade}" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
|
||||
:
|
||||
@@ -1,11 +0,0 @@
|
||||
/var/log/nghttpx/*.log {
|
||||
weekly
|
||||
rotate 52
|
||||
missingok
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
postrotate
|
||||
killall -USR1 nghttpx 2> /dev/null || true
|
||||
endscript
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
# vim: ft=upstart:
|
||||
|
||||
description "HTTP/2 reverse proxy"
|
||||
|
||||
start on runlevel [2]
|
||||
stop on runlevel [016]
|
||||
|
||||
exec @bindir@/nghttpx
|
||||
@@ -1,10 +0,0 @@
|
||||
[Unit]
|
||||
Description=HTTP/2 experimental proxy
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=@bindir@/nghttpx --errorlog-syslog
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
23
doc/.gitignore
vendored
23
doc/.gitignore
vendored
@@ -1,24 +1,3 @@
|
||||
# generated files
|
||||
apiref.rst
|
||||
asio_http2.h.rst
|
||||
asio_http2_client.h.rst
|
||||
asio_http2_server.h.rst
|
||||
building-android-binary.rst
|
||||
conf.py
|
||||
contribute.rst
|
||||
enums.rst
|
||||
h2load-howto.rst
|
||||
index.rst
|
||||
libnghttp2_asio.rst
|
||||
macros.rst
|
||||
manual/
|
||||
nghttp2.h.rst
|
||||
nghttp2_*.rst
|
||||
nghttp2ver.h.rst
|
||||
nghttpx-howto.rst
|
||||
package_README.rst
|
||||
python-apiref.rst
|
||||
tutorial-client.rst
|
||||
tutorial-hpack.rst
|
||||
tutorial-server.rst
|
||||
types.rst
|
||||
manual
|
||||
|
||||
180
doc/Makefile.am
180
doc/Makefile.am
@@ -1,4 +1,4 @@
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -21,150 +21,33 @@
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1
|
||||
|
||||
APIDOCS= \
|
||||
apiref.rst \
|
||||
macros.rst \
|
||||
enums.rst \
|
||||
types.rst \
|
||||
nghttp2_check_header_name.rst \
|
||||
nghttp2_check_header_value.rst \
|
||||
nghttp2_hd_deflate_bound.rst \
|
||||
nghttp2_hd_deflate_change_table_size.rst \
|
||||
nghttp2_hd_deflate_del.rst \
|
||||
nghttp2_hd_deflate_hd.rst \
|
||||
nghttp2_hd_deflate_new.rst \
|
||||
nghttp2_hd_deflate_new2.rst \
|
||||
nghttp2_hd_inflate_change_table_size.rst \
|
||||
nghttp2_hd_inflate_del.rst \
|
||||
nghttp2_hd_inflate_end_headers.rst \
|
||||
nghttp2_hd_inflate_hd.rst \
|
||||
nghttp2_hd_inflate_new.rst \
|
||||
nghttp2_hd_inflate_new2.rst \
|
||||
nghttp2_is_fatal.rst \
|
||||
nghttp2_nv_compare_name.rst \
|
||||
nghttp2_option_del.rst \
|
||||
nghttp2_option_new.rst \
|
||||
nghttp2_option_set_no_auto_window_update.rst \
|
||||
nghttp2_option_set_no_http_messaging.rst \
|
||||
nghttp2_option_set_peer_max_concurrent_streams.rst \
|
||||
nghttp2_option_set_recv_client_preface.rst \
|
||||
nghttp2_pack_settings_payload.rst \
|
||||
nghttp2_priority_spec_check_default.rst \
|
||||
nghttp2_priority_spec_default_init.rst \
|
||||
nghttp2_priority_spec_init.rst \
|
||||
nghttp2_select_next_protocol.rst \
|
||||
nghttp2_session_callbacks_del.rst \
|
||||
nghttp2_session_callbacks_new.rst \
|
||||
nghttp2_session_callbacks_set_before_frame_send_callback.rst \
|
||||
nghttp2_session_callbacks_set_data_source_read_length_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_begin_frame_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_frame_not_send_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_header_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback.rst \
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback.rst \
|
||||
nghttp2_session_callbacks_set_recv_callback.rst \
|
||||
nghttp2_session_callbacks_set_select_padding_callback.rst \
|
||||
nghttp2_session_callbacks_set_send_callback.rst \
|
||||
nghttp2_session_callbacks_set_send_data_callback.rst \
|
||||
nghttp2_session_client_new.rst \
|
||||
nghttp2_session_client_new2.rst \
|
||||
nghttp2_session_client_new3.rst \
|
||||
nghttp2_session_consume.rst \
|
||||
nghttp2_session_consume_connection.rst \
|
||||
nghttp2_session_consume_stream.rst \
|
||||
nghttp2_session_del.rst \
|
||||
nghttp2_session_get_effective_local_window_size.rst \
|
||||
nghttp2_session_get_effective_recv_data_length.rst \
|
||||
nghttp2_session_get_last_proc_stream_id.rst \
|
||||
nghttp2_session_get_next_stream_id.rst \
|
||||
nghttp2_session_get_outbound_queue_size.rst \
|
||||
nghttp2_session_get_remote_settings.rst \
|
||||
nghttp2_session_get_remote_window_size.rst \
|
||||
nghttp2_session_get_stream_effective_local_window_size.rst \
|
||||
nghttp2_session_get_stream_effective_recv_data_length.rst \
|
||||
nghttp2_session_get_stream_local_close.rst \
|
||||
nghttp2_session_get_stream_remote_close.rst \
|
||||
nghttp2_session_get_stream_remote_window_size.rst \
|
||||
nghttp2_session_get_stream_user_data.rst \
|
||||
nghttp2_session_mem_recv.rst \
|
||||
nghttp2_session_mem_send.rst \
|
||||
nghttp2_session_recv.rst \
|
||||
nghttp2_session_resume_data.rst \
|
||||
nghttp2_session_send.rst \
|
||||
nghttp2_session_server_new.rst \
|
||||
nghttp2_session_server_new2.rst \
|
||||
nghttp2_session_server_new3.rst \
|
||||
nghttp2_session_set_next_stream_id.rst \
|
||||
nghttp2_session_set_stream_user_data.rst \
|
||||
nghttp2_session_terminate_session.rst \
|
||||
nghttp2_session_terminate_session2.rst \
|
||||
nghttp2_session_upgrade.rst \
|
||||
nghttp2_session_want_read.rst \
|
||||
nghttp2_session_want_write.rst \
|
||||
nghttp2_strerror.rst \
|
||||
nghttp2_submit_altsvc.rst \
|
||||
nghttp2_submit_data.rst \
|
||||
nghttp2_submit_goaway.rst \
|
||||
nghttp2_submit_headers.rst \
|
||||
nghttp2_submit_ping.rst \
|
||||
nghttp2_submit_priority.rst \
|
||||
nghttp2_submit_push_promise.rst \
|
||||
nghttp2_submit_request.rst \
|
||||
nghttp2_submit_response.rst \
|
||||
nghttp2_submit_rst_stream.rst \
|
||||
nghttp2_submit_settings.rst \
|
||||
nghttp2_submit_shutdown_notice.rst \
|
||||
nghttp2_submit_trailer.rst \
|
||||
nghttp2_submit_window_update.rst \
|
||||
nghttp2_version.rst
|
||||
man_MANS = nghttp.1 nghttpd.1 nghttpx.1
|
||||
|
||||
EXTRA_DIST = \
|
||||
mkapiref.py \
|
||||
README.rst \
|
||||
programmers-guide.rst \
|
||||
$(APIDOCS) \
|
||||
nghttp.1.rst \
|
||||
nghttpd.1.rst \
|
||||
nghttpx.1.rst \
|
||||
h2load.1.rst \
|
||||
apiref-header.rst \
|
||||
sources/index.rst \
|
||||
sources/tutorial-client.rst \
|
||||
sources/tutorial-server.rst \
|
||||
sources/tutorial-hpack.rst \
|
||||
sources/nghttpx-howto.rst \
|
||||
sources/h2load-howto.rst \
|
||||
sources/libnghttp2_asio.rst \
|
||||
sources/python-apiref.rst \
|
||||
sources/building-android-binary.rst \
|
||||
sources/contribute.rst \
|
||||
_themes/sphinx_rtd_theme/__init__.py \
|
||||
_themes/sphinx_rtd_theme/breadcrumbs.html \
|
||||
_themes/sphinx_rtd_theme/footer.html \
|
||||
_themes/sphinx_rtd_theme/layout.html \
|
||||
_themes/sphinx_rtd_theme/layout_old.html \
|
||||
_themes/sphinx_rtd_theme/search.html \
|
||||
_themes/sphinx_rtd_theme/searchbox.html \
|
||||
_themes/sphinx_rtd_theme/static/css/badge_only.css \
|
||||
_themes/sphinx_rtd_theme/static/css/theme.css \
|
||||
_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \
|
||||
_themes/sphinx_rtd_theme/static/js/theme.js \
|
||||
_themes/sphinx_rtd_theme/theme.conf \
|
||||
_themes/sphinx_rtd_theme/layout_old.html \
|
||||
_themes/sphinx_rtd_theme/__init__.py \
|
||||
_themes/sphinx_rtd_theme/layout.html \
|
||||
_themes/sphinx_rtd_theme/search.html \
|
||||
_themes/sphinx_rtd_theme/breadcrumbs.html \
|
||||
_themes/sphinx_rtd_theme/versions.html \
|
||||
$(man_MANS) \
|
||||
bash_completion/nghttp \
|
||||
bash_completion/nghttpd \
|
||||
bash_completion/nghttpx \
|
||||
bash_completion/h2load
|
||||
_themes/sphinx_rtd_theme/searchbox.html \
|
||||
_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \
|
||||
_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \
|
||||
_themes/sphinx_rtd_theme/static/js/theme.js \
|
||||
_themes/sphinx_rtd_theme/static/css/theme.css \
|
||||
_themes/sphinx_rtd_theme/static/css/badge_only.css \
|
||||
$(man_MANS)
|
||||
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
@@ -201,33 +84,16 @@ help:
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
apiref.rst macros.rst enums.rst types.rst: \
|
||||
$(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
|
||||
apiref.rst: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
|
||||
$(top_builddir)/lib/includes/nghttp2/nghttp2.h
|
||||
$(PYTHON) $(top_srcdir)/doc/mkapiref.py \
|
||||
$@ macros.rst enums.rst types.rst . $^
|
||||
|
||||
# Inspired by
|
||||
# http://www.gnu.org/savannah-checkouts/gnu/automake/manual/html_node/Multiple-Outputs.html
|
||||
apidoc.stamp: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
|
||||
$(top_builddir)/lib/includes/nghttp2/nghttp2.h
|
||||
@rm -f apidoc.tmp
|
||||
@touch apidoc.tmp
|
||||
$(PYTHON) $(top_srcdir)/doc/mkapiref.py \
|
||||
$@ macros.rst enums.rst types.rst . $^
|
||||
@mv -f apidoc.tmp $@
|
||||
$(APIDOC): apidoc.stamp
|
||||
## Recover from the removal of $@
|
||||
@if test -f $@; then :; else \
|
||||
rm -f apidoc.stamp; \
|
||||
$(MAKE) $(AM_MAKEFLAGS) apidoc.stamp; \
|
||||
fi
|
||||
--header $(top_srcdir)/doc/apiref-header.rst $^ > $@
|
||||
|
||||
clean-local:
|
||||
-rm $(APIDOCS)
|
||||
-rm apiref.rst
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html-local: apiref.rst
|
||||
html: apiref.rst
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
@@ -299,7 +165,7 @@ text:
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man: apiref.rst
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
2
doc/_themes/sphinx_rtd_theme/__init__.py
vendored
2
doc/_themes/sphinx_rtd_theme/__init__.py
vendored
@@ -5,7 +5,7 @@ From https://github.com/ryan-roemer/sphinx-bootstrap-theme.
|
||||
"""
|
||||
import os
|
||||
|
||||
VERSION = (0, 1, 8)
|
||||
VERSION = (0, 1, 5)
|
||||
|
||||
__version__ = ".".join(str(v) for v in VERSION)
|
||||
__version_full__ = __version__
|
||||
|
||||
@@ -6,17 +6,13 @@
|
||||
{% endfor %}
|
||||
<li>{{ title }}</li>
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
{% if pagename != "search" %}
|
||||
{% if display_github %}
|
||||
<a href="https://{{ github_host|default("github.com") }}/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-github"> Edit on GitHub</a>
|
||||
<a href="https://github.com/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}.rst" class="fa fa-github"> Edit on GitHub</a>
|
||||
{% elif display_bitbucket %}
|
||||
<a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}" class="fa fa-bitbucket"> Edit on Bitbucket</a>
|
||||
{% elif show_source and source_url_prefix %}
|
||||
<a href="{{ source_url_prefix }}{{ pagename }}{{ source_suffix }}">View page source</a>
|
||||
<a href="https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}.rst" class="fa fa-bitbucket"> Edit on Bitbucket</a>
|
||||
{% elif show_source and has_source and sourcename %}
|
||||
<a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
<hr/>
|
||||
|
||||
10
doc/_themes/sphinx_rtd_theme/footer.html
vendored
10
doc/_themes/sphinx_rtd_theme/footer.html
vendored
@@ -2,10 +2,10 @@
|
||||
{% if next or prev %}
|
||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||
{% if next %}
|
||||
<a href="{{ next.link|e }}" class="btn btn-neutral float-right" title="{{ next.title|striptags|e }}" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
|
||||
<a href="{{ next.link|e }}" class="btn btn-neutral float-right" title="{{ next.title|striptags|e }}"/>Next <span class="fa fa-arrow-circle-right"></span></a>
|
||||
{% endif %}
|
||||
{% if prev %}
|
||||
<a href="{{ prev.link|e }}" class="btn btn-neutral" title="{{ prev.title|striptags|e }}" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||||
<a href="{{ prev.link|e }}" class="btn btn-neutral" title="{{ prev.title|striptags|e }}"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -28,9 +28,5 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{%- if show_sphinx %}
|
||||
{% trans %}Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>{% endtrans %}.
|
||||
{%- endif %}
|
||||
|
||||
{% trans %}<a href="https://github.com/snide/sphinx_rtd_theme">Sphinx theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>{% endtrans %}
|
||||
</footer>
|
||||
|
||||
|
||||
94
doc/_themes/sphinx_rtd_theme/layout.html
vendored
94
doc/_themes/sphinx_rtd_theme/layout.html
vendored
@@ -12,7 +12,6 @@
|
||||
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
{{ metatags }}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
{% block htmltitle %}
|
||||
<title>{{ title|striptags|e }}{{ titlesuffix }}</title>
|
||||
@@ -24,28 +23,49 @@
|
||||
{% endif %}
|
||||
|
||||
{# CSS #}
|
||||
<link href='https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>
|
||||
|
||||
{# OPENSEARCH #}
|
||||
{# JS #}
|
||||
{% if not embedded %}
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT:'{{ url_root }}',
|
||||
VERSION:'{{ release|e }}',
|
||||
COLLAPSE_INDEX:false,
|
||||
FILE_SUFFIX:'{{ '' if no_search_suffix else file_suffix }}',
|
||||
HAS_SOURCE: {{ has_source|lower }}
|
||||
};
|
||||
</script>
|
||||
{%- for scriptfile in script_files %}
|
||||
<script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
|
||||
{%- endfor %}
|
||||
|
||||
{% if use_opensearch %}
|
||||
<link rel="search" type="application/opensearchdescription+xml" title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}" href="{{ pathto('_static/opensearch.xml', 1) }}"/>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{# RTD hosts this file, so just load on non RTD builds #}
|
||||
{# RTD hosts these file themselves, so just load on non RTD builds #}
|
||||
{% if not READTHEDOCS %}
|
||||
<link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
|
||||
<script type="text/javascript" src="{{ pathto('_static/js/theme.js', 1) }}"></script>
|
||||
{% endif %}
|
||||
|
||||
{# STICKY NAVIGATION #}
|
||||
{% if theme_sticky_navigation %}
|
||||
<script type="text/javascript">
|
||||
jQuery(function () {
|
||||
SphinxRtdTheme.StickyNav.enable();
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
{% for cssfile in css_files %}
|
||||
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
|
||||
{% endfor %}
|
||||
|
||||
{% for cssfile in extra_css_files %}
|
||||
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
|
||||
{% endfor %}
|
||||
|
||||
{%- block linktags %}
|
||||
{%- if hasdoc('about') %}
|
||||
<link rel="author" title="{{ _('About these documents') }}"
|
||||
@@ -74,8 +94,7 @@
|
||||
{%- endblock %}
|
||||
{%- block extrahead %} {% endblock %}
|
||||
|
||||
{# Keep modernizr in head - http://modernizr.com/docs/#installing #}
|
||||
<script src="_static/js/modernizr.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
@@ -86,35 +105,18 @@
|
||||
{# SIDE NAV, TOGGLES ON MOBILE #}
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-nav-search">
|
||||
{% block sidebartitle %}
|
||||
|
||||
{% if logo and theme_logo_only %}
|
||||
<a href="{{ pathto(master_doc) }}">
|
||||
{% else %}
|
||||
<a href="{{ pathto(master_doc) }}" class="icon icon-home"> {{ project }}
|
||||
{% endif %}
|
||||
|
||||
{% if logo %}
|
||||
{# Not strictly valid HTML, but it's the only way to display/scale it properly, without weird scripting or heaps of work #}
|
||||
<img src="{{ pathto('_static/' + logo, 1) }}" class="logo" />
|
||||
{% endif %}
|
||||
</a>
|
||||
|
||||
<a href="{{ pathto(master_doc) }}" class="fa fa-home"> {{ project }}</a>
|
||||
{% include "searchbox.html" %}
|
||||
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||
{% block menu %}
|
||||
{% set toctree = toctree(maxdepth=4, collapse=False, includehidden=True) %}
|
||||
{% set toctree = toctree(maxdepth=2, collapse=False, includehidden=True) %}
|
||||
{% if toctree %}
|
||||
{{ toctree }}
|
||||
{% else %}
|
||||
<!-- Local TOC -->
|
||||
<div class="local-toc">{{ toc }}</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
@@ -132,7 +134,7 @@
|
||||
<div class="wy-nav-content">
|
||||
<div class="rst-content">
|
||||
{% include "breadcrumbs.html" %}
|
||||
<div role="main" class="document">
|
||||
<div role="main">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
{% include "footer.html" %}
|
||||
@@ -143,39 +145,5 @@
|
||||
|
||||
</div>
|
||||
{% include "versions.html" %}
|
||||
|
||||
{% if not embedded %}
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT:'{{ url_root }}',
|
||||
VERSION:'{{ release|e }}',
|
||||
COLLAPSE_INDEX:false,
|
||||
FILE_SUFFIX:'{{ '' if no_search_suffix else file_suffix }}',
|
||||
HAS_SOURCE: {{ has_source|lower }}
|
||||
};
|
||||
</script>
|
||||
{%- for scriptfile in script_files %}
|
||||
<script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
|
||||
{%- endfor %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{# RTD hosts this file, so just load on non RTD builds #}
|
||||
{% if not READTHEDOCS %}
|
||||
<script type="text/javascript" src="{{ pathto('_static/js/theme.js', 1) }}"></script>
|
||||
{% endif %}
|
||||
|
||||
{# STICKY NAVIGATION #}
|
||||
{% if theme_sticky_navigation %}
|
||||
<script type="text/javascript">
|
||||
jQuery(function () {
|
||||
SphinxRtdTheme.StickyNav.enable();
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
{%- block footer %} {% endblock %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
2
doc/_themes/sphinx_rtd_theme/search.html
vendored
2
doc/_themes/sphinx_rtd_theme/search.html
vendored
@@ -10,7 +10,7 @@
|
||||
{%- extends "layout.html" %}
|
||||
{% set title = _('Search') %}
|
||||
{% set script_files = script_files + ['_static/searchtools.js'] %}
|
||||
{% block footer %}
|
||||
{% block extrahead %}
|
||||
<script type="text/javascript">
|
||||
jQuery(function() { Search.loadIndex("{{ pathto('searchindex.js', 1) }}"); });
|
||||
</script>
|
||||
|
||||
2
doc/_themes/sphinx_rtd_theme/searchbox.html
vendored
2
doc/_themes/sphinx_rtd_theme/searchbox.html
vendored
@@ -1,4 +1,3 @@
|
||||
{%- if builder != 'singlehtml' %}
|
||||
<div role="search">
|
||||
<form id ="rtd-search-form" class="wy-form" action="{{ pathto('search') }}" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" />
|
||||
@@ -6,4 +5,3 @@
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
{%- endif %}
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}
|
||||
/*# sourceMappingURL=badge_only.css.map */
|
||||
.font-smooth,.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:"\f02d"}.icon-book:before{content:"\f02d"}.fa-caret-down:before{content:"\f0d7"}.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"version": 3,
|
||||
"mappings": "CAyDA,SAAY,EACV,qBAAsB,EAAE,UAAW,EAqDrC,QAAS,EARP,IAAK,EAAE,AAAC,EACR,+BAAS,EAEP,MAAO,EAAE,IAAK,EACd,MAAO,EAAE,CAAE,EACb,cAAO,EACL,IAAK,EAAE,GAAI,EC1Gb,SAkBC,EAjBC,UAAW,ECFJ,UAAW,EDGlB,UAAW,EAHqC,KAAM,EAItD,SAAU,EAJsD,KAAM,EAapE,EAAG,EAAE,qCAAwB,EAC7B,EAAG,EAAE,0PAAyE,ECZpF,SAAU,EACR,MAAO,EAAE,WAAY,EACrB,UAAW,EAAE,UAAW,EACxB,SAAU,EAAE,KAAM,EAClB,UAAW,EAAE,KAAM,EACnB,UAAW,EAAE,AAAC,EACd,cAAe,EAAE,MAAO,EAG1B,IAAK,EACH,MAAO,EAAE,WAAY,EACrB,cAAe,EAAE,MAAO,EAIxB,KAAG,EACD,MAAO,EAAE,WAAY,EACvB,sCAAiB,EAGf,IAAK,EAAE,MAAY,EAEvB,KAAM,EACJ,cAAe,EAAE,GAAI,EACrB,UAAW,EAAE,EAAG,EAChB,UAAW,EAAE,KAAM,EAEjB,YAAG,EACD,IAAK,EAAE,IAAI,EACb,oDAAiB,EAGf,aAAc,EAAE,OAAQ,EAG9B,cAAe,EACb,MAAO,EAAE,EAAO,EAElB,gBAAiB,EACf,MAAO,EAAE,EAAO,EAElB,oBAAqB,EACnB,MAAO,EAAE,EAAO,EAElB,sBAAuB,EACrB,MAAO,EAAE,EAAO,EAElB,kBAAmB,EACjB,MAAO,EAAE,EAAO,EAElB,oBAAqB,EACnB,MAAO,EAAE,EAAO,EAElB,oBAAqB,EACnB,MAAO,EAAE,EAAO,EAElB,sBAAuB,EACrB,MAAO,EAAE,EAAO,EAElB,qBAAsB,EACpB,MAAO,EAAE,EAAO,EAElB,uBAAwB,EACtB,MAAO,EAAE,EAAO,ECnElB,YAAa,EACX,OAAQ,EAAE,IAAK,EACf,KAAM,EAAE,AAAC,EACT,GAAI,EAAE,AAAC,EACP,IAAK,EC6E+B,IAAK,ED5EzC,IAAK,ECE+B,MAAyB,EDD7D,SAAU,EAAE,MAAkC,EAC9C,SAAU,EAAE,iBAAiC,EAC7C,UAAW,EEAyB,sDAAM,EFC1C,MAAO,EC+E6B,EAAG,ED9EvC,cAAC,EACC,IAAK,ECqE6B,MAAW,EDpE7C,cAAe,EAAE,GAAI,EACvB,6BAAgB,EACd,MAAO,EAAE,GAAI,EACf,iCAAoB,EAClB,MAAO,EAAE,GAAqB,EAC9B,eAAgB,EAAE,MAAkC,EACpD,MAAO,EAAE,IAAK,EACd,SAAU,EAAE,IAAK,EACjB,QAAS,EAAE,EAAG,EACd,KAAM,EAAE,MAAO,EACf,IAAK,ECiD6B,MAAM,EJgC1C,IAAK,EAAE,AAAC,EACR,iFAAS,EAEP,MAAO,EAAE,IAAK,EACd,MAAO,EAAE,CAAE,EACb,uCAAO,EACL,IAAK,EAAE,GAAI,EGrFX,qCAAG,EACD,IAAK,EClB2B,MAAyB,EDmB3D,0CAAQ,EACN,IAAK,EAAE,GAAI,EACb,4CAAU,EACR,IAAK,EAAE,GAAI,EACb,iDAAiB,EACf,eAAgB,ECQgB,MAAI,EDPpC,IAAK,EC0B2B,GAAM,EDzBxC,wDAAwB,EACtB,eAAgB,ECXgB,MAAO,EDYvC,IAAK,ECzB2B,GAAI,ED0BxC,yCAA8B,EAC5B,MAAO,EAAE,IAAK,EAChB,gCAAmB,EACjB,QAAS,EAAE,EAAG,EACd,MAAO,EAAE,GAAqB,EAC9B,IAAK,ECE6B,GAAwB,EDD1D,MAAO,EAAE,GAAI,EACb,mCAAE,EACA,MAAO,EAAE,IAAK,EACd,KAAM,EAAE,EAAG,EACX,KAAM,EAAE,AAAC,EACT,KAAM,EAAE,KAAM,EACd,MAAO,EAAE,AAAC,EACV,SAAU,EAAE,gBAA6C,EAC3D,mCAAE,EACA,MAAO,EAAE,WAAY,EACrB,KAAM,EAAE,AAAC,EACT,qCAAC,EACC,MAAO,EAAE,WAAY,EACrB,MAAO,EAAE,EAAqB,EAC9B,IAAK,ECjDyB,MAAyB,EDkD7D,sBAAW,EACT,IAAK,EAAE,GAAI,EACX,KAAM,EAAE,GAAI,EACZ,IAAK,EAAE,GAAI,EACX,GAAI,EAAE,GAAI,EACV,KAAM,EAAE,GAAI,EACZ,QAAS,ECkByB,IAAK,EDjBvC,iCAAU,EACR,IAAK,EAAE,GAAI,EACb,+BAAQ,EACN,IAAK,EAAE,GAAI,EACb,oDAA+B,EAC7B,SAAU,EAAE,IAAK,EACjB,6DAAQ,EACN,IAAK,EAAE,GAAI,EACb,+DAAU,EACR,IAAK,EAAE,GAAI,EACf,2CAAoB,EAClB,IAAK,EAAE,GAAI,EACX,KAAM,EAAE,GAAI,EACZ,UAAW,EAAE,GAAI,EACjB,MAAO,EAAE,IAAuB,EAChC,MAAO,EAAE,IAAK,EACd,SAAU,EAAE,KAAM,EGhDpB,mCAAsB,EHmDxB,YAAa,EACX,IAAK,EAAE,EAAG,EACV,MAAO,EAAE,GAAI,EACb,kBAAO,EACL,MAAO,EAAE,IAAK,EAClB,EAAG,EACD,IAAK,EAAE,GAAI,EACX,KAAM,EAAE,GAAI",
|
||||
"sources": ["../../../bower_components/wyrm/sass/wyrm_core/_mixin.sass","../../../bower_components/bourbon/dist/css3/_font-face.scss","../../../sass/_theme_badge_fa.sass","../../../sass/_theme_badge.sass","../../../bower_components/wyrm/sass/wyrm_core/_wy_variables.sass","../../../sass/_theme_variables.sass","../../../bower_components/neat/app/assets/stylesheets/grid/_media.scss"],
|
||||
"names": [],
|
||||
"file": "badge_only.css"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
90
doc/_themes/sphinx_rtd_theme/static/js/theme.js
vendored
90
doc/_themes/sphinx_rtd_theme/static/js/theme.js
vendored
@@ -1,110 +1,44 @@
|
||||
function toggleCurrent (elem) {
|
||||
var parent_li = elem.closest('li');
|
||||
parent_li.siblings('li.current').removeClass('current');
|
||||
parent_li.siblings().find('li.current').removeClass('current');
|
||||
parent_li.find('> ul li.current').removeClass('current');
|
||||
parent_li.toggleClass('current');
|
||||
}
|
||||
|
||||
$( document ).ready(function() {
|
||||
// Shift nav in mobile when clicking the menu.
|
||||
$(document).on('click', "[data-toggle='wy-nav-top']", function() {
|
||||
$("[data-toggle='wy-nav-shift']").toggleClass("shift");
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||
});
|
||||
// Nav menu link click operations
|
||||
$(document).on('click', ".wy-menu-vertical .current ul li a", function() {
|
||||
var target = $(this);
|
||||
// Close menu when you click a link.
|
||||
$(document).on('click', ".wy-menu-vertical .current ul li a", function() {
|
||||
$("[data-toggle='wy-nav-shift']").removeClass("shift");
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||
// Handle dynamic display of l3 and l4 nav lists
|
||||
toggleCurrent(target);
|
||||
if (typeof(window.SphinxRtdTheme) != 'undefined') {
|
||||
window.SphinxRtdTheme.StickyNav.hashChange();
|
||||
}
|
||||
});
|
||||
$(document).on('click', "[data-toggle='rst-current-version']", function() {
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift-up");
|
||||
});
|
||||
// Make tables responsive
|
||||
$("table.docutils:not(.field-list)").wrap("<div class='wy-table-responsive'></div>");
|
||||
|
||||
// Add expand links to all parents of nested ul
|
||||
$('.wy-menu-vertical ul').siblings('a').each(function () {
|
||||
var link = $(this);
|
||||
expand = $('<span class="toctree-expand"></span>');
|
||||
expand.on('click', function (ev) {
|
||||
toggleCurrent(link);
|
||||
ev.stopPropagation();
|
||||
return false;
|
||||
});
|
||||
link.prepend(expand);
|
||||
});
|
||||
});
|
||||
|
||||
// Sphinx theme state
|
||||
window.SphinxRtdTheme = (function (jquery) {
|
||||
var stickyNav = (function () {
|
||||
var navBar,
|
||||
win,
|
||||
winScroll = false,
|
||||
linkScroll = false,
|
||||
winPosition = 0,
|
||||
stickyNavCssClass = 'stickynav',
|
||||
applyStickNav = function () {
|
||||
if (navBar.height() <= win.height()) {
|
||||
navBar.addClass(stickyNavCssClass);
|
||||
} else {
|
||||
navBar.removeClass(stickyNavCssClass);
|
||||
}
|
||||
},
|
||||
enable = function () {
|
||||
init();
|
||||
reset();
|
||||
win.on('hashchange', reset);
|
||||
|
||||
// Set scrolling
|
||||
win.on('scroll', function () {
|
||||
if (!linkScroll) {
|
||||
winScroll = true;
|
||||
}
|
||||
});
|
||||
setInterval(function () {
|
||||
if (winScroll) {
|
||||
winScroll = false;
|
||||
var newWinPosition = win.scrollTop(),
|
||||
navPosition = navBar.scrollTop(),
|
||||
newNavPosition = navPosition + (newWinPosition - winPosition);
|
||||
navBar.scrollTop(newNavPosition);
|
||||
winPosition = newWinPosition;
|
||||
}
|
||||
}, 25);
|
||||
applyStickNav();
|
||||
win.on('resize', applyStickNav);
|
||||
},
|
||||
init = function () {
|
||||
navBar = jquery('nav.wy-nav-side:first');
|
||||
win = jquery(window);
|
||||
},
|
||||
reset = function () {
|
||||
// Get anchor from URL and open up nested nav
|
||||
var anchor = encodeURI(window.location.hash);
|
||||
if (anchor) {
|
||||
try {
|
||||
var link = $('.wy-menu-vertical')
|
||||
.find('[href="' + anchor + '"]');
|
||||
$('.wy-menu-vertical li.toctree-l1 li.current')
|
||||
.removeClass('current');
|
||||
link.closest('li.toctree-l2').addClass('current');
|
||||
link.closest('li.toctree-l3').addClass('current');
|
||||
link.closest('li.toctree-l4').addClass('current');
|
||||
}
|
||||
catch (err) {
|
||||
console.log("Error expanding nav for anchor", err);
|
||||
}
|
||||
}
|
||||
},
|
||||
hashChange = function () {
|
||||
linkScroll = true;
|
||||
win.one('hashchange', function () {
|
||||
linkScroll = false;
|
||||
});
|
||||
};
|
||||
jquery(init);
|
||||
return {
|
||||
enable: enable,
|
||||
hashChange: hashChange
|
||||
enable : enable
|
||||
};
|
||||
}());
|
||||
return {
|
||||
|
||||
1
doc/_themes/sphinx_rtd_theme/theme.conf
vendored
1
doc/_themes/sphinx_rtd_theme/theme.conf
vendored
@@ -6,4 +6,3 @@ stylesheet = css/theme.css
|
||||
typekit_id = hiw1hhg
|
||||
analytics_id =
|
||||
sticky_navigation = False
|
||||
logo_only =
|
||||
|
||||
22
doc/apiref-header.rst
Normal file
22
doc/apiref-header.rst
Normal file
@@ -0,0 +1,22 @@
|
||||
API Reference
|
||||
=============
|
||||
|
||||
Includes
|
||||
--------
|
||||
|
||||
To use the public APIs, include ``nghttp2/nghttp2.h``::
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
The header files are also available online: :doc:`nghttp2.h` and
|
||||
:doc:`nghttp2ver.h`.
|
||||
|
||||
Remarks
|
||||
-------
|
||||
|
||||
Do not call `nghttp2_session_send`, `nghttp2_session_recv` or
|
||||
`nghttp2_session_mem_recv` from the nghttp2 callback functions
|
||||
directly or indirectly. It will lead to the crash. You can submit
|
||||
requests or frames in the callbacks then call `nghttp2_session_send`,
|
||||
`nghttp2_session_recv` or `nghttp2_session_mem_recv` outside of the
|
||||
callbacks.
|
||||
@@ -1,5 +0,0 @@
|
||||
asio_http2.h
|
||||
============
|
||||
|
||||
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2.h
|
||||
:language: cpp
|
||||
@@ -1,5 +0,0 @@
|
||||
asio_http2_client.h
|
||||
===================
|
||||
|
||||
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_client.h
|
||||
:language: cpp
|
||||
@@ -1,5 +0,0 @@
|
||||
asio_http2_server.h
|
||||
===================
|
||||
|
||||
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_server.h
|
||||
:language: cpp
|
||||
@@ -1,19 +0,0 @@
|
||||
_h2load()
|
||||
{
|
||||
local cur prev split=false
|
||||
COMPREPLY=()
|
||||
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
|
||||
|
||||
cmd=${COMP_WORDS[0]}
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--threads --connection-window-bits --input-file --help --requests --data --verbose --version --window-bits --clients --no-tls-proto --header --max-concurrent-streams ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
return 0
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
complete -F _h2load h2load
|
||||
@@ -1,74 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import subprocess
|
||||
from StringIO import StringIO
|
||||
import re
|
||||
import sys
|
||||
import os.path
|
||||
|
||||
class Option:
|
||||
def __init__(self, long_opt, short_opt):
|
||||
self.long_opt = long_opt
|
||||
self.short_opt = short_opt
|
||||
|
||||
def get_all_options(cmd):
|
||||
opt_pattern = re.compile(r' (?:(-.), )?(--[^\s\[=]+)(\[)?')
|
||||
proc = subprocess.Popen([cmd, "--help"], stdout=subprocess.PIPE)
|
||||
stdoutdata, stderrdata = proc.communicate()
|
||||
cur_option = None
|
||||
opts = {}
|
||||
for line in StringIO(stdoutdata):
|
||||
match = opt_pattern.match(line)
|
||||
if not match:
|
||||
continue
|
||||
long_opt = match.group(2)
|
||||
short_opt = match.group(1)
|
||||
opts[long_opt] = Option(long_opt, short_opt)
|
||||
|
||||
return opts
|
||||
|
||||
def output_case(out, name, opts):
|
||||
out.write('''\
|
||||
_{name}()
|
||||
{{
|
||||
local cur prev split=false
|
||||
COMPREPLY=()
|
||||
COMP_WORDBREAKS=${{COMP_WORDBREAKS//=}}
|
||||
|
||||
cmd=${{COMP_WORDS[0]}}
|
||||
_get_comp_words_by_ref cur prev
|
||||
'''.format(name=name))
|
||||
|
||||
# Complete option name.
|
||||
out.write('''\
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '\
|
||||
''')
|
||||
for opt in opts.itervalues():
|
||||
out.write(opt.long_opt)
|
||||
out.write(' ')
|
||||
|
||||
out.write('''\
|
||||
' -- "$cur" ) )
|
||||
;;
|
||||
''')
|
||||
# If no option found for completion then complete with files.
|
||||
out.write('''\
|
||||
*)
|
||||
_filedir
|
||||
return 0
|
||||
esac
|
||||
return 0
|
||||
}}
|
||||
complete -F _{name} {name}
|
||||
'''.format(name=name))
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 2:
|
||||
print "Generates bash_completion using `/path/to/cmd --help'"
|
||||
print "Usage: make_bash_completion.py /path/to/cmd"
|
||||
exit(1)
|
||||
name = os.path.basename(sys.argv[1])
|
||||
opts = get_all_options(sys.argv[1])
|
||||
output_case(sys.stdout, name, opts)
|
||||
@@ -1,19 +0,0 @@
|
||||
_nghttp()
|
||||
{
|
||||
local cur prev split=false
|
||||
COMPREPLY=()
|
||||
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
|
||||
|
||||
cmd=${COMP_WORDS[0]}
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--no-push --verbose --no-dep --get-assets --har --header-table-size --multiply --padding --hexdump --continuation --connection-window-bits --peer-max-concurrent-streams --timeout --data --no-content-length --version --color --cert --upgrade --remote-name --trailer --weight --help --key --null-out --window-bits --stat --header ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
return 0
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
complete -F _nghttp nghttp
|
||||
@@ -1,19 +0,0 @@
|
||||
_nghttpd()
|
||||
{
|
||||
local cur prev split=false
|
||||
COMPREPLY=()
|
||||
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
|
||||
|
||||
cmd=${COMP_WORDS[0]}
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--error-gzip --push --header-table-size --trailer --htdocs --address --padding --verbose --version --help --hexdump --dh-param-file --daemon --verify-client --echo-upload --workers --no-tls --color --early-response --max-concurrent-streams ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
return 0
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
complete -F _nghttpd nghttpd
|
||||
@@ -1,19 +0,0 @@
|
||||
_nghttpx()
|
||||
{
|
||||
local cur prev split=false
|
||||
COMPREPLY=()
|
||||
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
|
||||
|
||||
cmd=${COMP_WORDS[0]}
|
||||
_get_comp_words_by_ref cur prev
|
||||
case $cur in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W '--worker-read-rate --frontend-no-tls --frontend-http2-dump-response-header --backend-http1-connections-per-frontend --tls-ticket-key-file --verify-client-cacert --backend-request-buffer --backend-http2-connection-window-bits --conf --worker-write-burst --npn-list --fetch-ocsp-response-file --stream-read-timeout --accesslog-syslog --frontend-http2-read-timeout --listener-disable-timeout --frontend-http2-connection-window-bits --ciphers --strip-incoming-x-forwarded-for --daemon --backend-keep-alive-timeout --backend-http-proxy-uri --backend-http1-connections-per-host --rlimit-nofile --no-via --ocsp-update-interval --backend-write-timeout --client --http2-no-cookie-crumbling --worker-read-burst --client-proxy --http2-bridge --accesslog-format --errorlog-syslog --errorlog-file --http2-max-concurrent-streams --frontend-write-timeout --read-burst --backend-ipv4 --backend-ipv6 --backend --insecure --log-level --tls-proto-list --backend-http2-connections-per-worker --dh-param-file --worker-frontend-connections --header-field-buffer --no-server-push --no-location-rewrite --no-ocsp --backend-response-buffer --workers --frontend-http2-window-bits --no-host-rewrite --worker-write-rate --backend-tls-sni-field --subcert --help --frontend-frame-debug --pid-file --frontend-http2-dump-request-header --private-key-passwd-file --write-rate --altsvc --user --add-x-forwarded-for --syslog-facility --frontend-read-timeout --backlog --write-burst --backend-http2-window-bits --padding --stream-write-timeout --cacert --version --verify-client --backend-read-timeout --frontend --accesslog-file --http2-proxy --max-header-fields --backend-no-tls --client-private-key-file --client-cert-file --add-response-header --read-rate ' -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
return 0
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
complete -F _nghttpx nghttpx
|
||||
@@ -1 +0,0 @@
|
||||
.. include:: @top_srcdir@/doc/sources/building-android-binary.rst
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -64,7 +64,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'nghttp2'
|
||||
copyright = u'2012, 2015, Tatsuhiro Tsujikawa'
|
||||
copyright = u'2012, 2014, Tatsuhiro Tsujikawa'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
@@ -155,7 +155,7 @@ html_theme_path = ['@top_srcdir@/doc/_themes']
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
html_use_smartypants = False
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {
|
||||
@@ -242,12 +242,6 @@ latex_documents = [
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('nghttp.1', 'nghttp', u'HTTP/2 experimental client',
|
||||
[u'Tatsuhiro Tsujikawa'], 1),
|
||||
('nghttpd.1', 'nghttpd', u'HTTP/2 experimental server',
|
||||
[u'Tatsuhiro Tsujikawa'], 1),
|
||||
('nghttpx.1', 'nghttpx', u'HTTP/2 experimental proxy',
|
||||
[u'Tatsuhiro Tsujikawa'], 1),
|
||||
('h2load.1', 'h2load', u'HTTP/2 benchmarking tool',
|
||||
('index', 'nghttp2', u'nghttp2 Documentation',
|
||||
[u'Tatsuhiro Tsujikawa'], 1)
|
||||
]
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
.. include:: @top_srcdir@/doc/sources/contribute.rst
|
||||
@@ -1 +0,0 @@
|
||||
.. include:: @top_srcdir@/doc/sources/h2load-howto.rst
|
||||
273
doc/h2load.1
273
doc/h2load.1
@@ -1,273 +0,0 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "H2LOAD" "1" "May 15, 2015" "0.7.15" "nghttp2"
|
||||
.SH NAME
|
||||
h2load \- HTTP/2 benchmarking tool
|
||||
.
|
||||
.nr rst2man-indent-level 0
|
||||
.
|
||||
.de1 rstReportMargin
|
||||
\\$1 \\n[an-margin]
|
||||
level \\n[rst2man-indent-level]
|
||||
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
-
|
||||
\\n[rst2man-indent0]
|
||||
\\n[rst2man-indent1]
|
||||
\\n[rst2man-indent2]
|
||||
..
|
||||
.de1 INDENT
|
||||
.\" .rstReportMargin pre:
|
||||
. RS \\$1
|
||||
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
|
||||
. nr rst2man-indent-level +1
|
||||
.\" .rstReportMargin post:
|
||||
..
|
||||
.de UNINDENT
|
||||
. RE
|
||||
.\" indent \\n[an-margin]
|
||||
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.nr rst2man-indent-level -1
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.SH SYNOPSIS
|
||||
.sp
|
||||
\fBh2load\fP [OPTIONS]... [URI]...
|
||||
.SH DESCRIPTION
|
||||
.sp
|
||||
benchmarking tool for HTTP/2 and SPDY server
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B <URI>
|
||||
Specify URI to access. Multiple URIs can be specified.
|
||||
URIs are used in this order for each client. All URIs
|
||||
are used, then first URI is used and then 2nd URI, and
|
||||
so on. The scheme, host and port in the subsequent
|
||||
URIs, if present, are ignored. Those in the first URI
|
||||
are used solely.
|
||||
.UNINDENT
|
||||
.SH OPTIONS
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-n, \-\-requests=<N>
|
||||
Number of requests.
|
||||
.sp
|
||||
Default: \fB1\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-c, \-\-clients=<N>
|
||||
Number of concurrent clients.
|
||||
.sp
|
||||
Default: \fB1\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-t, \-\-threads=<N>
|
||||
Number of native threads.
|
||||
.sp
|
||||
Default: \fB1\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-i, \-\-input\-file=<FILE>
|
||||
Path of a file with multiple URIs are separated by EOLs.
|
||||
This option will disable URIs getting from command\-line.
|
||||
If \(aq\-\(aq is given as <FILE>, URIs will be read from stdin.
|
||||
URIs are used in this order for each client. All URIs
|
||||
are used, then first URI is used and then 2nd URI, and
|
||||
so on. The scheme, host and port in the subsequent
|
||||
URIs, if present, are ignored. Those in the first URI
|
||||
are used solely.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-m, \-\-max\-concurrent\-streams=(auto|<N>)
|
||||
Max concurrent streams to issue per session. If "auto"
|
||||
is given, the number of given URIs is used.
|
||||
.sp
|
||||
Default: \fBauto\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-w, \-\-window\-bits=<N>
|
||||
Sets the stream level initial window size to (2**<N>)\-1.
|
||||
For SPDY, 2**<N> is used instead.
|
||||
.sp
|
||||
Default: \fB30\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-W, \-\-connection\-window\-bits=<N>
|
||||
Sets the connection level initial window size to
|
||||
(2**<N>)\-1. For SPDY, if <N> is strictly less than 16,
|
||||
this option is ignored. Otherwise 2**<N> is used for
|
||||
SPDY.
|
||||
.sp
|
||||
Default: \fB30\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-H, \-\-header=<HEADER>
|
||||
Add/Override a header to the requests.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-p, \-\-no\-tls\-proto=<PROTOID>
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
accessing http URI without SSL/TLS.
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c\-14
|
||||
.sp
|
||||
Default: \fBh2c\-14\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-d, \-\-data=<FILE>
|
||||
Post FILE to server. The request method is changed to
|
||||
POST.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-v, \-\-verbose
|
||||
Output debug information.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-version
|
||||
Display version information and exit.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-h, \-\-help
|
||||
Display this help and exit.
|
||||
.UNINDENT
|
||||
.SH OUTPUT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B requests
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B total
|
||||
The number of requests h2load was instructed to make.
|
||||
.TP
|
||||
.B started
|
||||
The number of requests h2load has started.
|
||||
.TP
|
||||
.B done
|
||||
The number of requests completed.
|
||||
.TP
|
||||
.B succeeded
|
||||
The number of requests completed successfully. Only HTTP status
|
||||
code 2xx or3xx are considered as success.
|
||||
.TP
|
||||
.B failed
|
||||
The number of requests failed, including HTTP level failures
|
||||
(non\-successful HTTP status code).
|
||||
.TP
|
||||
.B errored
|
||||
The number of requests failed, except for HTTP level failures.
|
||||
status code. This is the subset of the number reported in
|
||||
\fBfailed\fP and most likely the network level failures or stream
|
||||
was reset by RST_STREAM.
|
||||
.UNINDENT
|
||||
.TP
|
||||
.B status codes
|
||||
The number of status code h2load received.
|
||||
.TP
|
||||
.B traffic
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B total
|
||||
The number of bytes received from the server "on the wire". If
|
||||
requests were made via TLS, this value is the number of decrpyted
|
||||
bytes.
|
||||
.TP
|
||||
.B headers
|
||||
The number of response header bytes from the server without
|
||||
decompression. For HTTP/2, this is the sum of the payload of
|
||||
HEADERS frame. For SPDY, this is the sum of the payload of
|
||||
SYN_REPLY frame.
|
||||
.TP
|
||||
.B data
|
||||
The number of response body bytes received from the server.
|
||||
.UNINDENT
|
||||
.TP
|
||||
.B time for request
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B min
|
||||
The minimum time taken for request and response.
|
||||
.TP
|
||||
.B max
|
||||
The maximum time taken for request and response.
|
||||
.TP
|
||||
.B mean
|
||||
The mean time taken for request and response.
|
||||
.TP
|
||||
.B sd
|
||||
The standard deviation of the time taken for request and response.
|
||||
.TP
|
||||
.B +/\- sd
|
||||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/\- sd) against total number of successful requests.
|
||||
.UNINDENT
|
||||
.TP
|
||||
.B time for connect
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B min
|
||||
The minimum time taken to connect to a server.
|
||||
.TP
|
||||
.B max
|
||||
The maximum time taken to connect to a server.
|
||||
.TP
|
||||
.B mean
|
||||
The mean time taken to connect to a server.
|
||||
.TP
|
||||
.B sd
|
||||
The standard deviation of the time taken to connect to a server.
|
||||
.TP
|
||||
.B +/\- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/\- sd) against total number of successful
|
||||
connections.
|
||||
.UNINDENT
|
||||
.TP
|
||||
.B time for 1st byte (of (decrypted in case of TLS) application data)
|
||||
.INDENT 7.0
|
||||
.TP
|
||||
.B min
|
||||
The minimum time taken to get 1st byte from a server.
|
||||
.TP
|
||||
.B max
|
||||
The maximum time taken to get 1st byte from a server.
|
||||
.TP
|
||||
.B mean
|
||||
The mean time taken to get 1st byte from a server.
|
||||
.TP
|
||||
.B sd
|
||||
The standard deviation of the time taken to get 1st byte from a
|
||||
server.
|
||||
.TP
|
||||
.B +/\- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/\- sd) against total number of successful
|
||||
connections.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.SH FLOW CONTROL
|
||||
.sp
|
||||
h2load sets large flow control window by default, and effectively
|
||||
disables flow control to avoid under utilization of server
|
||||
performance. To set smaller flow control window, use \fI\%\-w\fP and
|
||||
\fI\%\-W\fP options. For example, use \fB\-w16 \-W16\fP to set default
|
||||
window size described in HTTP/2 and SPDY protocol specification.
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
\fInghttp(1)\fP, \fInghttpd(1)\fP, \fInghttpx(1)\fP
|
||||
.SH AUTHOR
|
||||
Tatsuhiro Tsujikawa
|
||||
.SH COPYRIGHT
|
||||
2012, 2015, Tatsuhiro Tsujikawa
|
||||
.\" Generated by docutils manpage writer.
|
||||
.
|
||||
204
doc/h2load.1.rst
204
doc/h2load.1.rst
@@ -1,204 +0,0 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: h2load
|
||||
|
||||
h2load(1)
|
||||
=========
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
**h2load** [OPTIONS]... [URI]...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
benchmarking tool for HTTP/2 and SPDY server
|
||||
|
||||
.. describe:: <URI>
|
||||
|
||||
Specify URI to access. Multiple URIs can be specified.
|
||||
URIs are used in this order for each client. All URIs
|
||||
are used, then first URI is used and then 2nd URI, and
|
||||
so on. The scheme, host and port in the subsequent
|
||||
URIs, if present, are ignored. Those in the first URI
|
||||
are used solely.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
.. option:: -n, --requests=<N>
|
||||
|
||||
Number of requests.
|
||||
|
||||
Default: ``1``
|
||||
|
||||
.. option:: -c, --clients=<N>
|
||||
|
||||
Number of concurrent clients.
|
||||
|
||||
Default: ``1``
|
||||
|
||||
.. option:: -t, --threads=<N>
|
||||
|
||||
Number of native threads.
|
||||
|
||||
Default: ``1``
|
||||
|
||||
.. option:: -i, --input-file=<FILE>
|
||||
|
||||
Path of a file with multiple URIs are separated by EOLs.
|
||||
This option will disable URIs getting from command-line.
|
||||
If '-' is given as <FILE>, URIs will be read from stdin.
|
||||
URIs are used in this order for each client. All URIs
|
||||
are used, then first URI is used and then 2nd URI, and
|
||||
so on. The scheme, host and port in the subsequent
|
||||
URIs, if present, are ignored. Those in the first URI
|
||||
are used solely.
|
||||
|
||||
.. option:: -m, --max-concurrent-streams=(auto|<N>)
|
||||
|
||||
Max concurrent streams to issue per session. If "auto"
|
||||
is given, the number of given URIs is used.
|
||||
|
||||
Default: ``auto``
|
||||
|
||||
.. option:: -w, --window-bits=<N>
|
||||
|
||||
Sets the stream level initial window size to (2\*\*<N>)-1.
|
||||
For SPDY, 2**<N> is used instead.
|
||||
|
||||
Default: ``30``
|
||||
|
||||
.. option:: -W, --connection-window-bits=<N>
|
||||
|
||||
Sets the connection level initial window size to
|
||||
(2**<N>)-1. For SPDY, if <N> is strictly less than 16,
|
||||
this option is ignored. Otherwise 2\*\*<N> is used for
|
||||
SPDY.
|
||||
|
||||
Default: ``30``
|
||||
|
||||
.. option:: -H, --header=<HEADER>
|
||||
|
||||
Add/Override a header to the requests.
|
||||
|
||||
.. option:: -p, --no-tls-proto=<PROTOID>
|
||||
|
||||
Specify ALPN identifier of the protocol to be used when
|
||||
accessing http URI without SSL/TLS.
|
||||
Available protocols: spdy/2, spdy/3, spdy/3.1 and h2c-14
|
||||
|
||||
Default: ``h2c-14``
|
||||
|
||||
.. option:: -d, --data=<FILE>
|
||||
|
||||
Post FILE to server. The request method is changed to
|
||||
POST.
|
||||
|
||||
.. option:: -v, --verbose
|
||||
|
||||
Output debug information.
|
||||
|
||||
.. option:: --version
|
||||
|
||||
Display version information and exit.
|
||||
|
||||
.. option:: -h, --help
|
||||
|
||||
Display this help and exit.
|
||||
|
||||
OUTPUT
|
||||
------
|
||||
|
||||
requests
|
||||
total
|
||||
The number of requests h2load was instructed to make.
|
||||
started
|
||||
The number of requests h2load has started.
|
||||
done
|
||||
The number of requests completed.
|
||||
succeeded
|
||||
The number of requests completed successfully. Only HTTP status
|
||||
code 2xx or3xx are considered as success.
|
||||
failed
|
||||
The number of requests failed, including HTTP level failures
|
||||
(non-successful HTTP status code).
|
||||
errored
|
||||
The number of requests failed, except for HTTP level failures.
|
||||
status code. This is the subset of the number reported in
|
||||
``failed`` and most likely the network level failures or stream
|
||||
was reset by RST_STREAM.
|
||||
|
||||
status codes
|
||||
The number of status code h2load received.
|
||||
|
||||
traffic
|
||||
total
|
||||
The number of bytes received from the server "on the wire". If
|
||||
requests were made via TLS, this value is the number of decrpyted
|
||||
bytes.
|
||||
headers
|
||||
The number of response header bytes from the server without
|
||||
decompression. For HTTP/2, this is the sum of the payload of
|
||||
HEADERS frame. For SPDY, this is the sum of the payload of
|
||||
SYN_REPLY frame.
|
||||
data
|
||||
The number of response body bytes received from the server.
|
||||
|
||||
time for request
|
||||
min
|
||||
The minimum time taken for request and response.
|
||||
max
|
||||
The maximum time taken for request and response.
|
||||
mean
|
||||
The mean time taken for request and response.
|
||||
sd
|
||||
The standard deviation of the time taken for request and response.
|
||||
+/- sd
|
||||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/- sd) against total number of successful requests.
|
||||
|
||||
time for connect
|
||||
min
|
||||
The minimum time taken to connect to a server.
|
||||
max
|
||||
The maximum time taken to connect to a server.
|
||||
mean
|
||||
The mean time taken to connect to a server.
|
||||
sd
|
||||
The standard deviation of the time taken to connect to a server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
time for 1st byte (of (decrypted in case of TLS) application data)
|
||||
min
|
||||
The minimum time taken to get 1st byte from a server.
|
||||
max
|
||||
The maximum time taken to get 1st byte from a server.
|
||||
mean
|
||||
The mean time taken to get 1st byte from a server.
|
||||
sd
|
||||
The standard deviation of the time taken to get 1st byte from a
|
||||
server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
FLOW CONTROL
|
||||
------------
|
||||
|
||||
h2load sets large flow control window by default, and effectively
|
||||
disables flow control to avoid under utilization of server
|
||||
performance. To set smaller flow control window, use :option:`-w` and
|
||||
:option:`-W` options. For example, use ``-w16 -W16`` to set default
|
||||
window size described in HTTP/2 and SPDY protocol specification.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`
|
||||
@@ -1,93 +0,0 @@
|
||||
OUTPUT
|
||||
------
|
||||
|
||||
requests
|
||||
total
|
||||
The number of requests h2load was instructed to make.
|
||||
started
|
||||
The number of requests h2load has started.
|
||||
done
|
||||
The number of requests completed.
|
||||
succeeded
|
||||
The number of requests completed successfully. Only HTTP status
|
||||
code 2xx or3xx are considered as success.
|
||||
failed
|
||||
The number of requests failed, including HTTP level failures
|
||||
(non-successful HTTP status code).
|
||||
errored
|
||||
The number of requests failed, except for HTTP level failures.
|
||||
status code. This is the subset of the number reported in
|
||||
``failed`` and most likely the network level failures or stream
|
||||
was reset by RST_STREAM.
|
||||
|
||||
status codes
|
||||
The number of status code h2load received.
|
||||
|
||||
traffic
|
||||
total
|
||||
The number of bytes received from the server "on the wire". If
|
||||
requests were made via TLS, this value is the number of decrpyted
|
||||
bytes.
|
||||
headers
|
||||
The number of response header bytes from the server without
|
||||
decompression. For HTTP/2, this is the sum of the payload of
|
||||
HEADERS frame. For SPDY, this is the sum of the payload of
|
||||
SYN_REPLY frame.
|
||||
data
|
||||
The number of response body bytes received from the server.
|
||||
|
||||
time for request
|
||||
min
|
||||
The minimum time taken for request and response.
|
||||
max
|
||||
The maximum time taken for request and response.
|
||||
mean
|
||||
The mean time taken for request and response.
|
||||
sd
|
||||
The standard deviation of the time taken for request and response.
|
||||
+/- sd
|
||||
The fraction of the number of requests within standard deviation
|
||||
range (mean +/- sd) against total number of successful requests.
|
||||
|
||||
time for connect
|
||||
min
|
||||
The minimum time taken to connect to a server.
|
||||
max
|
||||
The maximum time taken to connect to a server.
|
||||
mean
|
||||
The mean time taken to connect to a server.
|
||||
sd
|
||||
The standard deviation of the time taken to connect to a server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
time for 1st byte (of (decrypted in case of TLS) application data)
|
||||
min
|
||||
The minimum time taken to get 1st byte from a server.
|
||||
max
|
||||
The maximum time taken to get 1st byte from a server.
|
||||
mean
|
||||
The mean time taken to get 1st byte from a server.
|
||||
sd
|
||||
The standard deviation of the time taken to get 1st byte from a
|
||||
server.
|
||||
+/- sd
|
||||
The fraction of the number of connections within standard
|
||||
deviation range (mean +/- sd) against total number of successful
|
||||
connections.
|
||||
|
||||
FLOW CONTROL
|
||||
------------
|
||||
|
||||
h2load sets large flow control window by default, and effectively
|
||||
disables flow control to avoid under utilization of server
|
||||
performance. To set smaller flow control window, use :option:`-w` and
|
||||
:option:`-W` options. For example, use ``-w16 -W16`` to set default
|
||||
window size described in HTTP/2 and SPDY protocol specification.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`
|
||||
@@ -1 +0,0 @@
|
||||
.. include:: @top_srcdir@/doc/sources/libnghttp2_asio.rst
|
||||
127
doc/mkapiref.py
127
doc/mkapiref.py
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -24,21 +24,19 @@
|
||||
|
||||
# Generates API reference from C source code.
|
||||
from __future__ import print_function # At least python 2.6 is required
|
||||
import re, sys, argparse, os.path
|
||||
import re, sys, argparse
|
||||
|
||||
class FunctionDoc:
|
||||
def __init__(self, name, content, domain):
|
||||
self.name = name
|
||||
self.content = content
|
||||
self.domain = domain
|
||||
if self.domain == 'function':
|
||||
self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1)
|
||||
|
||||
def write(self, out):
|
||||
out.write('.. {}:: {}\n'.format(self.domain, self.name))
|
||||
out.write('\n')
|
||||
print('''.. {}:: {}'''.format(self.domain, self.name))
|
||||
print('')
|
||||
for line in self.content:
|
||||
out.write(' {}\n'.format(line))
|
||||
print(' {}'.format(line))
|
||||
|
||||
class StructDoc:
|
||||
def __init__(self, name, content, members, member_domain):
|
||||
@@ -49,17 +47,17 @@ class StructDoc:
|
||||
|
||||
def write(self, out):
|
||||
if self.name:
|
||||
out.write('.. type:: {}\n'.format(self.name))
|
||||
out.write('\n')
|
||||
print('''.. type:: {}'''.format(self.name))
|
||||
print('')
|
||||
for line in self.content:
|
||||
out.write(' {}\n'.format(line))
|
||||
out.write('\n')
|
||||
print(' {}'.format(line))
|
||||
print('')
|
||||
for name, content in self.members:
|
||||
out.write(' .. {}:: {}\n'.format(self.member_domain, name))
|
||||
out.write('\n')
|
||||
print(''' .. {}:: {}'''.format(self.member_domain, name))
|
||||
print('')
|
||||
for line in content:
|
||||
out.write(' {}\n'.format(line))
|
||||
out.write('\n')
|
||||
print(''' {}'''.format(line))
|
||||
print('')
|
||||
|
||||
class MacroDoc:
|
||||
def __init__(self, name, content):
|
||||
@@ -67,10 +65,10 @@ class MacroDoc:
|
||||
self.content = content
|
||||
|
||||
def write(self, out):
|
||||
out.write('''.. macro:: {}\n'''.format(self.name))
|
||||
out.write('\n')
|
||||
print('''.. macro:: {}'''.format(self.name))
|
||||
print('')
|
||||
for line in self.content:
|
||||
out.write(' {}\n'.format(line))
|
||||
print(' {}'.format(line))
|
||||
|
||||
def make_api_ref(infiles):
|
||||
macros = []
|
||||
@@ -95,65 +93,19 @@ def make_api_ref(infiles):
|
||||
enums.append(process_enum(infile))
|
||||
elif doctype == '@macro':
|
||||
macros.append(process_macro(infile))
|
||||
return macros, enums, types, functions
|
||||
|
||||
alldocs = [('Macros', macros),
|
||||
('Enums', enums),
|
||||
('Types (structs, unions and typedefs)', types),
|
||||
('Functions', functions)]
|
||||
|
||||
def output(
|
||||
indexfile, macrosfile, enumsfile, typesfile, funcsdir,
|
||||
macros, enums, types, functions):
|
||||
indexfile.write('''
|
||||
API Reference
|
||||
=============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
macros
|
||||
enums
|
||||
types
|
||||
''')
|
||||
|
||||
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 <nghttp2/nghttp2.h>*
|
||||
|
||||
'''.format(funcname=doc.funcname, secul='='*len(doc.funcname)))
|
||||
doc.write(f)
|
||||
for title, docs in alldocs:
|
||||
if not docs:
|
||||
continue
|
||||
print(title)
|
||||
print('-'*len(title))
|
||||
for doc in docs:
|
||||
doc.write(sys.stdout)
|
||||
print('')
|
||||
print('')
|
||||
|
||||
def process_macro(infile):
|
||||
content = read_content(infile)
|
||||
@@ -222,7 +174,6 @@ def process_function(domain, infile):
|
||||
func_proto = ''.join(func_proto)
|
||||
func_proto = re.sub(r';\n$', '', func_proto)
|
||||
func_proto = re.sub(r'\s+', ' ', func_proto)
|
||||
func_proto = re.sub(r'NGHTTP2_EXTERN ', '', func_proto)
|
||||
return FunctionDoc(func_proto, content, domain)
|
||||
|
||||
def read_content(infile):
|
||||
@@ -248,30 +199,12 @@ def transform_content(content):
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Generate API reference")
|
||||
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('--header', type=argparse.FileType('r'),
|
||||
help='header inserted at the top of the page')
|
||||
parser.add_argument('files', nargs='+', type=argparse.FileType('r'),
|
||||
help='source file')
|
||||
args = parser.parse_args()
|
||||
macros = []
|
||||
enums = []
|
||||
types = []
|
||||
funcs = []
|
||||
if args.header:
|
||||
print(args.header.read())
|
||||
for infile in args.files:
|
||||
m, e, t, f = make_api_ref(args.files)
|
||||
macros.extend(m)
|
||||
enums.extend(e)
|
||||
types.extend(t)
|
||||
funcs.extend(f)
|
||||
funcs.sort(key=lambda x: x.funcname)
|
||||
output(
|
||||
args.index, args.macros, args.enums, args.types, args.funcsdir,
|
||||
macros, enums, types, funcs)
|
||||
make_api_ref(args.files)
|
||||
|
||||
323
doc/nghttp.1
323
doc/nghttp.1
@@ -1,289 +1,90 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTP" "1" "May 15, 2015" "0.7.15" "nghttp2"
|
||||
.\" nghttp2 manual page
|
||||
.TH nghttp2 "1" "January 2014" "nghttp2" "User Commands"
|
||||
.SH NAME
|
||||
nghttp \- HTTP/2 experimental client
|
||||
.
|
||||
.nr rst2man-indent-level 0
|
||||
.
|
||||
.de1 rstReportMargin
|
||||
\\$1 \\n[an-margin]
|
||||
level \\n[rst2man-indent-level]
|
||||
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
-
|
||||
\\n[rst2man-indent0]
|
||||
\\n[rst2man-indent1]
|
||||
\\n[rst2man-indent2]
|
||||
..
|
||||
.de1 INDENT
|
||||
.\" .rstReportMargin pre:
|
||||
. RS \\$1
|
||||
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
|
||||
. nr rst2man-indent-level +1
|
||||
.\" .rstReportMargin post:
|
||||
..
|
||||
.de UNINDENT
|
||||
. RE
|
||||
.\" indent \\n[an-margin]
|
||||
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.nr rst2man-indent-level -1
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
nghttp2 \- HTTP2 experimental client
|
||||
.SH SYNOPSIS
|
||||
.sp
|
||||
\fBnghttp\fP [OPTIONS]... <URI>...
|
||||
\fBnghttp\fP [\fIOPTIONS\fP] \fIURI\fP...
|
||||
.SH DESCRIPTION
|
||||
.sp
|
||||
HTTP/2 experimental client
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B <URI>
|
||||
Specify URI to access.
|
||||
.UNINDENT
|
||||
Experimental client for HTTP 2.0.
|
||||
.SH OPTIONS
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-v, \-\-verbose
|
||||
Print debug information such as reception and
|
||||
transmission of frames and name/value pairs. Specifying
|
||||
this option multiple times increases verbosity.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Print debug information such as reception/
|
||||
transmission of frames and name/value pairs.
|
||||
.TP
|
||||
.B \-n, \-\-null\-out
|
||||
\fB\-n\fR, \fB\-\-null\-out\fR
|
||||
Discard downloaded data.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-O, \-\-remote\-name
|
||||
Save download data in the current directory. The
|
||||
filename is dereived from URI. If URI ends with \(aq\fI/\fP\(aq,
|
||||
\(aqindex.html\(aq is used as a filename. Not implemented
|
||||
yet.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-O\fR, \fB\-\-remote\-name\fR
|
||||
Save download data in the current directory.
|
||||
The filename is dereived from URI. If URI
|
||||
ends with '/', 'index.html' is used as a
|
||||
filename. Not implemented yet.
|
||||
.TP
|
||||
.B \-t, \-\-timeout=<DURATION>
|
||||
Timeout each request after <DURATION>. Set 0 to disable
|
||||
timeout.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-t\fR, \fB\-\-timeout=\fR<N>
|
||||
Timeout each request after <N> seconds.
|
||||
.TP
|
||||
.B \-w, \-\-window\-bits=<N>
|
||||
Sets the stream level initial window size to 2**<N>\-1.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-w\fR, \fB\-\-window\-bits=\fR<N>
|
||||
Sets the stream level initial window size
|
||||
to 2**<N>\-1.
|
||||
.TP
|
||||
.B \-W, \-\-connection\-window\-bits=<N>
|
||||
Sets the connection level initial window size to
|
||||
2**<N>\-1.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-W\fR, \fB\-\-connection\-window\-bits=\fR<N>
|
||||
Sets the connection level initial window
|
||||
size to 2**<N>\-1.
|
||||
.TP
|
||||
.B \-a, \-\-get\-assets
|
||||
Download assets such as stylesheets, images and script
|
||||
files linked from the downloaded resource. Only links
|
||||
whose origins are the same with the linking resource
|
||||
will be downloaded. nghttp prioritizes resources using
|
||||
HTTP/2 dependency based priority. The priority order,
|
||||
from highest to lowest, is html itself, css, javascript
|
||||
and images.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-a\fR, \fB\-\-get\-assets\fR
|
||||
Download assets such as stylesheets, images
|
||||
and script files linked from the downloaded
|
||||
resource. Only links whose origins are the
|
||||
same with the linking resource will be
|
||||
downloaded.
|
||||
.TP
|
||||
.B \-s, \-\-stat
|
||||
\fB\-s\fR, \fB\-\-stat\fR
|
||||
Print statistics.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-H, \-\-header=<HEADER>
|
||||
Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-H\fR, \fB\-\-header\fR
|
||||
Add a header to the requests.
|
||||
.TP
|
||||
.B \-\-trailer=<HEADER>
|
||||
Add a trailer header to the requests. <HEADER> must not
|
||||
include pseudo header field (header field name starting
|
||||
with \(aq:\(aq). To send trailer, one must use \fI\%\-d\fP option to
|
||||
send request body. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-\-cert=\fR<CERT>
|
||||
Use the specified client certificate file.
|
||||
The file must be in PEM format.
|
||||
.TP
|
||||
.B \-\-cert=<CERT>
|
||||
Use the specified client certificate file. The file
|
||||
\fB\-\-key=\fR<KEY>
|
||||
Use the client private key file. The file
|
||||
must be in PEM format.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-key=<KEY>
|
||||
Use the client private key file. The file must be in
|
||||
PEM format.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-d\fR, \fB\-\-data=\fR<FILE>
|
||||
Post FILE to server. If \- is given, data
|
||||
will be read from stdin.
|
||||
.TP
|
||||
.B \-d, \-\-data=<FILE>
|
||||
Post FILE to server. If \(aq\-\(aq is given, data will be read
|
||||
from stdin.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-m\fR, \fB\-\-multiply=\fR<N> Request each URI <N> times. By default, same
|
||||
URI is not requested twice. This option
|
||||
disables it too.
|
||||
.TP
|
||||
.B \-m, \-\-multiply=<N>
|
||||
Request each URI <N> times. By default, same URI is not
|
||||
requested twice. This option disables it too.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-f\fR, \fB\-\-no\-flow\-control\fR
|
||||
Disables connection and stream level flow
|
||||
controls.
|
||||
.TP
|
||||
.B \-u, \-\-upgrade
|
||||
Perform HTTP Upgrade for HTTP/2. This option is ignored
|
||||
if the request URI has https scheme. If \fI\%\-d\fP is used, the
|
||||
HTTP upgrade request is performed with OPTIONS method.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-u\fR, \fB\-\-upgrade\fR
|
||||
Perform HTTP Upgrade for HTTP/2.0. This
|
||||
option is ignored if the request URI has
|
||||
https scheme.
|
||||
If \fB\-d\fR is used, the HTTP upgrade request is
|
||||
performed with OPTIONS method.
|
||||
.TP
|
||||
.B \-p, \-\-weight=<WEIGHT>
|
||||
Sets priority group weight. The valid value range is
|
||||
[1, 256], inclusive.
|
||||
.sp
|
||||
Default: \fB16\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-p\fR, \fB\-\-pri=\fR<PRIORITY>
|
||||
Sets stream priority. Default: 1073741824
|
||||
.TP
|
||||
.B \-M, \-\-peer\-max\-concurrent\-streams=<N>
|
||||
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of
|
||||
remote endpoint as if it is received in SETTINGS frame.
|
||||
The default is large enough as it is seen as unlimited.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-M\fR, \fB\-\-peer\-max\-concurrent\-streams=\fR<N>
|
||||
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS
|
||||
value of remote endpoint as if it is
|
||||
received in SETTINGS frame. The default
|
||||
is large enough as it is seen as unlimited.
|
||||
.TP
|
||||
.B \-c, \-\-header\-table\-size=<SIZE>
|
||||
\fB\-c\fR, \fB\-\-header\-table\-size=\fR<N>
|
||||
Specify decoder header table size.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-b, \-\-padding=<N>
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-r, \-\-har=<FILE>
|
||||
Output HTTP transactions <FILE> in HAR format. If \(aq\-\(aq
|
||||
is given, data is written to stdout.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-color
|
||||
\fB\-\-color\fR
|
||||
Force colored log output.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-continuation
|
||||
Send large header to test CONTINUATION.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-content\-length
|
||||
Don\(aqt send content\-length header field.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-dep
|
||||
Don\(aqt send dependency based priority hint to server.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-hexdump
|
||||
Display the incoming traffic in hexadecimal (Canonical
|
||||
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||
are used.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-no\-push
|
||||
Disable server push.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-version
|
||||
Display version information and exit.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-h, \-\-help
|
||||
Display this help and exit.
|
||||
.UNINDENT
|
||||
.sp
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
.sp
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||
is omitted, a second is used as unit.
|
||||
.SH DEPENDENCY BASED PRIORITY
|
||||
.sp
|
||||
nghttp sends priority hints to server by default unless
|
||||
\fI\%\-\-no\-dep\fP is used. nghttp mimics the way Firefox employs to
|
||||
manages dependency using idle streams. We follows the behaviour of
|
||||
Firefox Nightly as of April, 2015, and nghttp\(aqs behaviour is very
|
||||
static and could be different from Firefox in detail. But reproducing
|
||||
the same behaviour of Firefox is not our goal. The goal is provide
|
||||
the easy way to test out the dependency priority in server
|
||||
implementation.
|
||||
.sp
|
||||
When connection is established, nghttp sends 5 PRIORITY frames to idle
|
||||
streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
|
||||
tree:
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
.sp
|
||||
.nf
|
||||
.ft C
|
||||
+\-\-\-\-\-+
|
||||
|id=0 |
|
||||
+\-\-\-\-\-+
|
||||
^ ^ ^
|
||||
w=201 / | \e w=1
|
||||
/ | \e
|
||||
/ w=101| \e
|
||||
+\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+
|
||||
|id=3 | |id=5 | |id=7 |
|
||||
+\-\-\-\-\-+ +\-\-\-\-\-+ +\-\-\-\-\-+
|
||||
^ ^
|
||||
w=1 | w=1 |
|
||||
| |
|
||||
+\-\-\-\-\-+ +\-\-\-\-\-+
|
||||
|id=11| |id=9 |
|
||||
+\-\-\-\-\-+ +\-\-\-\-\-+
|
||||
.ft P
|
||||
.fi
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.sp
|
||||
In the above figure, \fBid\fP means stream ID, and \fBw\fP means weight.
|
||||
The stream 0 is non\-existence stream, and forms the root of the tree.
|
||||
The stream 7 and 9 are not used for now.
|
||||
.sp
|
||||
The URIs given in the command\-line depend on stream 11 with the weight
|
||||
given in \fI\%\-p\fP option, which defaults to 16.
|
||||
.sp
|
||||
If \fI\%\-a\fP option is used, nghttp parses the resource pointed by
|
||||
URI given in command\-line as html, and extracts resource links from
|
||||
it. When requesting those resources, nghttp uses dependency according
|
||||
to its resource type.
|
||||
.sp
|
||||
For CSS, and Javascript files inside "head" element, they depend on
|
||||
stream 3 with the weight 2. The Javascript files outside "head"
|
||||
element depend on stream 5 with the weight 2. The mages depend on
|
||||
stream 11 with the weight 12. The other resources (e.g., icon) depend
|
||||
on stream 11 with the weight 2.
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
\fInghttpd(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP
|
||||
.SH AUTHOR
|
||||
Tatsuhiro Tsujikawa
|
||||
.SH COPYRIGHT
|
||||
2012, 2015, Tatsuhiro Tsujikawa
|
||||
.\" Generated by docutils manpage writer.
|
||||
.
|
||||
.SH "SEE ALSO"
|
||||
nghttpd(1), nghttpx(1)
|
||||
|
||||
233
doc/nghttp.1.rst
233
doc/nghttp.1.rst
@@ -1,233 +0,0 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttp
|
||||
|
||||
nghttp(1)
|
||||
=========
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
**nghttp** [OPTIONS]... <URI>...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
HTTP/2 experimental client
|
||||
|
||||
.. describe:: <URI>
|
||||
|
||||
Specify URI to access.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
.. option:: -v, --verbose
|
||||
|
||||
Print debug information such as reception and
|
||||
transmission of frames and name/value pairs. Specifying
|
||||
this option multiple times increases verbosity.
|
||||
|
||||
.. option:: -n, --null-out
|
||||
|
||||
Discard downloaded data.
|
||||
|
||||
.. option:: -O, --remote-name
|
||||
|
||||
Save download data in the current directory. The
|
||||
filename is dereived from URI. If URI ends with '*/*',
|
||||
'index.html' is used as a filename. Not implemented
|
||||
yet.
|
||||
|
||||
.. option:: -t, --timeout=<DURATION>
|
||||
|
||||
Timeout each request after <DURATION>. Set 0 to disable
|
||||
timeout.
|
||||
|
||||
.. option:: -w, --window-bits=<N>
|
||||
|
||||
Sets the stream level initial window size to 2\*\*<N>-1.
|
||||
|
||||
.. option:: -W, --connection-window-bits=<N>
|
||||
|
||||
Sets the connection level initial window size to
|
||||
2\*\*<N>-1.
|
||||
|
||||
.. option:: -a, --get-assets
|
||||
|
||||
Download assets such as stylesheets, images and script
|
||||
files linked from the downloaded resource. Only links
|
||||
whose origins are the same with the linking resource
|
||||
will be downloaded. nghttp prioritizes resources using
|
||||
HTTP/2 dependency based priority. The priority order,
|
||||
from highest to lowest, is html itself, css, javascript
|
||||
and images.
|
||||
|
||||
.. option:: -s, --stat
|
||||
|
||||
Print statistics.
|
||||
|
||||
.. option:: -H, --header=<HEADER>
|
||||
|
||||
Add a header to the requests. Example: :option:`-H`\':method: PUT'
|
||||
|
||||
.. option:: --trailer=<HEADER>
|
||||
|
||||
Add a trailer header to the requests. <HEADER> must not
|
||||
include pseudo header field (header field name starting
|
||||
with ':'). To send trailer, one must use :option:`-d` option to
|
||||
send request body. Example: :option:`--trailer` 'foo: bar'.
|
||||
|
||||
.. option:: --cert=<CERT>
|
||||
|
||||
Use the specified client certificate file. The file
|
||||
must be in PEM format.
|
||||
|
||||
.. option:: --key=<KEY>
|
||||
|
||||
Use the client private key file. The file must be in
|
||||
PEM format.
|
||||
|
||||
.. option:: -d, --data=<FILE>
|
||||
|
||||
Post FILE to server. If '-' is given, data will be read
|
||||
from stdin.
|
||||
|
||||
.. option:: -m, --multiply=<N>
|
||||
|
||||
Request each URI <N> times. By default, same URI is not
|
||||
requested twice. This option disables it too.
|
||||
|
||||
.. option:: -u, --upgrade
|
||||
|
||||
Perform HTTP Upgrade for HTTP/2. This option is ignored
|
||||
if the request URI has https scheme. If :option:`-d` is used, the
|
||||
HTTP upgrade request is performed with OPTIONS method.
|
||||
|
||||
.. option:: -p, --weight=<WEIGHT>
|
||||
|
||||
Sets priority group weight. The valid value range is
|
||||
[1, 256], inclusive.
|
||||
|
||||
Default: ``16``
|
||||
|
||||
.. option:: -M, --peer-max-concurrent-streams=<N>
|
||||
|
||||
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of
|
||||
remote endpoint as if it is received in SETTINGS frame.
|
||||
The default is large enough as it is seen as unlimited.
|
||||
|
||||
.. option:: -c, --header-table-size=<SIZE>
|
||||
|
||||
Specify decoder header table size.
|
||||
|
||||
.. option:: -b, --padding=<N>
|
||||
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
|
||||
.. option:: -r, --har=<FILE>
|
||||
|
||||
Output HTTP transactions <FILE> in HAR format. If '-'
|
||||
is given, data is written to stdout.
|
||||
|
||||
.. option:: --color
|
||||
|
||||
Force colored log output.
|
||||
|
||||
.. option:: --continuation
|
||||
|
||||
Send large header to test CONTINUATION.
|
||||
|
||||
.. option:: --no-content-length
|
||||
|
||||
Don't send content-length header field.
|
||||
|
||||
.. option:: --no-dep
|
||||
|
||||
Don't send dependency based priority hint to server.
|
||||
|
||||
.. option:: --hexdump
|
||||
|
||||
Display the incoming traffic in hexadecimal (Canonical
|
||||
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||
are used.
|
||||
|
||||
.. option:: --no-push
|
||||
|
||||
Disable server push.
|
||||
|
||||
.. option:: --version
|
||||
|
||||
Display version information and exit.
|
||||
|
||||
.. option:: -h, --help
|
||||
|
||||
Display this help and exit.
|
||||
|
||||
|
||||
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||
is omitted, a second is used as unit.
|
||||
|
||||
DEPENDENCY BASED PRIORITY
|
||||
-------------------------
|
||||
|
||||
nghttp sends priority hints to server by default unless
|
||||
:option:`--no-dep` is used. nghttp mimics the way Firefox employs to
|
||||
manages dependency using idle streams. We follows the behaviour of
|
||||
Firefox Nightly as of April, 2015, and nghttp's behaviour is very
|
||||
static and could be different from Firefox in detail. But reproducing
|
||||
the same behaviour of Firefox is not our goal. The goal is provide
|
||||
the easy way to test out the dependency priority in server
|
||||
implementation.
|
||||
|
||||
When connection is established, nghttp sends 5 PRIORITY frames to idle
|
||||
streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
|
||||
tree::
|
||||
|
||||
+-----+
|
||||
|id=0 |
|
||||
+-----+
|
||||
^ ^ ^
|
||||
w=201 / | \ w=1
|
||||
/ | \
|
||||
/ w=101| \
|
||||
+-----+ +-----+ +-----+
|
||||
|id=3 | |id=5 | |id=7 |
|
||||
+-----+ +-----+ +-----+
|
||||
^ ^
|
||||
w=1 | w=1 |
|
||||
| |
|
||||
+-----+ +-----+
|
||||
|id=11| |id=9 |
|
||||
+-----+ +-----+
|
||||
|
||||
In the above figure, ``id`` means stream ID, and ``w`` means weight.
|
||||
The stream 0 is non-existence stream, and forms the root of the tree.
|
||||
The stream 7 and 9 are not used for now.
|
||||
|
||||
The URIs given in the command-line depend on stream 11 with the weight
|
||||
given in :option:`-p` option, which defaults to 16.
|
||||
|
||||
If :option:`-a` option is used, nghttp parses the resource pointed by
|
||||
URI given in command-line as html, and extracts resource links from
|
||||
it. When requesting those resources, nghttp uses dependency according
|
||||
to its resource type.
|
||||
|
||||
For CSS, and Javascript files inside "head" element, they depend on
|
||||
stream 3 with the weight 2. The Javascript files outside "head"
|
||||
element depend on stream 5 with the weight 2. The mages depend on
|
||||
stream 11 with the weight 12. The other resources (e.g., icon) depend
|
||||
on stream 11 with the weight 2.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
:manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||
@@ -1,55 +0,0 @@
|
||||
DEPENDENCY BASED PRIORITY
|
||||
-------------------------
|
||||
|
||||
nghttp sends priority hints to server by default unless
|
||||
:option:`--no-dep` is used. nghttp mimics the way Firefox employs to
|
||||
manages dependency using idle streams. We follows the behaviour of
|
||||
Firefox Nightly as of April, 2015, and nghttp's behaviour is very
|
||||
static and could be different from Firefox in detail. But reproducing
|
||||
the same behaviour of Firefox is not our goal. The goal is provide
|
||||
the easy way to test out the dependency priority in server
|
||||
implementation.
|
||||
|
||||
When connection is established, nghttp sends 5 PRIORITY frames to idle
|
||||
streams 3, 5, 7, 9 and 11 to create "anchor" nodes in dependency
|
||||
tree::
|
||||
|
||||
+-----+
|
||||
|id=0 |
|
||||
+-----+
|
||||
^ ^ ^
|
||||
w=201 / | \ w=1
|
||||
/ | \
|
||||
/ w=101| \
|
||||
+-----+ +-----+ +-----+
|
||||
|id=3 | |id=5 | |id=7 |
|
||||
+-----+ +-----+ +-----+
|
||||
^ ^
|
||||
w=1 | w=1 |
|
||||
| |
|
||||
+-----+ +-----+
|
||||
|id=11| |id=9 |
|
||||
+-----+ +-----+
|
||||
|
||||
In the above figure, ``id`` means stream ID, and ``w`` means weight.
|
||||
The stream 0 is non-existence stream, and forms the root of the tree.
|
||||
The stream 7 and 9 are not used for now.
|
||||
|
||||
The URIs given in the command-line depend on stream 11 with the weight
|
||||
given in :option:`-p` option, which defaults to 16.
|
||||
|
||||
If :option:`-a` option is used, nghttp parses the resource pointed by
|
||||
URI given in command-line as html, and extracts resource links from
|
||||
it. When requesting those resources, nghttp uses dependency according
|
||||
to its resource type.
|
||||
|
||||
For CSS, and Javascript files inside "head" element, they depend on
|
||||
stream 3 with the weight 2. The Javascript files outside "head"
|
||||
element depend on stream 5 with the weight 2. The mages depend on
|
||||
stream 11 with the weight 12. The other resources (e.g., icon) depend
|
||||
on stream 11 with the weight 2.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
:manpage:`nghttpd(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||
215
doc/nghttpd.1
215
doc/nghttpd.1
@@ -1,194 +1,55 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "NGHTTPD" "1" "May 15, 2015" "0.7.15" "nghttp2"
|
||||
.\" nghttpd manual page
|
||||
.TH nghttpd "1" "January 2014" "nghttpd" "User Commands"
|
||||
.SH NAME
|
||||
nghttpd \- HTTP/2 experimental server
|
||||
.
|
||||
.nr rst2man-indent-level 0
|
||||
.
|
||||
.de1 rstReportMargin
|
||||
\\$1 \\n[an-margin]
|
||||
level \\n[rst2man-indent-level]
|
||||
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
-
|
||||
\\n[rst2man-indent0]
|
||||
\\n[rst2man-indent1]
|
||||
\\n[rst2man-indent2]
|
||||
..
|
||||
.de1 INDENT
|
||||
.\" .rstReportMargin pre:
|
||||
. RS \\$1
|
||||
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
|
||||
. nr rst2man-indent-level +1
|
||||
.\" .rstReportMargin post:
|
||||
..
|
||||
.de UNINDENT
|
||||
. RE
|
||||
.\" indent \\n[an-margin]
|
||||
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.nr rst2man-indent-level -1
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
nghttpd \- HTTP 2.0 experimental server
|
||||
.SH SYNOPSIS
|
||||
.sp
|
||||
\fBnghttpd\fP [OPTION]... <PORT> [<PRIVATE_KEY> <CERT>]
|
||||
\fBnghttpd\fP [\fIOPTIONS\fP...] [\fIPRIVATE_KEY\fP \fICERT\fP]
|
||||
.SH DESCRIPTION
|
||||
.sp
|
||||
HTTP/2 experimental server
|
||||
.INDENT 0.0
|
||||
Experimental HTTP 2.0 server.
|
||||
.SH "Positional arguments"
|
||||
.TP
|
||||
.B <PORT>
|
||||
Specify listening port number.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fIPRIVATE_KEY\fP
|
||||
Set path to server's private key. Required
|
||||
unless either \fB\-p\fR or \fB\-\-client\fR is specified.
|
||||
.TP
|
||||
.B <PRIVATE_KEY>
|
||||
Set path to server\(aqs private key. Required unless
|
||||
\fI\%\-\-no\-tls\fP is specified.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B <CERT>
|
||||
Set path to server\(aqs certificate. Required unless
|
||||
\fI\%\-\-no\-tls\fP is specified.
|
||||
.UNINDENT
|
||||
\fICERT\fP
|
||||
Set path to server's certificate. Required
|
||||
unless either \fB\-p\fR or \fB\-\-client\fR is specified.
|
||||
.SH OPTIONS
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-a, \-\-address=<ADDR>
|
||||
The address to bind to. If not specified the default IP
|
||||
address determined by getaddrinfo is used.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-D\fR, \fB\-\-daemon\fR
|
||||
Run in a background. If \fB\-D\fR is used, the
|
||||
current working directory is changed to '/'.
|
||||
Therefore if this option is used, \fB\-d\fR option
|
||||
must be specified.
|
||||
.TP
|
||||
.B \-D, \-\-daemon
|
||||
Run in a background. If \fI\%\-D\fP is used, the current working
|
||||
directory is changed to \(aq\fI/\fP\(aq. Therefore if this option
|
||||
is used, \fI\%\-d\fP option must be specified.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-V, \-\-verify\-client
|
||||
The server sends a client certificate request. If the
|
||||
client did not return a certificate, the handshake is
|
||||
terminated. Currently, this option just requests a
|
||||
\fB\-V\fR, \fB\-\-verify\-client\fR
|
||||
The server sends a client certificate
|
||||
request. If the client did not return a
|
||||
certificate, the handshake is terminated.
|
||||
Currently, this option just requests a
|
||||
client certificate and does not verify it.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-d, \-\-htdocs=<PATH>
|
||||
Specify document root. If this option is not specified,
|
||||
the document root is the current working directory.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-d\fR, \fB\-\-htdocs=\fR<PATH>
|
||||
Specify document root. If this option is
|
||||
not specified, the document root is the
|
||||
current working directory.
|
||||
.TP
|
||||
.B \-v, \-\-verbose
|
||||
Print debug information such as reception/ transmission
|
||||
of frames and name/value pairs.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Print debug information such as reception/
|
||||
transmission of frames and name/value pairs.
|
||||
.TP
|
||||
.B \-\-no\-tls
|
||||
\fB\-\-no\-tls\fR
|
||||
Disable SSL/TLS.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-c, \-\-header\-table\-size=<SIZE>
|
||||
Specify decoder header table size.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
\fB\-f\fR, \fB\-\-no\-flow\-control\fR
|
||||
Disables connection and stream level flow
|
||||
controls.
|
||||
.TP
|
||||
.B \-\-color
|
||||
\fB\-\-color\fR
|
||||
Force colored log output.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-p, \-\-push=<PATH>=<PUSH_PATH,...>
|
||||
Push resources <PUSH_PATH>s when <PATH> is requested.
|
||||
This option can be used repeatedly to specify multiple
|
||||
push configurations. <PATH> and <PUSH_PATH>s are
|
||||
relative to document root. See \fI\%\-\-htdocs\fP option.
|
||||
Example: \fI\%\-p\fP/=/foo.png \fI\%\-p\fP/doc=/bar.css
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-b, \-\-padding=<N>
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-m, \-\-max\-concurrent\-streams=<N>
|
||||
Set the maximum number of the concurrent streams in one
|
||||
HTTP/2 session.
|
||||
.sp
|
||||
Default: \fB100\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-n, \-\-workers=<N>
|
||||
Set the number of worker threads.
|
||||
.sp
|
||||
Default: \fB1\fP
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-e, \-\-error\-gzip
|
||||
Make error response gzipped.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-dh\-param\-file=<PATH>
|
||||
Path to file that contains DH parameters in PEM format.
|
||||
Without this option, DHE cipher suites are not
|
||||
available.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-early\-response
|
||||
Start sending response when request HEADERS is received,
|
||||
rather than complete request is received.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-trailer=<HEADER>
|
||||
Add a trailer header to a response. <HEADER> must not
|
||||
include pseudo header field (header field name starting
|
||||
with \(aq:\(aq). The trailer is sent only if a response has
|
||||
body part. Example: \fI\%\-\-trailer\fP \(aqfoo: bar\(aq.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-hexdump
|
||||
Display the incoming traffic in hexadecimal (Canonical
|
||||
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||
are used.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-echo\-upload
|
||||
Send back uploaded content if method is POST or PUT.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-\-version
|
||||
Display version information and exit.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-h, \-\-help
|
||||
Display this help and exit.
|
||||
.UNINDENT
|
||||
.sp
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
\fInghttp(1)\fP, \fInghttpx(1)\fP, \fIh2load(1)\fP
|
||||
.SH AUTHOR
|
||||
Tatsuhiro Tsujikawa
|
||||
.SH COPYRIGHT
|
||||
2012, 2015, Tatsuhiro Tsujikawa
|
||||
.\" Generated by docutils manpage writer.
|
||||
.
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print this help.
|
||||
.SH "SEE ALSO"
|
||||
nghttp(1), nghttpx(1)
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttpd
|
||||
|
||||
nghttpd(1)
|
||||
==========
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
**nghttpd** [OPTION]... <PORT> [<PRIVATE_KEY> <CERT>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
HTTP/2 experimental server
|
||||
|
||||
.. describe:: <PORT>
|
||||
|
||||
Specify listening port number.
|
||||
|
||||
.. describe:: <PRIVATE_KEY>
|
||||
|
||||
|
||||
Set path to server's private key. Required unless
|
||||
:option:`--no-tls` is specified.
|
||||
|
||||
.. describe:: <CERT>
|
||||
|
||||
Set path to server's certificate. Required unless
|
||||
:option:`--no-tls` is specified.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
.. option:: -a, --address=<ADDR>
|
||||
|
||||
The address to bind to. If not specified the default IP
|
||||
address determined by getaddrinfo is used.
|
||||
|
||||
.. option:: -D, --daemon
|
||||
|
||||
Run in a background. If :option:`-D` is used, the current working
|
||||
directory is changed to '*/*'. Therefore if this option
|
||||
is used, :option:`-d` option must be specified.
|
||||
|
||||
.. option:: -V, --verify-client
|
||||
|
||||
The server sends a client certificate request. If the
|
||||
client did not return a certificate, the handshake is
|
||||
terminated. Currently, this option just requests a
|
||||
client certificate and does not verify it.
|
||||
|
||||
.. option:: -d, --htdocs=<PATH>
|
||||
|
||||
Specify document root. If this option is not specified,
|
||||
the document root is the current working directory.
|
||||
|
||||
.. option:: -v, --verbose
|
||||
|
||||
Print debug information such as reception/ transmission
|
||||
of frames and name/value pairs.
|
||||
|
||||
.. option:: --no-tls
|
||||
|
||||
Disable SSL/TLS.
|
||||
|
||||
.. option:: -c, --header-table-size=<SIZE>
|
||||
|
||||
Specify decoder header table size.
|
||||
|
||||
.. option:: --color
|
||||
|
||||
Force colored log output.
|
||||
|
||||
.. option:: -p, --push=<PATH>=<PUSH_PATH,...>
|
||||
|
||||
Push resources <PUSH_PATH>s when <PATH> is requested.
|
||||
This option can be used repeatedly to specify multiple
|
||||
push configurations. <PATH> and <PUSH_PATH>s are
|
||||
relative to document root. See :option:`--htdocs` option.
|
||||
Example: :option:`-p`\/=/foo.png :option:`-p`\/doc=/bar.css
|
||||
|
||||
.. option:: -b, --padding=<N>
|
||||
|
||||
Add at most <N> bytes to a frame payload as padding.
|
||||
Specify 0 to disable padding.
|
||||
|
||||
.. option:: -m, --max-concurrent-streams=<N>
|
||||
|
||||
Set the maximum number of the concurrent streams in one
|
||||
HTTP/2 session.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
.. option:: -n, --workers=<N>
|
||||
|
||||
Set the number of worker threads.
|
||||
|
||||
Default: ``1``
|
||||
|
||||
.. option:: -e, --error-gzip
|
||||
|
||||
Make error response gzipped.
|
||||
|
||||
.. option:: --dh-param-file=<PATH>
|
||||
|
||||
Path to file that contains DH parameters in PEM format.
|
||||
Without this option, DHE cipher suites are not
|
||||
available.
|
||||
|
||||
.. option:: --early-response
|
||||
|
||||
Start sending response when request HEADERS is received,
|
||||
rather than complete request is received.
|
||||
|
||||
.. option:: --trailer=<HEADER>
|
||||
|
||||
Add a trailer header to a response. <HEADER> must not
|
||||
include pseudo header field (header field name starting
|
||||
with ':'). The trailer is sent only if a response has
|
||||
body part. Example: :option:`--trailer` 'foo: bar'.
|
||||
|
||||
.. option:: --hexdump
|
||||
|
||||
Display the incoming traffic in hexadecimal (Canonical
|
||||
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||
are used.
|
||||
|
||||
.. option:: --echo-upload
|
||||
|
||||
Send back uploaded content if method is POST or PUT.
|
||||
|
||||
.. option:: --version
|
||||
|
||||
Display version information and exit.
|
||||
|
||||
.. option:: -h, --help
|
||||
|
||||
Display this help and exit.
|
||||
|
||||
|
||||
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
:manpage:`nghttp(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||
@@ -1,4 +0,0 @@
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
:manpage:`nghttp(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`
|
||||
@@ -1 +0,0 @@
|
||||
.. include:: @top_srcdir@/doc/sources/nghttpx-howto.rst
|
||||
1129
doc/nghttpx.1
1129
doc/nghttpx.1
File diff suppressed because it is too large
Load Diff
@@ -1,811 +0,0 @@
|
||||
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: nghttpx
|
||||
|
||||
nghttpx(1)
|
||||
==========
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
**nghttpx** [OPTIONS]... [<PRIVATE_KEY> <CERT>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
A reverse proxy for HTTP/2, HTTP/1 and SPDY.
|
||||
|
||||
.. describe:: <PRIVATE_KEY>
|
||||
|
||||
|
||||
Set path to server's private key. Required unless :option:`-p`\,
|
||||
:option:`--client` or :option:`\--frontend-no-tls` are given.
|
||||
|
||||
.. describe:: <CERT>
|
||||
|
||||
Set path to server's certificate. Required unless :option:`-p`\,
|
||||
:option:`--client` or :option:`\--frontend-no-tls` are given. To make OCSP
|
||||
stapling work, this must be absolute path.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
The options are categorized into several groups.
|
||||
|
||||
Connections
|
||||
~~~~~~~~~~~
|
||||
|
||||
.. option:: -b, --backend=<HOST,PORT>
|
||||
|
||||
Set backend host and port. The multiple backend
|
||||
addresses are accepted by repeating this option. UNIX
|
||||
domain socket can be specified by prefixing path name
|
||||
with "unix:" (e.g., unix:/var/run/backend.sock)
|
||||
|
||||
Default: ``127.0.0.1,80``
|
||||
|
||||
.. option:: -f, --frontend=<HOST,PORT>
|
||||
|
||||
Set frontend host and port. If <HOST> is '\*', it
|
||||
assumes all addresses including both IPv4 and IPv6.
|
||||
UNIX domain socket can be specified by prefixing path
|
||||
name with "unix:" (e.g., unix:/var/run/nghttpx.sock)
|
||||
|
||||
Default: ``*,3000``
|
||||
|
||||
.. option:: --backlog=<N>
|
||||
|
||||
Set listen backlog size.
|
||||
|
||||
Default: ``512``
|
||||
|
||||
.. option:: --backend-ipv4
|
||||
|
||||
Resolve backend hostname to IPv4 address only.
|
||||
|
||||
.. option:: --backend-ipv6
|
||||
|
||||
Resolve backend hostname to IPv6 address only.
|
||||
|
||||
.. option:: --backend-http-proxy-uri=<URI>
|
||||
|
||||
Specify proxy URI in the form
|
||||
http://[<USER>:<PASS>@]<PROXY>:<PORT>. If a proxy
|
||||
requires authentication, specify <USER> and <PASS>.
|
||||
Note that they must be properly percent-encoded. This
|
||||
proxy is used when the backend connection is HTTP/2.
|
||||
First, make a CONNECT request to the proxy and it
|
||||
connects to the backend on behalf of nghttpx. This
|
||||
forms tunnel. After that, nghttpx performs SSL/TLS
|
||||
handshake with the downstream through the tunnel. The
|
||||
timeouts when connecting and making CONNECT request can
|
||||
be specified by :option:`--backend-read-timeout` and
|
||||
:option:`--backend-write-timeout` options.
|
||||
|
||||
|
||||
Performance
|
||||
~~~~~~~~~~~
|
||||
|
||||
.. option:: -n, --workers=<N>
|
||||
|
||||
Set the number of worker threads.
|
||||
|
||||
Default: ``1``
|
||||
|
||||
.. option:: --read-rate=<SIZE>
|
||||
|
||||
Set maximum average read rate on frontend connection.
|
||||
Setting 0 to this option means read rate is unlimited.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --read-burst=<SIZE>
|
||||
|
||||
Set maximum read burst size on frontend connection.
|
||||
Setting 0 to this option means read burst size is
|
||||
unlimited.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --write-rate=<SIZE>
|
||||
|
||||
Set maximum average write rate on frontend connection.
|
||||
Setting 0 to this option means write rate is unlimited.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --write-burst=<SIZE>
|
||||
|
||||
Set maximum write burst size on frontend connection.
|
||||
Setting 0 to this option means write burst size is
|
||||
unlimited.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --worker-read-rate=<SIZE>
|
||||
|
||||
Set maximum average read rate on frontend connection per
|
||||
worker. Setting 0 to this option means read rate is
|
||||
unlimited. Not implemented yet.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --worker-read-burst=<SIZE>
|
||||
|
||||
Set maximum read burst size on frontend connection per
|
||||
worker. Setting 0 to this option means read burst size
|
||||
is unlimited. Not implemented yet.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --worker-write-rate=<SIZE>
|
||||
|
||||
Set maximum average write rate on frontend connection
|
||||
per worker. Setting 0 to this option means write rate
|
||||
is unlimited. Not implemented yet.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --worker-write-burst=<SIZE>
|
||||
|
||||
Set maximum write burst size on frontend connection per
|
||||
worker. Setting 0 to this option means write burst size
|
||||
is unlimited. Not implemented yet.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --worker-frontend-connections=<N>
|
||||
|
||||
Set maximum number of simultaneous connections frontend
|
||||
accepts. Setting 0 means unlimited.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --backend-http2-connections-per-worker=<N>
|
||||
|
||||
Set maximum number of HTTP/2 connections per worker.
|
||||
The default value is 0, which means the number of
|
||||
backend addresses specified by :option:`-b` option.
|
||||
|
||||
.. option:: --backend-http1-connections-per-host=<N>
|
||||
|
||||
Set maximum number of backend concurrent HTTP/1
|
||||
connections per host. This option is meaningful when :option:`-s`
|
||||
option is used. To limit the number of connections per
|
||||
frontend for default mode, use
|
||||
:option:`--backend-http1-connections-per-frontend`\.
|
||||
|
||||
Default: ``8``
|
||||
|
||||
.. option:: --backend-http1-connections-per-frontend=<N>
|
||||
|
||||
Set maximum number of backend concurrent HTTP/1
|
||||
connections per frontend. This option is only used for
|
||||
default mode. 0 means unlimited. To limit the number
|
||||
of connections per host for HTTP/2 or SPDY proxy mode
|
||||
(-s option), use :option:`--backend-http1-connections-per-host`\.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --rlimit-nofile=<N>
|
||||
|
||||
Set maximum number of open files (RLIMIT_NOFILE) to <N>.
|
||||
If 0 is given, nghttpx does not set the limit.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --backend-request-buffer=<SIZE>
|
||||
|
||||
Set buffer size used to store backend request.
|
||||
|
||||
Default: ``16K``
|
||||
|
||||
.. option:: --backend-response-buffer=<SIZE>
|
||||
|
||||
Set buffer size used to store backend response.
|
||||
|
||||
Default: ``16K``
|
||||
|
||||
|
||||
Timeout
|
||||
~~~~~~~
|
||||
|
||||
.. option:: --frontend-http2-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/2 and SPDY frontend
|
||||
connection.
|
||||
|
||||
Default: ``3m``
|
||||
|
||||
.. option:: --frontend-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/1.1 frontend connection.
|
||||
|
||||
Default: ``3m``
|
||||
|
||||
.. option:: --frontend-write-timeout=<DURATION>
|
||||
|
||||
Specify write timeout for all frontend connections.
|
||||
|
||||
Default: ``30s``
|
||||
|
||||
.. option:: --stream-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for HTTP/2 and SPDY streams. 0
|
||||
means no timeout.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --stream-write-timeout=<DURATION>
|
||||
|
||||
Specify write timeout for HTTP/2 and SPDY streams. 0
|
||||
means no timeout.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
.. option:: --backend-read-timeout=<DURATION>
|
||||
|
||||
Specify read timeout for backend connection.
|
||||
|
||||
Default: ``3m``
|
||||
|
||||
.. option:: --backend-write-timeout=<DURATION>
|
||||
|
||||
Specify write timeout for backend connection.
|
||||
|
||||
Default: ``30s``
|
||||
|
||||
.. option:: --backend-keep-alive-timeout=<DURATION>
|
||||
|
||||
Specify keep-alive timeout for backend connection.
|
||||
|
||||
Default: ``2s``
|
||||
|
||||
.. option:: --listener-disable-timeout=<DURATION>
|
||||
|
||||
After accepting connection failed, connection listener
|
||||
is disabled for a given amount of time. Specifying 0
|
||||
disables this feature.
|
||||
|
||||
Default: ``0``
|
||||
|
||||
|
||||
SSL/TLS
|
||||
~~~~~~~
|
||||
|
||||
.. option:: --ciphers=<SUITE>
|
||||
|
||||
Set allowed cipher list. The format of the string is
|
||||
described in OpenSSL ciphers(1).
|
||||
|
||||
.. option:: -k, --insecure
|
||||
|
||||
Don't verify backend server's certificate if :option:`-p`\,
|
||||
:option:`--client` or :option:`\--http2-bridge` are given and
|
||||
:option:`--backend-no-tls` is not given.
|
||||
|
||||
.. option:: --cacert=<PATH>
|
||||
|
||||
Set path to trusted CA certificate file if :option:`-p`\, :option:`--client`
|
||||
or :option:`--http2-bridge` are given and :option:`\--backend-no-tls` is not
|
||||
given. The file must be in PEM format. It can contain
|
||||
multiple certificates. If the linked OpenSSL is
|
||||
configured to load system wide certificates, they are
|
||||
loaded at startup regardless of this option.
|
||||
|
||||
.. option:: --private-key-passwd-file=<PATH>
|
||||
|
||||
Path to file that contains password for the server's
|
||||
private key. If none is given and the private key is
|
||||
password protected it'll be requested interactively.
|
||||
|
||||
.. option:: --subcert=<KEYPATH>:<CERTPATH>
|
||||
|
||||
Specify additional certificate and private key file.
|
||||
nghttpx will choose certificates based on the hostname
|
||||
indicated by client using TLS SNI extension. This
|
||||
option can be used multiple times. To make OCSP
|
||||
stapling work, <CERTPATH> must be absolute path.
|
||||
|
||||
.. option:: --backend-tls-sni-field=<HOST>
|
||||
|
||||
Explicitly set the content of the TLS SNI extension.
|
||||
This will default to the backend HOST name.
|
||||
|
||||
.. option:: --dh-param-file=<PATH>
|
||||
|
||||
Path to file that contains DH parameters in PEM format.
|
||||
Without this option, DHE cipher suites are not
|
||||
available.
|
||||
|
||||
.. option:: --npn-list=<LIST>
|
||||
|
||||
Comma delimited list of ALPN protocol identifier sorted
|
||||
in the order of preference. That means most desirable
|
||||
protocol comes first. This is used in both ALPN and
|
||||
NPN. The parameter must be delimited by a single comma
|
||||
only and any white spaces are treated as a part of
|
||||
protocol string.
|
||||
|
||||
Default: ``h2,h2-16,h2-14,spdy/3.1,http/1.1``
|
||||
|
||||
.. option:: --verify-client
|
||||
|
||||
Require and verify client certificate.
|
||||
|
||||
.. option:: --verify-client-cacert=<PATH>
|
||||
|
||||
Path to file that contains CA certificates to verify
|
||||
client certificate. The file must be in PEM format. It
|
||||
can contain multiple certificates.
|
||||
|
||||
.. option:: --client-private-key-file=<PATH>
|
||||
|
||||
Path to file that contains client private key used in
|
||||
backend client authentication.
|
||||
|
||||
.. option:: --client-cert-file=<PATH>
|
||||
|
||||
Path to file that contains client certificate used in
|
||||
backend client authentication.
|
||||
|
||||
.. option:: --tls-proto-list=<LIST>
|
||||
|
||||
Comma delimited list of SSL/TLS protocol to be enabled.
|
||||
The following protocols are available: TLSv1.2, TLSv1.1
|
||||
and TLSv1.0. The name matching is done in
|
||||
case-insensitive manner. The parameter must be
|
||||
delimited by a single comma only and any white spaces
|
||||
are treated as a part of protocol string.
|
||||
|
||||
Default: ``TLSv1.2,TLSv1.1``
|
||||
|
||||
.. option:: --tls-ticket-key-file=<PATH>
|
||||
|
||||
Path to file that contains 48 bytes random data to
|
||||
construct TLS session ticket parameters. This options
|
||||
can be used repeatedly to specify multiple ticket
|
||||
parameters. If several files are given, only the first
|
||||
key is used to encrypt TLS session tickets. Other keys
|
||||
are accepted but server will issue new session ticket
|
||||
with first key. This allows session key rotation.
|
||||
Please note that key rotation does not occur
|
||||
automatically. User should rearrange files or change
|
||||
options values and restart nghttpx gracefully. If
|
||||
opening or reading given file fails, all loaded keys are
|
||||
discarded and it is treated as if none of this option is
|
||||
given. If this option is not given or an error occurred
|
||||
while opening or reading a file, key is generated
|
||||
automatically and renewed every 12hrs. At most 2 keys
|
||||
are stored in memory.
|
||||
|
||||
.. option:: --fetch-ocsp-response-file=<PATH>
|
||||
|
||||
Path to fetch-ocsp-response script file. It should be
|
||||
absolute path.
|
||||
|
||||
Default: ``/usr/local/share/nghttp2/fetch-ocsp-response``
|
||||
|
||||
.. option:: --ocsp-update-interval=<DURATION>
|
||||
|
||||
Set interval to update OCSP response cache.
|
||||
|
||||
Default: ``4h``
|
||||
|
||||
.. option:: --no-ocsp
|
||||
|
||||
Disable OCSP stapling.
|
||||
|
||||
|
||||
HTTP/2 and SPDY
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. option:: -c, --http2-max-concurrent-streams=<N>
|
||||
|
||||
Set the maximum number of the concurrent streams in one
|
||||
HTTP/2 and SPDY session.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
.. option:: --frontend-http2-window-bits=<N>
|
||||
|
||||
Sets the per-stream initial window size of HTTP/2 SPDY
|
||||
frontend connection. For HTTP/2, the size is 2\*\*<N>-1.
|
||||
For SPDY, the size is 2\*\*<N>.
|
||||
|
||||
Default: ``16``
|
||||
|
||||
.. option:: --frontend-http2-connection-window-bits=<N>
|
||||
|
||||
Sets the per-connection window size of HTTP/2 and SPDY
|
||||
frontend connection. For HTTP/2, the size is
|
||||
2**<N>-1. For SPDY, the size is 2\*\*<N>.
|
||||
|
||||
Default: ``16``
|
||||
|
||||
.. option:: --frontend-no-tls
|
||||
|
||||
Disable SSL/TLS on frontend connections.
|
||||
|
||||
.. option:: --backend-http2-window-bits=<N>
|
||||
|
||||
Sets the initial window size of HTTP/2 backend
|
||||
connection to 2\*\*<N>-1.
|
||||
|
||||
Default: ``16``
|
||||
|
||||
.. option:: --backend-http2-connection-window-bits=<N>
|
||||
|
||||
Sets the per-connection window size of HTTP/2 backend
|
||||
connection to 2\*\*<N>-1.
|
||||
|
||||
Default: ``16``
|
||||
|
||||
.. option:: --backend-no-tls
|
||||
|
||||
Disable SSL/TLS on backend connections.
|
||||
|
||||
.. option:: --http2-no-cookie-crumbling
|
||||
|
||||
Don't crumble cookie header field.
|
||||
|
||||
.. option:: --padding=<N>
|
||||
|
||||
Add at most <N> bytes to a HTTP/2 frame payload as
|
||||
padding. Specify 0 to disable padding. This option is
|
||||
meant for debugging purpose and not intended to enhance
|
||||
protocol security.
|
||||
|
||||
.. option:: --no-server-push
|
||||
|
||||
Disable HTTP/2 server push. Server push is only
|
||||
supported by default mode and HTTP/2 frontend. SPDY
|
||||
frontend does not support server push.
|
||||
|
||||
|
||||
Mode
|
||||
~~~~
|
||||
|
||||
.. describe:: (default mode)
|
||||
|
||||
|
||||
Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. If
|
||||
:option:`--frontend-no-tls` is used, accept HTTP/2 and HTTP/1.1.
|
||||
The incoming HTTP/1.1 connection can be upgraded to
|
||||
HTTP/2 through HTTP Upgrade. The protocol to the
|
||||
backend is HTTP/1.1.
|
||||
|
||||
.. option:: -s, --http2-proxy
|
||||
|
||||
Like default mode, but enable secure proxy mode.
|
||||
|
||||
.. option:: --http2-bridge
|
||||
|
||||
Like default mode, but communicate with the backend in
|
||||
HTTP/2 over SSL/TLS. Thus the incoming all connections
|
||||
are converted to HTTP/2 connection and relayed to the
|
||||
backend. See :option:`--backend-http-proxy-uri` option if you are
|
||||
behind the proxy and want to connect to the outside
|
||||
HTTP/2 proxy.
|
||||
|
||||
.. option:: --client
|
||||
|
||||
Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The
|
||||
incoming HTTP/1.1 connection can be upgraded to HTTP/2
|
||||
connection through HTTP Upgrade. The protocol to the
|
||||
backend is HTTP/2. To use nghttpx as a forward proxy,
|
||||
use :option:`-p` option instead.
|
||||
|
||||
.. option:: -p, --client-proxy
|
||||
|
||||
Like :option:`--client` option, but it also requires the request
|
||||
path from frontend must be an absolute URI, suitable for
|
||||
use as a forward proxy.
|
||||
|
||||
|
||||
Logging
|
||||
~~~~~~~
|
||||
|
||||
.. option:: -L, --log-level=<LEVEL>
|
||||
|
||||
Set the severity level of log output. <LEVEL> must be
|
||||
one of INFO, NOTICE, WARN, ERROR and FATAL.
|
||||
|
||||
Default: ``NOTICE``
|
||||
|
||||
.. option:: --accesslog-file=<PATH>
|
||||
|
||||
Set path to write access log. To reopen file, send USR1
|
||||
signal to nghttpx.
|
||||
|
||||
.. option:: --accesslog-syslog
|
||||
|
||||
Send access log to syslog. If this option is used,
|
||||
:option:`--accesslog-file` option is ignored.
|
||||
|
||||
.. option:: --accesslog-format=<FORMAT>
|
||||
|
||||
Specify format string for access log. The default
|
||||
format is combined format. The following variables are
|
||||
available:
|
||||
|
||||
* $remote_addr: client IP address.
|
||||
* $time_local: local time in Common Log format.
|
||||
* $time_iso8601: local time in ISO 8601 format.
|
||||
* $request: HTTP request line.
|
||||
* $status: HTTP response status code.
|
||||
* $body_bytes_sent: the number of bytes sent to client
|
||||
as response body.
|
||||
* $http_<VAR>: value of HTTP request header <VAR> where
|
||||
'_' in <VAR> is replaced with '-'.
|
||||
* $remote_port: client port.
|
||||
* $server_port: server port.
|
||||
* $request_time: request processing time in seconds with
|
||||
milliseconds resolution.
|
||||
* $pid: PID of the running process.
|
||||
* $alpn: ALPN identifier of the protocol which generates
|
||||
the response. For HTTP/1, ALPN is always http/1.1,
|
||||
regardless of minor version.
|
||||
|
||||
|
||||
Default: ``$remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"``
|
||||
|
||||
.. option:: --errorlog-file=<PATH>
|
||||
|
||||
Set path to write error log. To reopen file, send USR1
|
||||
signal to nghttpx. stderr will be redirected to the
|
||||
error log file unless :option:`--errorlog-syslog` is used.
|
||||
|
||||
Default: ``/dev/stderr``
|
||||
|
||||
.. option:: --errorlog-syslog
|
||||
|
||||
Send error log to syslog. If this option is used,
|
||||
:option:`--errorlog-file` option is ignored.
|
||||
|
||||
.. option:: --syslog-facility=<FACILITY>
|
||||
|
||||
Set syslog facility to <FACILITY>.
|
||||
|
||||
Default: ``daemon``
|
||||
|
||||
|
||||
HTTP
|
||||
~~~~
|
||||
|
||||
.. option:: --add-x-forwarded-for
|
||||
|
||||
Append X-Forwarded-For header field to the downstream
|
||||
request.
|
||||
|
||||
.. option:: --strip-incoming-x-forwarded-for
|
||||
|
||||
Strip X-Forwarded-For header field from inbound client
|
||||
requests.
|
||||
|
||||
.. option:: --no-via
|
||||
|
||||
Don't append to Via header field. If Via header field
|
||||
is received, it is left unaltered.
|
||||
|
||||
.. option:: --no-location-rewrite
|
||||
|
||||
Don't rewrite location header field on :option:`--http2-bridge`\,
|
||||
:option:`--client` and default mode. For :option:`\--http2-proxy` and
|
||||
:option:`--client-proxy` mode, location header field will not be
|
||||
altered regardless of this option.
|
||||
|
||||
.. option:: --no-host-rewrite
|
||||
|
||||
Don't rewrite host and :authority header fields on
|
||||
:option:`--http2-bridge`\, :option:`--client` and default mode. For
|
||||
:option:`--http2-proxy` and :option:`\--client-proxy` mode, these headers
|
||||
will not be altered regardless of this option.
|
||||
|
||||
.. option:: --altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
|
||||
|
||||
Specify protocol ID, port, host and origin of
|
||||
alternative service. <HOST> and <ORIGIN> are optional.
|
||||
They are advertised in alt-svc header field or HTTP/2
|
||||
ALTSVC frame. This option can be used multiple times to
|
||||
specify multiple alternative services. Example:
|
||||
:option:`--altsvc`\=h2,443
|
||||
|
||||
.. option:: --add-response-header=<HEADER>
|
||||
|
||||
Specify additional header field to add to response
|
||||
header set. This option just appends header field and
|
||||
won't replace anything already set. This option can be
|
||||
used several times to specify multiple header fields.
|
||||
Example: :option:`--add-response-header`\="foo: bar"
|
||||
|
||||
.. option:: --header-field-buffer=<SIZE>
|
||||
|
||||
Set maximum buffer size for incoming HTTP header field
|
||||
list. This is the sum of header name and value in
|
||||
bytes.
|
||||
|
||||
Default: ``64K``
|
||||
|
||||
.. option:: --max-header-fields=<N>
|
||||
|
||||
Set maximum number of incoming HTTP header fields, which
|
||||
appear in one request or response header field list.
|
||||
|
||||
Default: ``100``
|
||||
|
||||
|
||||
Debug
|
||||
~~~~~
|
||||
|
||||
.. option:: --frontend-http2-dump-request-header=<PATH>
|
||||
|
||||
Dumps request headers received by HTTP/2 frontend to the
|
||||
file denoted in <PATH>. The output is done in HTTP/1
|
||||
header field format and each header block is followed by
|
||||
an empty line. This option is not thread safe and MUST
|
||||
NOT be used with option :option:`-n`\<N>, where <N> >= 2.
|
||||
|
||||
.. option:: --frontend-http2-dump-response-header=<PATH>
|
||||
|
||||
Dumps response headers sent from HTTP/2 frontend to the
|
||||
file denoted in <PATH>. The output is done in HTTP/1
|
||||
header field format and each header block is followed by
|
||||
an empty line. This option is not thread safe and MUST
|
||||
NOT be used with option :option:`-n`\<N>, where <N> >= 2.
|
||||
|
||||
.. option:: -o, --frontend-frame-debug
|
||||
|
||||
Print HTTP/2 frames in frontend to stderr. This option
|
||||
is not thread safe and MUST NOT be used with option
|
||||
:option:`-n`\=N, where N >= 2.
|
||||
|
||||
|
||||
Process
|
||||
~~~~~~~
|
||||
|
||||
.. option:: -D, --daemon
|
||||
|
||||
Run in a background. If :option:`-D` is used, the current working
|
||||
directory is changed to '*/*'.
|
||||
|
||||
.. option:: --pid-file=<PATH>
|
||||
|
||||
Set path to save PID of this program.
|
||||
|
||||
.. option:: --user=<USER>
|
||||
|
||||
Run this program as <USER>. This option is intended to
|
||||
be used to drop root privileges.
|
||||
|
||||
|
||||
Misc
|
||||
~~~~
|
||||
|
||||
.. option:: --conf=<PATH>
|
||||
|
||||
Load configuration from <PATH>.
|
||||
|
||||
Default: ``/etc/nghttpx/nghttpx.conf``
|
||||
|
||||
.. option:: -v, --version
|
||||
|
||||
Print version and exit.
|
||||
|
||||
.. option:: -h, --help
|
||||
|
||||
Print this help and exit.
|
||||
|
||||
|
||||
|
||||
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
||||
10 * 1024). Units are K, M and G (powers of 1024).
|
||||
|
||||
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
||||
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
||||
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
||||
is omitted, a second is used as unit.
|
||||
|
||||
FILES
|
||||
-----
|
||||
|
||||
*/etc/nghttpx/nghttpx.conf*
|
||||
The default configuration file path nghttpx searches at startup.
|
||||
The configuration file path can be changed using :option:`--conf`
|
||||
option.
|
||||
|
||||
Those lines which are staring ``#`` are treated as comment.
|
||||
|
||||
The option name in the configuration file is the long command-line
|
||||
option name with leading ``--`` stripped (e.g., ``frontend``). Put
|
||||
``=`` between option name and value. Don't put extra leading or
|
||||
trailing spaces.
|
||||
|
||||
The options which do not take argument in the command-line *take*
|
||||
argument in the configuration file. Specify ``yes`` as an argument
|
||||
(e.g., ``http2-proxy=yes``). If other string is given, it is
|
||||
ignored.
|
||||
|
||||
To specify private key and certificate file which are given as
|
||||
positional arguments in command-line, use ``private-key-file`` and
|
||||
``certificate-file``.
|
||||
|
||||
:option:`--conf` option cannot be used in the configuration file and
|
||||
will be ignored if specified.
|
||||
|
||||
SIGNALS
|
||||
-------
|
||||
|
||||
SIGQUIT
|
||||
Shutdown gracefully. First accept pending connections and stop
|
||||
accepting connection. After all connections are handled, nghttpx
|
||||
exits.
|
||||
|
||||
SIGUSR1
|
||||
Reopen log files.
|
||||
|
||||
SIGUSR2
|
||||
Fork and execute nghttpx. It will execute the binary in the same
|
||||
path with same command-line arguments and environment variables.
|
||||
After new process comes up, sending SIGQUIT to the original process
|
||||
to perform hot swapping.
|
||||
|
||||
SERVER PUSH
|
||||
-----------
|
||||
|
||||
nghttpx supports HTTP/2 server push in default mode. nghttpx looks
|
||||
for Link header field (`RFC 5988
|
||||
<http://tools.ietf.org/html/rfc5988>`_) in response headers from
|
||||
backend server and extracts URI-reference with parameter
|
||||
``rel=preload`` (see `preload
|
||||
<http://w3c.github.io/preload/#interoperability-with-http-link-header>`_)
|
||||
and pushes those URIs to the frontend client. Here is a sample Link
|
||||
header field to initiate server push:
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
Link: </fonts/font.woff>; rel=preload
|
||||
Link: </css/theme.css>; rel=preload
|
||||
|
||||
Currently, the following restrictions are applied for server push:
|
||||
|
||||
1. URI-reference must not contain authority. If it exists, it is not
|
||||
pushed. ``/fonts/font.woff`` and ``css/theme.css`` are eligible to
|
||||
be pushed. ``https://example.org/fonts/font.woff`` and
|
||||
``//example.org/css/theme.css`` are not.
|
||||
|
||||
2. The associated stream must have method "GET" or "POST". The
|
||||
associated stream's status code must be 200.
|
||||
|
||||
These limitations may be loosened in the future release.
|
||||
|
||||
UNIX DOMAIN SOCKET
|
||||
------------------
|
||||
|
||||
nghttpx supports UNIX domain socket with a filename for both frontend
|
||||
and backend connections.
|
||||
|
||||
Please note that current nghttpx implementation does not delete a
|
||||
socket with a filename. And on start up, if nghttpx detects that the
|
||||
specified socket already exists in the file system, nghttpx first
|
||||
deletes it. However, if SIGUSR2 is used to execute new binary and
|
||||
both old and new configurations use same filename, new binary does not
|
||||
delete the socket and continues to use it.
|
||||
|
||||
OCSP STAPLING
|
||||
-------------
|
||||
|
||||
OCSP query is done using external perl script ``fetch-ocsp-response``,
|
||||
which has been developed as part of h2o project
|
||||
(https://github.com/h2o/h2o).
|
||||
|
||||
The script file is usually installed under
|
||||
``$(prefix)/share/nghttp2/`` directory. The actual path to script can
|
||||
be customized using :option:`--fetch-ocsp-response-file` option.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`h2load(1)`
|
||||
101
doc/nghttpx.h2r
101
doc/nghttpx.h2r
@@ -1,101 +0,0 @@
|
||||
FILES
|
||||
-----
|
||||
|
||||
*/etc/nghttpx/nghttpx.conf*
|
||||
The default configuration file path nghttpx searches at startup.
|
||||
The configuration file path can be changed using :option:`--conf`
|
||||
option.
|
||||
|
||||
Those lines which are staring ``#`` are treated as comment.
|
||||
|
||||
The option name in the configuration file is the long command-line
|
||||
option name with leading ``--`` stripped (e.g., ``frontend``). Put
|
||||
``=`` between option name and value. Don't put extra leading or
|
||||
trailing spaces.
|
||||
|
||||
The options which do not take argument in the command-line *take*
|
||||
argument in the configuration file. Specify ``yes`` as an argument
|
||||
(e.g., ``http2-proxy=yes``). If other string is given, it is
|
||||
ignored.
|
||||
|
||||
To specify private key and certificate file which are given as
|
||||
positional arguments in command-line, use ``private-key-file`` and
|
||||
``certificate-file``.
|
||||
|
||||
:option:`--conf` option cannot be used in the configuration file and
|
||||
will be ignored if specified.
|
||||
|
||||
SIGNALS
|
||||
-------
|
||||
|
||||
SIGQUIT
|
||||
Shutdown gracefully. First accept pending connections and stop
|
||||
accepting connection. After all connections are handled, nghttpx
|
||||
exits.
|
||||
|
||||
SIGUSR1
|
||||
Reopen log files.
|
||||
|
||||
SIGUSR2
|
||||
Fork and execute nghttpx. It will execute the binary in the same
|
||||
path with same command-line arguments and environment variables.
|
||||
After new process comes up, sending SIGQUIT to the original process
|
||||
to perform hot swapping.
|
||||
|
||||
SERVER PUSH
|
||||
-----------
|
||||
|
||||
nghttpx supports HTTP/2 server push in default mode. nghttpx looks
|
||||
for Link header field (`RFC 5988
|
||||
<http://tools.ietf.org/html/rfc5988>`_) in response headers from
|
||||
backend server and extracts URI-reference with parameter
|
||||
``rel=preload`` (see `preload
|
||||
<http://w3c.github.io/preload/#interoperability-with-http-link-header>`_)
|
||||
and pushes those URIs to the frontend client. Here is a sample Link
|
||||
header field to initiate server push:
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
Link: </fonts/font.woff>; rel=preload
|
||||
Link: </css/theme.css>; rel=preload
|
||||
|
||||
Currently, the following restrictions are applied for server push:
|
||||
|
||||
1. URI-reference must not contain authority. If it exists, it is not
|
||||
pushed. ``/fonts/font.woff`` and ``css/theme.css`` are eligible to
|
||||
be pushed. ``https://example.org/fonts/font.woff`` and
|
||||
``//example.org/css/theme.css`` are not.
|
||||
|
||||
2. The associated stream must have method "GET" or "POST". The
|
||||
associated stream's status code must be 200.
|
||||
|
||||
These limitations may be loosened in the future release.
|
||||
|
||||
UNIX DOMAIN SOCKET
|
||||
------------------
|
||||
|
||||
nghttpx supports UNIX domain socket with a filename for both frontend
|
||||
and backend connections.
|
||||
|
||||
Please note that current nghttpx implementation does not delete a
|
||||
socket with a filename. And on start up, if nghttpx detects that the
|
||||
specified socket already exists in the file system, nghttpx first
|
||||
deletes it. However, if SIGUSR2 is used to execute new binary and
|
||||
both old and new configurations use same filename, new binary does not
|
||||
delete the socket and continues to use it.
|
||||
|
||||
OCSP STAPLING
|
||||
-------------
|
||||
|
||||
OCSP query is done using external perl script ``fetch-ocsp-response``,
|
||||
which has been developed as part of h2o project
|
||||
(https://github.com/h2o/h2o).
|
||||
|
||||
The script file is usually installed under
|
||||
``$(prefix)/share/nghttp2/`` directory. The actual path to script can
|
||||
be customized using :option:`--fetch-ocsp-response-file` option.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
:manpage:`nghttp(1)`, :manpage:`nghttpd(1)`, :manpage:`h2load(1)`
|
||||
@@ -1,107 +0,0 @@
|
||||
Programmers' Guide
|
||||
==================
|
||||
|
||||
Includes
|
||||
--------
|
||||
|
||||
To use the public APIs, include ``nghttp2/nghttp2.h``::
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
The header files are also available online: :doc:`nghttp2.h` and
|
||||
:doc:`nghttp2ver.h`.
|
||||
|
||||
Remarks
|
||||
-------
|
||||
|
||||
Do not call `nghttp2_session_send()`, `nghttp2_session_mem_send()`,
|
||||
`nghttp2_session_recv()` or `nghttp2_session_mem_recv()` from the
|
||||
nghttp2 callback functions directly or indirectly. It will lead to the
|
||||
crash. You can submit requests or frames in the callbacks then call
|
||||
these functions outside the callbacks.
|
||||
|
||||
Currently, `nghttp2_session_send()` and `nghttp2_session_mem_send()`
|
||||
do not send client connection preface
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`). The applications are
|
||||
responsible to send it before sending any HTTP/2 frames using these
|
||||
functions if :type:`nghttp2_session` is configured as client.
|
||||
Similarly, `nghttp2_session_recv()` and `nghttp2_session_mem_recv()`
|
||||
do not consume client connection preface unless
|
||||
`nghttp2_option_set_recv_client_preface()` is used with nonzero option
|
||||
value. The applications are responsible to receive it before calling
|
||||
these functions if :type:`nghttp2_session` is configured as server and
|
||||
`nghttp2_option_set_recv_client_preface()` is not used.
|
||||
|
||||
.. _http-messaging:
|
||||
|
||||
HTTP Messaging
|
||||
--------------
|
||||
|
||||
By default, nghttp2 library checks HTTP messaging rules described in
|
||||
`HTTP/2 specification, section 8
|
||||
<https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8>`_.
|
||||
Everything described in that section is not validated however. We
|
||||
briefly describe what the library does in this area. In the following
|
||||
description, without loss of generality we omit CONTINUATION frame
|
||||
since they must follow HEADERS frame and are processed atomically. In
|
||||
other words, they are just one big HEADERS frame. To disable these
|
||||
validations, use `nghttp2_option_set_no_http_messaging()`.
|
||||
|
||||
For HTTP request, including those carried by PUSH_PROMISE, HTTP
|
||||
message starts with one HEADERS frame containing request headers. It
|
||||
is followed by zero or more DATA frames containing request body, which
|
||||
is followed by zero or one HEADERS containing trailer headers. The
|
||||
request headers must include ":scheme", ":method" and ":path" pseudo
|
||||
header fields unless ":method" is not "CONNECT". ":authority" is
|
||||
optional, but nghttp2 requires either ":authority" or "Host" header
|
||||
field must be present. If ":method" is "CONNECT", the request headers
|
||||
must include ":method" and ":authority" and must omit ":scheme" and
|
||||
":path".
|
||||
|
||||
For HTTP response, HTTP message starts with zero or more HEADERS
|
||||
frames containing non-final response (status code 1xx). They are
|
||||
followed by one HEADERS frame containing final response headers
|
||||
(non-1xx). It is followed by zero or more DATA frames containing
|
||||
response body, which is followed by zero or one HEADERS containing
|
||||
trailer headers. The non-final and final response headers must
|
||||
contain ":status" pseudo header field containing 3 digits only.
|
||||
|
||||
All request and response headers must include exactly one valid value
|
||||
for each pseudo header field. Additionally nghttp2 requires all
|
||||
request headers must not include more than one "Host" header field.
|
||||
|
||||
HTTP/2 prohibits connection-specific header fields. The following
|
||||
header fields must not appear: "Connection", "Keep-Alive",
|
||||
"Proxy-Connection", "Transfer-Encoding" and "Upgrade". Additionally,
|
||||
"TE" header field must not include any value other than "trailers".
|
||||
|
||||
Each header field name and value must obey the field-name and
|
||||
field-value production rules described in `RFC 7230, section
|
||||
3.2. <https://tools.ietf.org/html/rfc7230#section-3.2>`_.
|
||||
Additionally, all field name must be lower cased. While the pseudo
|
||||
header fields must satisfy these rules, we just ignore illegal regular
|
||||
headers (this means that these header fields are not passed to
|
||||
application callback). This is because these illegal header fields
|
||||
are floating around in existing internet and resetting stream just
|
||||
because of this may break many web sites. This is especially true if
|
||||
we forward to or translate from HTTP/1 traffic.
|
||||
|
||||
For "http" or "https" URIs, ":path" pseudo header fields must start
|
||||
with "/". The only exception is OPTIONS request, in that case, "*" is
|
||||
allowed in ":path" pseudo header field to represent system-wide
|
||||
OPTIONS request.
|
||||
|
||||
With the above validations, nghttp2 library guarantees that header
|
||||
field name passed to `nghttp2_on_header_callback()` is not empty.
|
||||
Also required pseudo headers are all present and not empty.
|
||||
|
||||
nghttp2 enforces "Content-Length" validation as well. All request or
|
||||
response headers must not contain more than one "Content-Length"
|
||||
header field. If "Content-Length" header field is present, it must be
|
||||
parsed as 64 bit signed integer. The sum of data length in the
|
||||
following DATA frames must match with the number in "Content-Length"
|
||||
header field if it is present (this does not include padding bytes).
|
||||
|
||||
Any deviation results in stream error of type PROTOCOL_ERROR. If
|
||||
error is found in PUSH_PROMISE frame, stream error is raised against
|
||||
promised stream.
|
||||
@@ -1 +0,0 @@
|
||||
.. include:: @top_srcdir@/doc/sources/python-apiref.rst
|
||||
@@ -1,135 +0,0 @@
|
||||
Building Android binary
|
||||
=======================
|
||||
|
||||
In this article, we briefly describe how to build Android binary using
|
||||
`Android NDK <http://developer.android.com/tools/sdk/ndk/index.html>`_
|
||||
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`` and ``android-make`` scripts to make the
|
||||
build easier. To make these script work, NDK toolchain must be
|
||||
installed in the following way. First, let us introduce
|
||||
``ANDROID_HOME`` environment variable. We need to install toolchain
|
||||
under ``$ANDROID_HOME/toolchain``. An user can freely choose the path
|
||||
for ``ANDROID_HOME``. For example, to install toolchain under
|
||||
``$ANDROID_HOME/toolchain``, do this in the the directory where NDK is
|
||||
unpacked::
|
||||
|
||||
$ build/tools/make-standalone-toolchain.sh \
|
||||
--install-dir=$ANDROID_HOME/toolchain \
|
||||
--toolchain=arm-linux-androideabi-4.9 \
|
||||
--llvm-version=3.5 \
|
||||
--platform=android-16
|
||||
|
||||
The additional flag ``--system=linux-x86_64`` may be required if you
|
||||
are using x86_64 system.
|
||||
|
||||
The platform level is not important here because we don't use Android
|
||||
specific C/C++ API.
|
||||
|
||||
The dependent libraries, such as OpenSSL and libev should be built
|
||||
with the toolchain and installed under ``$ANDROID_HOME/usr/local``.
|
||||
We recommend to build these libraries as static library to make the
|
||||
deployment easier. libxml2 support is currently disabled.
|
||||
|
||||
We use zlib which comes with Android NDK, so we don't have to build it
|
||||
by ourselves.
|
||||
|
||||
If SPDY support is required for nghttpx and h2load, build and install
|
||||
spdylay as well.
|
||||
|
||||
Before running ``android-config`` and ``android-make``,
|
||||
``ANDROID_HOME`` environment variable must be set to point to the
|
||||
correct path. Also add ``$ANDROID_HOME/toolchain/bin`` to ``PATH``::
|
||||
|
||||
$ export PATH=$PATH:$ANDROID_HOME/toolchain/bin
|
||||
|
||||
To configure OpenSSL, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
if [ -z "$ANDROID_HOME" ]; then
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
exit 1
|
||||
fi
|
||||
PREFIX=$ANDROID_HOME/usr/local
|
||||
TOOLCHAIN=$ANDROID_HOME/toolchain
|
||||
PATH=$TOOLCHAIN/bin:$PATH
|
||||
|
||||
export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi-
|
||||
./Configure --prefix=$PREFIX android
|
||||
|
||||
And run ``make install`` to build and install.
|
||||
|
||||
We cannot compile libev without modification. Apply `this patch
|
||||
<https://gist.github.com/tatsuhiro-t/48c45f08950f587180ed>`_ before
|
||||
configuring libev. This patch is for libev-4.19. After applying the
|
||||
patch, to configure libev, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
if [ -z "$ANDROID_HOME" ]; then
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
exit 1
|
||||
fi
|
||||
PREFIX=$ANDROID_HOME/usr/local
|
||||
TOOLCHAIN=$ANDROID_HOME/toolchain
|
||||
PATH=$TOOLCHAIN/bin:$PATH
|
||||
|
||||
./configure \
|
||||
--host=arm-linux-androideabi \
|
||||
--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 spdylay, use the following script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
if [ -z "$ANDROID_HOME" ]; then
|
||||
echo 'No $ANDROID_HOME specified.'
|
||||
exit 1
|
||||
fi
|
||||
PREFIX=$ANDROID_HOME/usr/local
|
||||
TOOLCHAIN=$ANDROID_HOME/toolchain
|
||||
PATH=$TOOLCHAIN/bin:$PATH
|
||||
|
||||
./configure \
|
||||
--disable-shared \
|
||||
--host=arm-linux-androideabi \
|
||||
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
|
||||
--prefix=$PREFIX \
|
||||
--without-libxml2 \
|
||||
--disable-src \
|
||||
--disable-examples \
|
||||
CPPFLAGS="-I$PREFIX/include" \
|
||||
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
|
||||
LDFLAGS="-L$PREFIX/lib"
|
||||
|
||||
And run ``make install`` to build and install. After spdylay
|
||||
installation, edit $ANDROID_HOME/usr/local/lib/pkgconfig/libspdylay.pc
|
||||
and remove the following line::
|
||||
|
||||
Requires.private: zlib
|
||||
|
||||
After prerequisite libraries are prepared, run ``android-config`` and
|
||||
then ``android-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::
|
||||
|
||||
$ arm-linux-androideabi-strip src/nghttpx
|
||||
@@ -1,57 +0,0 @@
|
||||
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 which comes with
|
||||
clang-3.5.
|
||||
|
||||
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-3.5 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://llvm.org/svn/llvm-project/cfe/trunk/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.
|
||||
@@ -1,91 +0,0 @@
|
||||
h2load - HTTP/2 benchmarking tool - HOW-TO
|
||||
==========================================
|
||||
|
||||
h2load is benchmarking tool for HTTP/2. If built with
|
||||
spdylay (http://tatsuhiro-t.github.io/spdylay/) library, it also
|
||||
supports SPDY protocol. It supports SSL/TLS and clear text for both
|
||||
HTTP/2 and SPDY.
|
||||
|
||||
Basic Usage
|
||||
-----------
|
||||
|
||||
In order to set benchmark settings, specify following 3 options.
|
||||
|
||||
``-n``
|
||||
The number of total requests. Default: 1
|
||||
|
||||
``-c``
|
||||
The number of concurrent clients. Default: 1
|
||||
|
||||
``-m``
|
||||
The max concurrent streams to issue per client.
|
||||
If ``auto`` is given, the number of given URIs is used.
|
||||
Default: ``auto``
|
||||
|
||||
Here is a command-line to perform benchmark to URI \https://localhost
|
||||
using total 100000 requests, 100 concurrent clients and 10 max
|
||||
concurrent streams::
|
||||
|
||||
$ h2load -n100000 -c100 -m10 https://localhost
|
||||
|
||||
The benchmarking result looks like this::
|
||||
|
||||
finished in 0 sec, 385 millisec and 851 microsec, 2591 req/s, 1689 kbytes/s
|
||||
requests: 1000 total, 1000 started, 1000 done, 1000 succeeded, 0 failed, 0 errored
|
||||
status codes: 1000 2xx, 0 3xx, 0 4xx, 0 5xx
|
||||
traffic: 667500 bytes total, 28700 bytes headers, 612000 bytes data
|
||||
|
||||
The number of ``failed`` is the number of requests returned with non
|
||||
2xx status. The number of ``error`` is the number of ``failed`` plus
|
||||
the number of requests which failed with connection error.
|
||||
|
||||
The number of ``total`` in ``traffic`` is the received application
|
||||
data. If SSL/TLS is used, this number is calculated after decryption.
|
||||
The number of ``headers`` is the sum of payload size of response
|
||||
HEADERS (or SYN_REPLY for SPDY). This number comes before
|
||||
decompressing header block. The number of ``data`` is the sum of
|
||||
response body.
|
||||
|
||||
Flow Control
|
||||
------------
|
||||
|
||||
HTTP/2 and SPDY/3 or later employ 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:
|
||||
|
||||
``-w``
|
||||
Sets the stream level initial window size to
|
||||
(2**<N>)-1. For SPDY, 2**<N> is used instead.
|
||||
|
||||
``-W``
|
||||
Sets the connection level initial window size to
|
||||
(2**<N>)-1. For SPDY, if <N> is strictly less
|
||||
than 16, this option is ignored. Otherwise
|
||||
2**<N> is used for SPDY.
|
||||
|
||||
Multi-Threading
|
||||
---------------
|
||||
|
||||
Sometimes benchmarking client itself becomes a bottleneck. To remedy
|
||||
this situation, use ``-t`` option to specify the number of native
|
||||
thread to use.
|
||||
|
||||
``-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 ``-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.
|
||||
@@ -3,11 +3,11 @@
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
nghttp2 - HTTP/2 C Library
|
||||
nghttp2 - HTTP/2.0 C Library
|
||||
============================
|
||||
|
||||
This is an experimental implementation of Hypertext Transfer Protocol
|
||||
version 2.
|
||||
version 2.0.
|
||||
|
||||
The project is hosted at `github.com/tatsuhiro-t/nghttp2 <https://github.com/tatsuhiro-t/nghttp2>`_.
|
||||
|
||||
@@ -17,37 +17,24 @@ Contents:
|
||||
:maxdepth: 2
|
||||
|
||||
package_README
|
||||
contribute
|
||||
building-android-binary
|
||||
tutorial-client
|
||||
tutorial-server
|
||||
tutorial-hpack
|
||||
nghttp.1
|
||||
nghttpd.1
|
||||
nghttpx.1
|
||||
h2load.1
|
||||
nghttpx-howto
|
||||
h2load-howto
|
||||
programmers-guide
|
||||
apiref
|
||||
libnghttp2_asio
|
||||
python-apiref
|
||||
nghttp2.h
|
||||
nghttp2ver.h
|
||||
asio_http2_server.h
|
||||
asio_http2_client.h
|
||||
asio_http2.h
|
||||
Source <https://github.com/tatsuhiro-t/nghttp2>
|
||||
Issues <https://github.com/tatsuhiro-t/nghttp2/issues>
|
||||
nghttp2.org <https://nghttp2.org/>
|
||||
|
||||
Released Versions
|
||||
=================
|
||||
|
||||
https://github.com/tatsuhiro-t/nghttp2/releases
|
||||
* `v0.3.0 <released-versions/v0.3.0/>`_ `(Download v0.3.0) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.3.0>`_
|
||||
* `v0.2.0 <released-versions/v0.2.0/>`_ `(Download v0.2.0) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.2.0>`_
|
||||
* `v0.1.0 <released-versions/v0.1.0/>`_ `(Download v0.1.0) <https://github.com/tatsuhiro-t/nghttp2/releases/tag/v0.1.0>`_
|
||||
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-http2-14
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-http2-09
|
||||
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05
|
||||
|
||||
@@ -1,433 +0,0 @@
|
||||
libnghttp2_asio: High level HTTP/2 C++ library
|
||||
==============================================
|
||||
|
||||
libnghttp2_asio is C++ library built on top of libnghttp2 and provides
|
||||
high level abstraction API to build HTTP/2 applications. It depends
|
||||
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
|
||||
provides server and client side API.
|
||||
|
||||
libnghttp2_asio is not built by default. Use ``--enable-asio-lib``
|
||||
configure flag to build libnghttp2_asio. The required Boost libraries
|
||||
are:
|
||||
|
||||
* Boost::Asio
|
||||
* Boost::System
|
||||
* Boost::Thread
|
||||
|
||||
We have 3 header files for this library:
|
||||
|
||||
* :doc:`asio_http2_server.h`
|
||||
* :doc:`asio_http2_client.h`
|
||||
* :doc:`asio_http2.h`
|
||||
|
||||
asio_http2.h is included from the other two files.
|
||||
|
||||
To build a program with libnghttp2_asio, link to the following
|
||||
libraries::
|
||||
|
||||
-lnghttp2_asio -lboost_system
|
||||
|
||||
If ``boost::asio::ssl`` is used in application code, OpenSSL is also
|
||||
required in link line::
|
||||
|
||||
-lnghttp2_asio -lboost_system -lssl -lcrypto
|
||||
|
||||
Server API
|
||||
----------
|
||||
|
||||
To use server API, first include following header file:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
Also take a look at that header file :doc:`asio_http2_server.h`.
|
||||
|
||||
Server API is designed to build HTTP/2 server very easily to utilize
|
||||
C++11 anonymous function and closure. The bare minimum example of
|
||||
HTTP/2 server looks like this:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
boost::system::error_code ec;
|
||||
http2 server;
|
||||
|
||||
server.handle("/", [](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
res.end("hello, world\n");
|
||||
});
|
||||
|
||||
if (server.listen_and_serve(ec, "localhost", "3000")) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
First we instantiate ``nghttp2::asio_http2::server::http2`` object.
|
||||
``nghttp2::asio_http2::server::http2::handle`` function registers
|
||||
pattern and its handler function. In this example, we register "/" as
|
||||
pattern, which matches all requests. Then call
|
||||
``nghttp2::asio_http2::server::http2::listen_and_serve`` function with
|
||||
address and port to listen to.
|
||||
|
||||
The ``req`` and ``res`` represent HTTP request and response
|
||||
respectively. ``nghttp2::asio_http2_::server::response::write_head``
|
||||
constructs HTTP response header fields. The first argument is HTTP
|
||||
status code, in the above example, which is 200. The second argument,
|
||||
which is omitted in the above example, is additional header fields to
|
||||
send.
|
||||
|
||||
``nghttp2::asio_http2::server::response::end`` sends response body.
|
||||
In the above example, we send string "hello, world".
|
||||
|
||||
The life time of req and res object ends after the callback set by
|
||||
``nghttp2::asio_http2::server::response::on_close`` function.
|
||||
Application must not use those objects after this call.
|
||||
|
||||
Serving static files and enabling SSL/TLS
|
||||
+++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
In this example, we serve a couple of static files and also enable
|
||||
SSL/TLS.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
boost::system::error_code ec;
|
||||
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||
|
||||
tls.use_private_key_file("server.key", boost::asio::ssl::context::pem);
|
||||
tls.use_certificate_chain_file("server.crt");
|
||||
|
||||
configure_tls_context_easy(ec, tls);
|
||||
|
||||
http2 server;
|
||||
|
||||
server.handle("/index.html", [](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
res.end(file_generator("index.html"));
|
||||
});
|
||||
|
||||
if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
We first create ``boost::asio::ssl::context`` object and set path to
|
||||
private key file and certificate file.
|
||||
``nghttp2::asio_http2::server::configure_tls_context_easy`` function
|
||||
configures SSL/TLS context object for HTTP/2 server use, including NPN
|
||||
callbacks.
|
||||
|
||||
In the above example, if request path is "/index.html", we serve
|
||||
index.html file in the current working directory.
|
||||
``nghttp2::asio_http2::server::response::end`` has overload to take
|
||||
function of type ``nghttp2::asio_http2::generator_cb`` and application
|
||||
pass its implementation to generate response body. For the
|
||||
convenience, libnghttp2_asio library provides
|
||||
``nghttp2::asio_http2::file_generator`` function to generate function
|
||||
to server static file. If other resource is requested, server
|
||||
automatically responds with 404 status code.
|
||||
|
||||
Server push
|
||||
+++++++++++
|
||||
|
||||
Server push is also supported.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
boost::system::error_code ec;
|
||||
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||
|
||||
tls.use_private_key_file("server.key", boost::asio::ssl::context::pem);
|
||||
tls.use_certificate_chain_file("server.crt");
|
||||
|
||||
configure_tls_context_easy(ec, tls);
|
||||
|
||||
http2 server;
|
||||
|
||||
std::string style_css = "h1 { color: green; }";
|
||||
|
||||
server.handle("/", [&style_css](const request &req, const response &res) {
|
||||
boost::system::error_code ec;
|
||||
auto push = res.push(ec, "GET", "/style.css");
|
||||
push->write_head(200);
|
||||
push->end(style_css);
|
||||
|
||||
res.write_head(200);
|
||||
res.end(R"(
|
||||
<!DOCTYPE html><html lang="en">
|
||||
<title>HTTP/2 FTW</title><body>
|
||||
<link href="/style.css" rel="stylesheet" type="text/css">
|
||||
<h1>This should be green</h1>
|
||||
</body></html>
|
||||
)");
|
||||
});
|
||||
|
||||
server.handle("/style.css",
|
||||
[&style_css](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
res.end(style_css);
|
||||
});
|
||||
|
||||
if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
When client requested any resource other than "/style.css", we push
|
||||
"/style.css". To push resource, call
|
||||
``nghttp2::asio_http2::server::response::push`` function with desired
|
||||
method and path. It returns another response object and use its
|
||||
functions to send push response.
|
||||
|
||||
Enable multi-threading
|
||||
++++++++++++++++++++++
|
||||
|
||||
Enabling multi-threading is very easy. Just call
|
||||
``nghttp2::asio_http2::server::http2::num_threads`` function with the
|
||||
desired number of threads:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
http2 server;
|
||||
|
||||
// Use 4 native threads
|
||||
server.num_threads(4);
|
||||
|
||||
Client API
|
||||
----------
|
||||
|
||||
To use client API, first include following header file:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
Also take a look at that header file :doc:`asio_http2_client.h`.
|
||||
|
||||
Here is the sample client code to access HTTP/2 server and print out
|
||||
response header fields and response body to the console screen:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::client;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
boost::system::error_code ec;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
// connect to localhost:3000
|
||||
session sess(io_service, "localhost", "3000");
|
||||
|
||||
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||
boost::system::error_code ec;
|
||||
|
||||
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||
|
||||
req->on_response([](const response &res) {
|
||||
// print status code and response header fields.
|
||||
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
|
||||
for (auto &kv : res.header()) {
|
||||
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
|
||||
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
|
||||
req->on_close([&sess](uint32_t error_code) {
|
||||
// shutdown session after first request was done.
|
||||
sess.shutdown();
|
||||
});
|
||||
});
|
||||
|
||||
sess.on_error([](const boost::system::error_code &ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
}
|
||||
|
||||
``nghttp2::asio_http2::client::session`` object takes
|
||||
``boost::asio::io_service`` object and remote server address. When
|
||||
connection is made, the callback function passed to
|
||||
``nghttp2::asio_http2::client::on_connect`` is invoked with connected
|
||||
address as its parameter. After this callback call, use
|
||||
``nghttp2::asio_http2::session::submit`` to send request to the
|
||||
server. You can submit multiple requests at once without waiting for
|
||||
the completion of previous request.
|
||||
|
||||
The life time of req and res object ends after the callback set by
|
||||
``nghttp2::asio_http2::server::request::on_close`` function.
|
||||
Application must not use those objects after this call.
|
||||
|
||||
Normally, client does not stop even after all requests are done unless
|
||||
connection is lost. To stop client, call
|
||||
``nghttp2::asio_http2::server::session::shutdown()``.
|
||||
|
||||
Recieve server push and enable SSL/TLS
|
||||
++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::client;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
boost::system::error_code ec;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||
tls.set_default_verify_paths();
|
||||
// disabled to make development easier...
|
||||
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||
configure_tls_context(ec, tls);
|
||||
|
||||
// connect to localhost:3000
|
||||
session sess(io_service, tls, "localhost", "3000");
|
||||
|
||||
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||
boost::system::error_code ec;
|
||||
|
||||
auto req = sess.submit(ec, "GET", "http://localhost:3000/");
|
||||
|
||||
req->on_response([&sess](const response &res) {
|
||||
std::cerr << "response received!" << std::endl;
|
||||
res.on_data([&sess](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
|
||||
req->on_push([](const request &push) {
|
||||
std::cerr << "push request received!" << std::endl;
|
||||
push.on_response([](const response &res) {
|
||||
std::cerr << "push response received!" << std::endl;
|
||||
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
sess.on_error([](const boost::system::error_code &ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
}
|
||||
|
||||
The above sample code demonstrates how to enable SSL/TLS and receive
|
||||
server push. Currently,
|
||||
``nghttp2::asio_http2::client::configure_tls_context`` function setups
|
||||
NPN callbacks for SSL/TLS context for HTTP/2 use.
|
||||
|
||||
To receive server push, use
|
||||
``nghttp2::asio_http2::client::request::on_push`` function to set
|
||||
callback function which is invoked when server push request is
|
||||
arrived. The callback function takes
|
||||
``nghttp2::asio_http2::client::request`` object, which contains the
|
||||
pushed request. To get server push response, set callback using
|
||||
``nghttp2::asio_http2::client::request::on_response``.
|
||||
|
||||
As stated in the previous section, client does not stop automatically
|
||||
as long as HTTP/2 session is fine and connection is alive. We don't
|
||||
call ``nghttp2::asio_http2::client::session::shutdown`` in this
|
||||
example, so the program does not terminate after all responses are
|
||||
received. Hit Ctrl-C to terminate the program.
|
||||
|
||||
Multiple concurrent requests
|
||||
++++++++++++++++++++++++++++
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::client;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
boost::system::error_code ec;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
// connect to localhost:3000
|
||||
session sess(io_service, "localhost", "3000");
|
||||
|
||||
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||
boost::system::error_code ec;
|
||||
|
||||
auto printer = [](const response &res) {
|
||||
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
};
|
||||
|
||||
std::size_t num = 3;
|
||||
auto count = std::make_shared<int>(num);
|
||||
|
||||
for (std::size_t i = 0; i < num; ++i) {
|
||||
auto req = sess.submit(ec, "GET",
|
||||
"http://localhost:3000/" + std::to_string(i + 1));
|
||||
|
||||
req->on_response(printer);
|
||||
req->on_close([&sess, count](uint32_t error_code) {
|
||||
if (--*count == 0) {
|
||||
// shutdown session after |num| requests were done.
|
||||
sess.shutdown();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
sess.on_error([](const boost::system::error_code &ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
}
|
||||
|
||||
Here is the sample to send 3 requests at once. Depending on the
|
||||
server settings, these requests are processed out-of-order. In this
|
||||
example, we have a trick to shutdown session after all requests were
|
||||
done. We made ``count`` object which is shared pointer to int and is
|
||||
initialized to 3. On each request closure (the invocation of the
|
||||
callback set by ``nghttp2::asio_http2::client::request::on_close``),
|
||||
we decrement the count. If count becomes 0, we are sure that all
|
||||
requests have been done and initiate shutdown.
|
||||
@@ -1,303 +0,0 @@
|
||||
nghttpx - HTTP/2 proxy - HOW-TO
|
||||
===============================
|
||||
|
||||
nghttpx is a proxy translating protocols between HTTP/2 and other
|
||||
protocols (e.g., HTTP/1, SPDY). 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 any ``-s``, ``-p`` and ``--client``, it
|
||||
operates in default mode. In this mode, nghttpx frontend listens for
|
||||
HTTP/2 requests and translates them to HTTP/1 requests. Thus it works
|
||||
as reverse proxy (gateway) for HTTP/2 clients to HTTP/1 web server.
|
||||
HTTP/1 requests are also supported in frontend as a fallback. If
|
||||
nghttpx is linked with spdylay library and frontend connection is
|
||||
SSL/TLS, the frontend also supports SPDY protocol.
|
||||
|
||||
By default, this mode's 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 is done via ALPN or NPN.
|
||||
|
||||
With ``--frontend-no-tls`` option, user can turn off SSL/TLS in
|
||||
frontend connection. In this case, SPDY protocol is not available
|
||||
even if spdylay library is liked to nghttpx. HTTP/2 and HTTP/1 are
|
||||
available on the frontend and a 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.
|
||||
|
||||
The backend is supposed to be HTTP/1 Web server. For example, to make
|
||||
nghttpx listen to encrypted HTTP/2 requests at port 8443, and a
|
||||
backend HTTP/1 web server is configured to listen to HTTP/1 request at
|
||||
port 8080 in the same host, run nghttpx command-line like this::
|
||||
|
||||
$ nghttpx -f0.0.0.0,8443 -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt
|
||||
|
||||
Then HTTP/2 enabled client can access to the nghttpx in HTTP/2. For
|
||||
example, you can send GET request to the server using nghttp::
|
||||
|
||||
$ nghttp -nv https://localhost:8443/
|
||||
|
||||
HTTP/2 proxy mode
|
||||
-----------------
|
||||
|
||||
If nghttpx is invoked with ``-s`` option, it operates in HTTP/2 proxy
|
||||
mode. The supported protocols in frontend and backend connections are
|
||||
the same in `default mode`_. The difference is that this mode acts
|
||||
like forward proxy and assumes the backend is HTTP/1 proxy server
|
||||
(e.g., squid, traffic server). So HTTP/1 request must include
|
||||
absolute URI in request line.
|
||||
|
||||
By default, frontend connection is encrypted. So this mode is also
|
||||
called secure proxy. If nghttpx is linked with spdylay, it supports
|
||||
SPDY protocols and it works as so called SPDY proxy.
|
||||
|
||||
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
||||
connection, so the connection gets insecure.
|
||||
|
||||
The backend must be HTTP/1 proxy server. nghttpx supports multiple
|
||||
backend server addresses. It translates incoming requests to HTTP/1
|
||||
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.
|
||||
|
||||
For example, to make nghttpx listen to encrypted HTTP/2 requests at
|
||||
port 8443, and a backend HTTP/1 proxy server is configured to listen
|
||||
to HTTP/1 request at port 8080 in the same host, run nghttpx
|
||||
command-line like this::
|
||||
|
||||
$ nghttpx -s -f'*,8443' -b127.0.0.1,8080 /path/to/server.key /path/to/server.crt
|
||||
|
||||
At the time of this writing, Firefox nightly supports HTTP/2 proxy.
|
||||
Chromium can use nghttpx as secure (SPDY) proxy and will support
|
||||
HTTP/2 proxy in the near future.
|
||||
|
||||
To make Firefox nightly or Chromium use nghttpx as HTTP/2 or SPDY
|
||||
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 nightly and
|
||||
Chromium require valid certificate for secure proxy.
|
||||
|
||||
For Firefox nightly, 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::
|
||||
|
||||
file:///path/to/proxy.pac
|
||||
|
||||
For Chromium, use following command-line::
|
||||
|
||||
$ 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::
|
||||
|
||||
CONFIG proxy.config.reverse_proxy.enabled INT 0
|
||||
CONFIG proxy.config.url_remap.remap_required INT 0
|
||||
|
||||
Consult Traffic server `documentation
|
||||
<https://docs.trafficserver.apache.org/en/latest/admin/forward-proxy.en.html>`_
|
||||
to know how to configure traffic server as forward proxy and its
|
||||
security implications.
|
||||
|
||||
Client mode
|
||||
-----------
|
||||
|
||||
If nghttpx is invoked with ``--client`` option, it operates in client
|
||||
mode. In this mode, nghttpx listens for plain, unencrypted HTTP/2 and
|
||||
HTTP/1 requests and translates them to encrypted HTTP/2 requests to
|
||||
the backend. User cannot enable SSL/TLS in frontend connection.
|
||||
|
||||
HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
|
||||
Upgrade. To disable SSL/TLS in backend connection, use
|
||||
``--backend-no-tls`` option.
|
||||
|
||||
By default, the number of backend HTTP/2 connections per worker
|
||||
(thread) is determined by number of ``-b`` option. To adjust this
|
||||
value, use ``--backend-http2-connections-per-worker`` option.
|
||||
|
||||
The backend server is supporsed to be a HTTP/2 web server (e.g.,
|
||||
nghttpd). The one use-case of this mode is utilize existing HTTP/1
|
||||
clients to test HTTP/2 deployment. Suppose that HTTP/2 web server
|
||||
listens to port 80 without encryption. Then run nghttpx as client
|
||||
mode to access to that web server::
|
||||
|
||||
$ nghttpx --client -f127.0.0.1,8080 -b127.0.0.1,80 --backend-no-tls
|
||||
|
||||
.. note::
|
||||
|
||||
You may need ``-k`` option if HTTP/2 server enables SSL/TLS and
|
||||
its certificate is self-signed. But please note that it is
|
||||
insecure.
|
||||
|
||||
Then you can use curl to access HTTP/2 server via nghttpx::
|
||||
|
||||
$ curl http://localhost:8080/
|
||||
|
||||
Client proxy mode
|
||||
-----------------
|
||||
|
||||
If nghttpx is invoked with ``-p`` option, it operates in client proxy
|
||||
mode. This mode behaves like `client mode`_, but it works like
|
||||
forward proxy. So HTTP/1 request must include absolute URI in request
|
||||
line.
|
||||
|
||||
HTTP/1 frontend connection can be upgraded to HTTP/2 using HTTP
|
||||
Upgrade. To disable SSL/TLS in backend connection, use
|
||||
``--backend-no-tls`` option.
|
||||
|
||||
By default, the number of backend HTTP/2 connections per worker
|
||||
(thread) is determined by number of ``-b`` option. To adjust this
|
||||
value, use ``--backend-http2-connections-per-worker`` option.
|
||||
|
||||
The backend server must be a HTTP/2 proxy. You can use nghttpx in
|
||||
`HTTP/2 proxy mode`_ as backend server. The one use-case of this mode
|
||||
is utilize existing HTTP/1 clients to test HTTP/2 connections between
|
||||
2 proxies. The another use-case is use this mode to aggregate local
|
||||
HTTP/1 connections to one HTTP/2 backend encrypted connection. This
|
||||
makes HTTP/1 clients which does not support secure proxy can use
|
||||
secure HTTP/2 proxy via nghttpx client mode.
|
||||
|
||||
Suppose that HTTP/2 proxy listens to port 8443, just like we saw in
|
||||
`HTTP/2 proxy mode`_. To run nghttpx in client proxy mode to access
|
||||
that server, invoke nghttpx like this::
|
||||
|
||||
$ nghttpx -p -f127.0.0.1,8080 -b127.0.0.1,8443
|
||||
|
||||
.. note::
|
||||
|
||||
You may need ``-k`` option if HTTP/2 server's certificate is
|
||||
self-signed. But please note that it is insecure.
|
||||
|
||||
Then you can use curl to issue HTTP request via HTTP/2 proxy::
|
||||
|
||||
$ curl --http-proxy=http://localhost:8080 http://www.google.com/
|
||||
|
||||
You can configure web browser to use localhost:8080 as forward
|
||||
proxy.
|
||||
|
||||
HTTP/2 bridge mode
|
||||
------------------
|
||||
|
||||
If nghttpx is invoked with ``--http2-bridge`` option, it operates in
|
||||
HTTP/2 bridge mode. The supported protocols in frontend connections
|
||||
are the same in `default mode`_. The protocol in backend is HTTP/2
|
||||
only.
|
||||
|
||||
With ``--frontend-no-tls`` option, SSL/TLS is turned off in frontend
|
||||
connection, so the connection gets insecure. To disable SSL/TLS in
|
||||
backend connection, use ``--backend-no-tls`` option.
|
||||
|
||||
By default, the number of backend HTTP/2 connections per worker
|
||||
(thread) is determined by number of ``-b`` option. To adjust this
|
||||
value, use ``--backend-http2-connections-per-worker`` option.
|
||||
|
||||
The backend server is supporsed to be a HTTP/2 web server or HTTP/2
|
||||
proxy. If backend server is HTTP/2 proxy, use
|
||||
``--no-location-rewrite`` and ``--no-host-rewrite`` options to disable
|
||||
rewriting location, host and :authority header field.
|
||||
|
||||
The use-case of this mode is aggregate the incoming connections to one
|
||||
HTTP/2 connection. One backend HTTP/2 connection is created per
|
||||
worker (thread).
|
||||
|
||||
Disable SSL/TLS
|
||||
---------------
|
||||
|
||||
In `default mode`_, `HTTP/2 proxy mode`_ and `HTTP/2 bridge mode`_,
|
||||
frontend connections are encrypted with SSL/TLS by default. To turn
|
||||
off SSL/TLS, use ``--frontend-no-tls`` option. If this option is
|
||||
used, the private key and certificate are not required to run nghttpx.
|
||||
|
||||
In `client mode`_, `client proxy mode`_ and `HTTP/2 bridge mode`_,
|
||||
backend connections are encrypted with SSL/TLS by default. To turn
|
||||
off SSL/TLS, use ``--backend-no-tls`` option.
|
||||
|
||||
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 ``--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 ``-k`` 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 ``--read-rate`` and
|
||||
``--read-burst`` options. For writing, use ``--write-rate`` and
|
||||
``--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:
|
||||
|
||||
* URI in location header field is not absolute URI or is not https 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 and port are replaced with the ones
|
||||
used in frontend, and host is replaced with which appears in
|
||||
:authority or host request header field. :authority header field has
|
||||
precedence. If the above conditions are not met with the host value
|
||||
in :authority header field, rewrite is retried with the value in host
|
||||
header field.
|
||||
|
||||
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. 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.
|
||||
|
||||
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 ``--accesslog-file`` and
|
||||
``--errorlog-file`` options.
|
||||
|
||||
Multiple backend addresses
|
||||
--------------------------
|
||||
|
||||
nghttpx supports multiple backend addresses. To specify them, just
|
||||
use ``-b`` option repeatedly. For example, to use backend1:8080 and
|
||||
backend2:8080, use command-line like this: ``-bbackend1,8080
|
||||
-bbackend2,8080``. For HTTP/2 backend, see also
|
||||
``--backend-http2-connections-per-worker`` option.
|
||||
@@ -1,437 +0,0 @@
|
||||
Python API Reference
|
||||
====================
|
||||
|
||||
.. py:module:: nghttp2
|
||||
|
||||
nghttp2 offers some high level Python API to C library. The bindings
|
||||
currently provide HPACK compressor and decompressor classes and HTTP/2
|
||||
server class.
|
||||
|
||||
The extension module is called ``nghttp2``.
|
||||
|
||||
``make`` will build the bindings. The target Python version is
|
||||
determined by configure script. If the detected Python version is not
|
||||
what you expect, specify a path to Python executable in ``PYTHON``
|
||||
variable as an argument to configure script (e.g., ``./configure
|
||||
PYTHON=/usr/bin/python3.4``).
|
||||
|
||||
HPACK API
|
||||
---------
|
||||
|
||||
.. py:class:: HDDeflater(hd_table_bufsize_max=DEFLATE_MAX_HEADER_TABLE_SIZE)
|
||||
|
||||
This class is used to perform header compression. The
|
||||
*hd_table_bufsize_max* limits the usage of header table in the
|
||||
given amount of bytes. The default value is
|
||||
:py:data:`DEFLATE_MAX_HEADER_TABLE_SIZE`. This is necessary
|
||||
because the deflater and inflater share the same amount of header
|
||||
table and the inflater decides that number. The deflater may not
|
||||
want to use all header table size because of limited memory
|
||||
availability. In that case, *hd_table_bufsize_max* can be used to
|
||||
cap the upper limit of table size whatever the header table size is
|
||||
chosen by the inflater.
|
||||
|
||||
.. py:method:: deflate(headers)
|
||||
|
||||
Deflates the *headers*. The *headers* must be sequence of tuple
|
||||
of name/value pair, which are byte strings (not unicode string).
|
||||
|
||||
This method returns the deflated header block in byte string.
|
||||
Raises the exception if any error occurs.
|
||||
|
||||
.. py:method:: set_no_refset(no_refset)
|
||||
|
||||
Tells the deflater not to use reference set if *no_refset* is
|
||||
evaluated to ``True``. If that happens, on each subsequent
|
||||
invocation of :py:meth:`deflate()`, deflater will clear up
|
||||
refersent set.
|
||||
|
||||
.. py:method:: change_table_size(hd_table_bufsize_max)
|
||||
|
||||
Changes header table size to *hd_table_bufsize_max* byte. if
|
||||
*hd_table_bufsize_max* is strictly larger than
|
||||
``hd_table_bufsize_max`` given in constructor,
|
||||
``hd_table_bufsize_max`` is used as header table size instead.
|
||||
|
||||
Raises the exception if any error occurs.
|
||||
|
||||
.. py:method:: get_hd_table()
|
||||
|
||||
Returns copy of current dynamic header table.
|
||||
|
||||
The following example shows how to deflate header name/value pairs:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import binascii, nghttp2
|
||||
|
||||
deflater = nghttp2.HDDeflater()
|
||||
|
||||
res = deflater.deflate([(b'foo', b'bar'),
|
||||
(b'baz', b'buz')])
|
||||
|
||||
print(binascii.b2a_hex(res))
|
||||
|
||||
|
||||
.. py:class:: HDInflater()
|
||||
|
||||
This class is used to perform header decompression.
|
||||
|
||||
.. py:method:: inflate(data)
|
||||
|
||||
Inflates the deflated header block *data*. The *data* must be
|
||||
byte string.
|
||||
|
||||
Raises the exception if any error occurs.
|
||||
|
||||
.. py:method:: change_table_size(hd_table_bufsize_max)
|
||||
|
||||
Changes header table size to *hd_table_bufsize_max* byte.
|
||||
|
||||
Raises the exception if any error occurs.
|
||||
|
||||
.. py:method:: get_hd_table()
|
||||
|
||||
Returns copy of current dynamic header table.
|
||||
|
||||
The following example shows how to inflate deflated header block:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
deflater = nghttp2.HDDeflater()
|
||||
|
||||
data = deflater.deflate([(b'foo', b'bar'),
|
||||
(b'baz', b'buz')])
|
||||
|
||||
inflater = nghttp2.HDInflater()
|
||||
|
||||
hdrs = inflater.inflate(data)
|
||||
|
||||
print(hdrs)
|
||||
|
||||
|
||||
.. py:function:: print_hd_table(hdtable)
|
||||
|
||||
Convenient function to print *hdtable* to the standard output. The
|
||||
*hdtable* is the one retrieved by
|
||||
:py:meth:`HDDeflater.get_hd_table()` or
|
||||
:py:meth:`HDInflater.get_hd_table()`. This function does not work
|
||||
if header name/value cannot be decoded using UTF-8 encoding.
|
||||
|
||||
In output, ``s=N`` means the entry occupies ``N`` bytes in header
|
||||
table. If ``r=y``, then the entry is in the reference set.
|
||||
|
||||
.. py:data:: DEFAULT_HEADER_TABLE_SIZE
|
||||
|
||||
The default header table size, which is 4096 as per HTTP/2
|
||||
specification.
|
||||
|
||||
.. py:data:: DEFLATE_MAX_HEADER_TABLE_SIZE
|
||||
|
||||
The default header table size for deflater. The initial value
|
||||
is 4096.
|
||||
|
||||
HTTP/2 servers
|
||||
--------------
|
||||
|
||||
.. note::
|
||||
|
||||
We use :py:mod:`asyncio` for HTTP/2 server classes. Therefore,
|
||||
Python 3.4 or later is required to use these objects. To
|
||||
explicitly configure nghttp2 build to use Python 3.4, specify the
|
||||
``PYTHON`` variable to the path to Python 3.4 executable when
|
||||
invoking configure script like this::
|
||||
|
||||
$ ./configure PYTHON=/usr/bin/python3.4
|
||||
|
||||
.. py:class:: HTTP2Server(address, RequestHandlerClass, ssl=None)
|
||||
|
||||
This class builds on top of the :py:mod:`asyncio` event loop. On
|
||||
construction, *RequestHandlerClass* must be given, which must be a
|
||||
subclass of :py:class:`BaseRequestHandler` class.
|
||||
|
||||
The *address* must be a tuple of hostname/IP address and port to
|
||||
bind. If hostname/IP address is ``None``, all interfaces are
|
||||
assumed.
|
||||
|
||||
To enable SSL/TLS, specify instance of :py:class:`ssl.SSLContext`
|
||||
in *ssl*. Before passing *ssl* to
|
||||
:py:func:`BaseEventLoop.create_server`, ALPN protocol identifiers
|
||||
are set using :py:meth:`ssl.SSLContext.set_npn_protocols`.
|
||||
|
||||
To disable SSL/TLS, omit *ssl* or specify ``None``.
|
||||
|
||||
.. py:method:: serve_forever()
|
||||
|
||||
Runs server and processes incoming requests forever.
|
||||
|
||||
.. py:class:: BaseRequestHandler(http2, stream_id)
|
||||
|
||||
The class is used to handle the single HTTP/2 stream. By default,
|
||||
it does not nothing. It must be subclassed to handle each event
|
||||
callback method.
|
||||
|
||||
The first callback method invoked is :py:meth:`on_headers()`. It is
|
||||
called when HEADERS frame, which includes request header fields, is
|
||||
arrived.
|
||||
|
||||
If request has request body, :py:meth:`on_data()` is invoked for
|
||||
each chunk of received data chunk.
|
||||
|
||||
When whole request is received, :py:meth:`on_request_done()` is
|
||||
invoked.
|
||||
|
||||
When stream is closed, :py:meth:`on_close()` is called.
|
||||
|
||||
The application can send response using :py:meth:`send_response()`
|
||||
method. It can be used in :py:meth:`on_headers()`,
|
||||
:py:meth:`on_data()` or :py:meth:`on_request_done()`.
|
||||
|
||||
The application can push resource using :py:meth:`push()` method.
|
||||
It must be used before :py:meth:`send_response()` call.
|
||||
|
||||
A :py:class:`BaseRequestHandler` has the following instance
|
||||
variables:
|
||||
|
||||
.. py:attribute:: client_address
|
||||
|
||||
Contains a tuple of the form ``(host, port)`` referring to the
|
||||
client's address.
|
||||
|
||||
.. py:attribute:: stream_id
|
||||
|
||||
Stream ID of this stream
|
||||
|
||||
.. py:attribute:: scheme
|
||||
|
||||
Scheme of the request URI. This is a value of ``:scheme``
|
||||
header field.
|
||||
|
||||
.. py:attribute:: method
|
||||
|
||||
Method of this stream. This is a value of ``:method`` header
|
||||
field.
|
||||
|
||||
.. py:attribute:: host
|
||||
|
||||
This is a value of ``:authority`` or ``host`` header field.
|
||||
|
||||
.. py:attribute:: path
|
||||
|
||||
This is a value of ``:path`` header field.
|
||||
|
||||
.. py:attribute:: headers
|
||||
|
||||
Request header fields.
|
||||
|
||||
A :py:class:`BaseRequestHandler` has the following methods:
|
||||
|
||||
.. py:method:: on_headers()
|
||||
|
||||
Called when request HEADERS is arrived. By default, this method
|
||||
does nothing.
|
||||
|
||||
.. py:method:: on_data(data)
|
||||
|
||||
Called when a chunk of request body *data* is arrived. This
|
||||
method will be called multiple times until all data are
|
||||
received. By default, this method does nothing.
|
||||
|
||||
.. py:method:: on_request_done()
|
||||
|
||||
Called when whole request was received. By default, this method
|
||||
does nothing.
|
||||
|
||||
.. py:method:: on_close(error_code)
|
||||
|
||||
Called when stream is about to close. The *error_code*
|
||||
indicates the reason of closure. If it is ``0``, the stream is
|
||||
going to close without error.
|
||||
|
||||
.. py:method:: send_response(status=200, headers=None, body=None)
|
||||
|
||||
Send response. The *status* is HTTP status code. The *headers*
|
||||
is additional response headers. The *:status* header field will
|
||||
be appended by the library. The *body* is the response body.
|
||||
It could be ``None`` if response body is empty. Or it must be
|
||||
instance of either ``str``, ``bytes``, :py:class:`io.IOBase` or
|
||||
callable, called body generator, which takes one parameter,
|
||||
size. The body generator generates response body. It can pause
|
||||
generation of response so that it can wait for slow backend data
|
||||
generation. When invoked, it should return tuple, byte string
|
||||
at most size length and flag. The flag is either
|
||||
:py:data:`DATA_OK`, :py:data:`DATA_EOF` or
|
||||
:py:data:`DATA_DEFERRED`. For non-empty byte string and it is
|
||||
not the last chunk of response, :py:data:`DATA_OK` must be
|
||||
returned as flag. If this is the last chunk of the response
|
||||
(byte string could be ``None``), :py:data:`DATA_EOF` must be
|
||||
returned as flag. If there is no data available right now, but
|
||||
additional data are anticipated, return tuple (``None``,
|
||||
:py:data:`DATA_DEFERRED`). When data arrived, call
|
||||
:py:meth:`resume()` and restart response body transmission.
|
||||
|
||||
Only the body generator can pause response body generation;
|
||||
instance of :py:class:`io.IOBase` must not block.
|
||||
|
||||
If instance of ``str`` is specified as *body*, it will be
|
||||
encoded using UTF-8.
|
||||
|
||||
The *headers* is a list of tuple of the form ``(name,
|
||||
value)``. The ``name`` and ``value`` can be either byte string
|
||||
or unicode string. In the latter case, they will be encoded
|
||||
using UTF-8.
|
||||
|
||||
Raises the exception if any error occurs.
|
||||
|
||||
.. py:method:: push(path, method='GET', request_headers=None, status=200, headers=None, body=None)
|
||||
|
||||
Push a specified resource. The *path* is a path portion of
|
||||
request URI for this resource. The *method* is a method to
|
||||
access this resource. The *request_headers* is additional
|
||||
request headers to access this resource. The ``:scheme``,
|
||||
``:method``, ``:authority`` and ``:path`` are appended by the
|
||||
library. The ``:scheme`` and ``:authority`` are inherited from
|
||||
request header fields of the associated stream.
|
||||
|
||||
The *status* is HTTP status code. The *headers* is additional
|
||||
response headers. The ``:status`` header field is appended by
|
||||
the library. The *body* is the response body. It has the same
|
||||
semantics of *body* parameter of :py:meth:`send_response()`.
|
||||
|
||||
The headers and request_headers are a list of tuple of the form
|
||||
``(name, value)``. The ``name`` and ``value`` can be either byte
|
||||
string or unicode string. In the latter case, they will be
|
||||
encoded using UTF-8.
|
||||
|
||||
Returns an instance of ``RequestHandlerClass`` specified in
|
||||
:py:class:`HTTP2Server` constructor for the pushed resource.
|
||||
|
||||
Raises the exception if any error occurs.
|
||||
|
||||
.. py:method:: resume()
|
||||
|
||||
Signals the restarting of response body transmission paused by
|
||||
``DATA_DEFERRED`` from the body generator (see
|
||||
:py:meth:`send_response()` about the body generator). It is not
|
||||
an error calling this method while response body transmission is
|
||||
not paused.
|
||||
|
||||
.. py:data:: DATA_OK
|
||||
|
||||
``DATA_OK`` indicates non empty data is generated from body generator.
|
||||
|
||||
.. py:data:: DATA_EOF
|
||||
|
||||
``DATA_EOF`` indicates the end of response body.
|
||||
|
||||
.. py:data:: DATA_DEFERRED
|
||||
|
||||
``DATA_DEFERRED`` indicates that data are not available right now
|
||||
and response should be paused.
|
||||
|
||||
The following example illustrates :py:class:`HTTP2Server` and
|
||||
:py:class:`BaseRequestHandler` usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/usr/bin/env python
|
||||
|
||||
import io, ssl
|
||||
|
||||
import nghttp2
|
||||
|
||||
class Handler(nghttp2.BaseRequestHandler):
|
||||
|
||||
def on_headers(self):
|
||||
self.push(path='/css/style.css',
|
||||
request_headers = [('content-type', 'text/css')],
|
||||
status=200,
|
||||
body='body{margin:0;}')
|
||||
|
||||
self.send_response(status=200,
|
||||
headers = [('content-type', 'text/plain')],
|
||||
body=io.BytesIO(b'nghttp2-python FTW'))
|
||||
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||
ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
|
||||
ctx.load_cert_chain('server.crt', 'server.key')
|
||||
|
||||
# give None to ssl to make the server non-SSL/TLS
|
||||
server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx)
|
||||
server.serve_forever()
|
||||
|
||||
The following example illustrates HTTP/2 server using asynchronous
|
||||
response body generation. This is simplified reverse proxy:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/usr/bin/env python
|
||||
|
||||
import ssl
|
||||
import os
|
||||
import urllib
|
||||
import asyncio
|
||||
import io
|
||||
|
||||
import nghttp2
|
||||
|
||||
@asyncio.coroutine
|
||||
def get_http_header(handler, url):
|
||||
url = urllib.parse.urlsplit(url)
|
||||
ssl = url.scheme == 'https'
|
||||
if url.port == None:
|
||||
if url.scheme == 'https':
|
||||
port = 443
|
||||
else:
|
||||
port = 80
|
||||
else:
|
||||
port = url.port
|
||||
|
||||
connect = asyncio.open_connection(url.hostname, port, ssl=ssl)
|
||||
reader, writer = yield from connect
|
||||
req = 'GET {path} HTTP/1.0\r\n\r\n'.format(path=url.path or '/')
|
||||
writer.write(req.encode('utf-8'))
|
||||
# skip response header fields
|
||||
while True:
|
||||
line = yield from reader.readline()
|
||||
line = line.rstrip()
|
||||
if not line:
|
||||
break
|
||||
# read body
|
||||
while True:
|
||||
b = yield from reader.read(4096)
|
||||
if not b:
|
||||
break
|
||||
handler.buf.write(b)
|
||||
writer.close()
|
||||
handler.buf.seek(0)
|
||||
handler.eof = True
|
||||
handler.resume()
|
||||
|
||||
class Body:
|
||||
def __init__(self, handler):
|
||||
self.handler = handler
|
||||
self.handler.eof = False
|
||||
self.handler.buf = io.BytesIO()
|
||||
|
||||
def generate(self, n):
|
||||
buf = self.handler.buf
|
||||
data = buf.read1(n)
|
||||
if not data and not self.handler.eof:
|
||||
return None, nghttp2.DATA_DEFERRED
|
||||
return data, nghttp2.DATA_EOF if self.handler.eof else nghttp2.DATA_OK
|
||||
|
||||
class Handler(nghttp2.BaseRequestHandler):
|
||||
|
||||
def on_headers(self):
|
||||
body = Body(self)
|
||||
asyncio.async(get_http_header(
|
||||
self, 'http://localhost' + self.path.decode('utf-8')))
|
||||
self.send_response(status=200, body=body.generate)
|
||||
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||
ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
|
||||
ctx.load_cert_chain('server.crt', 'server.key')
|
||||
|
||||
server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx)
|
||||
server.serve_forever()
|
||||
@@ -1,7 +1,7 @@
|
||||
Tutorial: HTTP/2 client
|
||||
Tutorial: HTTP/2.0 client
|
||||
=========================
|
||||
|
||||
In this tutorial, we are going to write very primitive HTTP/2
|
||||
In this tutorial, we are going to write very primitive HTTP/2.0
|
||||
client. The complete source code, `libevent-client.c`_, is attached at
|
||||
the end of this page. It also resides in examples directory in the
|
||||
archive or repository.
|
||||
@@ -19,12 +19,14 @@ function ``main()`` and ``run()``, which is not so relevant to nghttp2
|
||||
library use. The one thing you should look at is setup NPN callback.
|
||||
The NPN callback is used for the client to select the next application
|
||||
protocol over the SSL/TLS transport. In this tutorial, we use
|
||||
`nghttp2_select_next_protocol()` function to select the HTTP/2
|
||||
`nghttp2_select_next_protocol()` function to select the HTTP/2.0
|
||||
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_) {
|
||||
static int select_next_proto_cb(SSL* ssl,
|
||||
unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen,
|
||||
void *arg)
|
||||
{
|
||||
if(nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
||||
}
|
||||
@@ -34,7 +36,8 @@ protocol the library supports::
|
||||
The callback is set to the SSL_CTX object using
|
||||
``SSL_CTX_set_next_proto_select_cb()`` function::
|
||||
|
||||
static SSL_CTX *create_ssl_ctx(void) {
|
||||
static SSL_CTX* create_ssl_ctx(void)
|
||||
{
|
||||
SSL_CTX *ssl_ctx;
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if(!ssl_ctx) {
|
||||
@@ -42,15 +45,14 @@ The callback is set to the SSL_CTX object using
|
||||
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_ALL | SSL_OP_NO_SSLv2 | 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);
|
||||
return ssl_ctx;
|
||||
}
|
||||
|
||||
We use ``http2_session_data`` structure to store the data related to
|
||||
the HTTP/2 session::
|
||||
the HTTP/2.0 session::
|
||||
|
||||
typedef struct {
|
||||
nghttp2_session *session;
|
||||
@@ -65,11 +67,11 @@ its stream specific data in ``http2_stream_data`` structure and the
|
||||
defined as follows::
|
||||
|
||||
typedef struct {
|
||||
/* The NULL-terminated URI string to retrieve. */
|
||||
/* The NULL-terminated URI string to retreive. */
|
||||
const char *uri;
|
||||
/* Parsed result of the |uri| */
|
||||
struct http_parser_url *u;
|
||||
/* The authority portion of the |uri|, not NULL-terminated */
|
||||
/* The authroity portion of the |uri|, not NULL-terminated */
|
||||
char *authority;
|
||||
/* The path portion of the |uri|, including query, not
|
||||
NULL-terminated */
|
||||
@@ -89,17 +91,20 @@ respectively.
|
||||
Then we call function ``initiate_connection()`` to start connecting to
|
||||
the remote server::
|
||||
|
||||
static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
|
||||
static void initiate_connection(struct event_base *evbase,
|
||||
SSL_CTX *ssl_ctx,
|
||||
const char *host, uint16_t port,
|
||||
http2_session_data *session_data) {
|
||||
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);
|
||||
bev = bufferevent_openssl_socket_new(evbase, -1, ssl,
|
||||
BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_DEFER_CALLBACKS |
|
||||
BEV_OPT_CLOSE_ON_FREE);
|
||||
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
||||
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
||||
AF_UNSPEC, host, port);
|
||||
@@ -117,7 +122,8 @@ The ``eventcb()`` is invoked by libevent event loop when an event
|
||||
(e.g., connection has been established, timeout, etc) happens on the
|
||||
underlying network socket::
|
||||
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr) {
|
||||
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);
|
||||
@@ -148,31 +154,18 @@ event, we just simply tear down the connection. The
|
||||
finished successfully. We first initialize nghttp2 session object in
|
||||
``initialize_nghttp2_session()`` function::
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks = {0};
|
||||
|
||||
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);
|
||||
callbacks.send_callback = send_callback;
|
||||
callbacks.before_frame_send_callback = before_frame_send_callback;
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||
callbacks.on_header_callback = on_header_callback;
|
||||
callbacks.on_begin_headers_callback = on_begin_headers_callback;
|
||||
nghttp2_session_client_new(&session_data->session, &callbacks, session_data);
|
||||
}
|
||||
|
||||
Since we are creating client, we use `nghttp2_session_client_new()` to
|
||||
@@ -183,28 +176,31 @@ The `delete_http2_session_data()` destroys ``session_data`` and frees
|
||||
its bufferevent, so it closes underlying connection as well. It also
|
||||
calls `nghttp2_session_del()` to delete nghttp2 session object.
|
||||
|
||||
We begin HTTP/2 communication by sending client connection preface,
|
||||
We begin HTTP/2.0 communication by sending client connection header,
|
||||
which is 24 bytes magic byte sequence
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`) and SETTINGS frame. The
|
||||
transmission of client connection header is done in
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_HEADER`) followed by SETTINGS
|
||||
frame. The transmission of client connection header is done in
|
||||
``send_client_connection_header()``::
|
||||
|
||||
static void send_client_connection_header(http2_session_data *session_data) {
|
||||
static void send_client_connection_header(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_settings_entry iv[1] = {
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
||||
};
|
||||
int rv;
|
||||
|
||||
bufferevent_write(session_data->bev, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
bufferevent_write(session_data->bev,
|
||||
NGHTTP2_CLIENT_CONNECTION_HEADER,
|
||||
NGHTTP2_CLIENT_CONNECTION_HEADER_LEN);
|
||||
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 to 100, which is
|
||||
really not needed for this tiny example program, but we are
|
||||
really not needed for this tiny example progoram, but we are
|
||||
demonstrating the use of SETTINGS frame. To queue the SETTINGS frame
|
||||
for the transmission, we use `nghttp2_submit_settings()`. Note that
|
||||
`nghttp2_submit_settings()` function only queues the frame and not
|
||||
@@ -215,26 +211,26 @@ used, which is described about later.
|
||||
After the transmission of client connection header, we enqueue HTTP
|
||||
request in ``submit_request()`` function::
|
||||
|
||||
static void submit_request(http2_session_data *session_data) {
|
||||
int32_t stream_id;
|
||||
static void submit_request(http2_session_data *session_data)
|
||||
{
|
||||
int rv;
|
||||
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(":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)};
|
||||
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));
|
||||
rv = nghttp2_submit_request(session_data->session, NGHTTP2_PRI_DEFAULT,
|
||||
hdrs, ARRLEN(hdrs), NULL, stream_data);
|
||||
if(rv != 0) {
|
||||
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(rv));
|
||||
}
|
||||
|
||||
stream_data->stream_id = stream_id;
|
||||
}
|
||||
|
||||
We build HTTP request header fields in ``hdrs`` which is an array of
|
||||
@@ -243,30 +239,24 @@ We build HTTP request header fields in ``hdrs`` which is an array of
|
||||
we use `nghttp2_submit_request()` function. The `stream_data` is
|
||||
passed in *stream_user_data* parameter. It is used in nghttp2
|
||||
callbacks which we'll describe about later.
|
||||
`nghttp2_submit_request()` returns the newly assigned stream ID for
|
||||
this request.
|
||||
|
||||
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, void *ptr) {
|
||||
static void readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
ssize_t readlen;
|
||||
int rv;
|
||||
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, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
rv = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(rv < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
evbuffer_drain(input, rv);
|
||||
if(session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
@@ -280,7 +270,8 @@ invoke nghttp2 callbacks and also queue frames. Since there may be
|
||||
pending frames, we call ``session_send()`` function to send those
|
||||
frames. The ``session_send()`` function is defined as follows::
|
||||
|
||||
static int session_send(http2_session_data *session_data) {
|
||||
static int session_send(http2_session_data *session_data)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = nghttp2_session_send(session_data->session);
|
||||
@@ -292,12 +283,16 @@ frames. The ``session_send()`` function is defined as follows::
|
||||
}
|
||||
|
||||
The `nghttp2_session_send()` function serializes the frame into wire
|
||||
format and call ``send_callback()`` function of type
|
||||
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
||||
follows::
|
||||
format and call :member:`nghttp2_session_callbacks.send_callback` with
|
||||
it. We set ``send_callback()`` function to
|
||||
:member:`nghttp2_session_callbacks.send_callback` in
|
||||
``initialize_nghttp2_session()`` function described earlier. It 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) {
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length,
|
||||
int flags, void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
struct bufferevent *bev = session_data->bev;
|
||||
bufferevent_write(bev, data, length);
|
||||
@@ -308,19 +303,21 @@ 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 ``write()`` system call
|
||||
in the ``send_callback()``, we will surely get ``EAGAIN`` or
|
||||
``EWOULDBLOCK`` since the socket has limited send buffer. If that
|
||||
happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the
|
||||
nghttp2 library to stop sending further data. But writing to the
|
||||
bufferevent, we have to regulate the amount data to be buffered by
|
||||
ourselves to avoid possible huge memory consumption. In this example
|
||||
client, we do not limit anything. To see how to regulate the amount of
|
||||
buffered data, see the ``send_callback()`` in the server tutorial.
|
||||
in the :member:`nghttp2_session_callbacks.send_callback`, we will
|
||||
surely get ``EAGAIN`` or ``EWOULDBLOCK`` since the socket has limited
|
||||
send buffer. If that happens, we can return
|
||||
:macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the nghttp2 library to stop
|
||||
sending further data. But writing to the bufferevent, we have to
|
||||
regulate the amount data to be buffered by ourselves to avoid possible
|
||||
huge memory consumption. In this example client, we do not limit
|
||||
anything. 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 have been sent::
|
||||
|
||||
static void writecb(struct bufferevent *bev _U_, void *ptr) {
|
||||
static void writecb(struct bufferevent *bev, 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 &&
|
||||
@@ -347,14 +344,57 @@ We have already described about nghttp2 callback ``send_callback()``.
|
||||
Let's describe remaining nghttp2 callbacks we setup in
|
||||
``initialize_nghttp2_setup()`` function.
|
||||
|
||||
The `before_frame_send_callback()` function is invoked when a frame is
|
||||
about to be sent::
|
||||
|
||||
static int before_frame_send_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) {
|
||||
stream_data =
|
||||
(http2_stream_data*)nghttp2_session_get_stream_user_data
|
||||
(session, frame->hd.stream_id);
|
||||
if(stream_data == session_data->stream_data) {
|
||||
stream_data->stream_id = frame->hd.stream_id;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Remember that we have not get stream ID when we submit HTTP request
|
||||
using `nghttp2_submit_request()`. Since nghttp2 library reorders the
|
||||
request based on priority and stream ID must be monotonically
|
||||
increased, the stream ID is not assigned just before transmission.
|
||||
The one of the purpose of this callback is get the stream ID assigned
|
||||
to the frame. First we check that the frame is HEADERS frame. Since
|
||||
HEADERS has several meanings in HTTP/2.0, we check that it is request
|
||||
HEADERS (which means that the first HEADERS frame to create a stream).
|
||||
The assigned stream ID is ``frame->hd.stream_id``. Recall that we
|
||||
passed ``stream_data`` in the *stream_user_data* parameter of
|
||||
`nghttp2_submit_request()` function. We can get it using
|
||||
`nghttp2_session_get_stream_user_data()` function. To really sure that
|
||||
this HEADERS frame is the request HEADERS we have queued, we check
|
||||
that ``session_data->stream_data`` and ``stream_data`` returned from
|
||||
`nghttp2_session_get_stream_user_data()` are pointing the same
|
||||
location. In this example program, we just only uses 1 stream, it is
|
||||
unnecessary to compare them, but real applications surely deal with
|
||||
multiple streams, and *stream_user_data* is very handy to identify
|
||||
which HEADERS we are seeing in the callback. Therefore we just show
|
||||
how to use it here.
|
||||
|
||||
Each request header name/value pair is emitted via
|
||||
``on_header_callback`` function::
|
||||
|
||||
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) {
|
||||
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,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
switch(frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
@@ -368,13 +408,14 @@ Each request header name/value pair is emitted via
|
||||
return 0;
|
||||
}
|
||||
|
||||
In this tutorial, we just print the name/value pair.
|
||||
In this turotial, we just print the name/value pair.
|
||||
|
||||
After all name/value pairs are emitted for a frame,
|
||||
``on_frame_recv_callback`` function is called::
|
||||
|
||||
static int on_frame_recv_callback(nghttp2_session *session _U_,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
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;
|
||||
switch(frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
@@ -388,17 +429,18 @@ After all name/value pairs are emitted for a frame,
|
||||
}
|
||||
|
||||
In this tutorial, we are just interested in the HTTP response
|
||||
HEADERS. We check the frame type and its category (it should be
|
||||
HEADERS. We check te frame type and its category (it should be
|
||||
:macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). Also check
|
||||
its stream ID.
|
||||
|
||||
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,
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const uint8_t *data, size_t len,
|
||||
void *user_data) {
|
||||
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);
|
||||
@@ -407,22 +449,24 @@ of data is received from the remote peer::
|
||||
}
|
||||
|
||||
In our case, a chunk of data is response body. After checking stream
|
||||
ID, we just write the received data to the stdout. Note that the
|
||||
ID, we just write the recieved data to the stdout. Note that 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,
|
||||
static int on_stream_close_callback(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
void *user_data) {
|
||||
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);
|
||||
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;
|
||||
@@ -435,6 +479,6 @@ If the stream ID matches the one we initiated, it means that its
|
||||
stream is going to be closed. Since we have finished to get the
|
||||
resource we want (or the stream was reset by RST_STREAM from the
|
||||
remote peer), we call `nghttp2_session_terminate_session()` to
|
||||
commencing the closure of the HTTP/2 session gracefully. If you have
|
||||
commencing the closure of the HTTP/2.0 session gracefully. If you have
|
||||
some data associated for the stream to be closed, you may delete it
|
||||
here.
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
Tutorial: HPACK API
|
||||
===================
|
||||
|
||||
In this tutorial, we describe basic use of HPACK API in nghttp2
|
||||
library. We briefly describe APIs for deflating and inflating header
|
||||
fields. The example of using these APIs are presented as complete
|
||||
source code `deflate.c`_.
|
||||
|
||||
Deflating (encoding) headers
|
||||
----------------------------
|
||||
|
||||
First we need to initialize :type:`nghttp2_hd_deflater` object using
|
||||
`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 :type:`nghttp2_hd_deflater` object and
|
||||
initializes it and assigns its pointer to ``*deflater_ptr`` passed by
|
||||
parameter. The *deflate_hd_table_bufsize_max* is the upper bound of
|
||||
header table size the deflater will use. This will limit the memory
|
||||
usage in deflater object for dynamic header table. If you doubt, just
|
||||
specify 4096 here, which is the default upper bound of dynamic header
|
||||
table buffer size.
|
||||
|
||||
To encode header fields, `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()` function described above. The *buf* is a
|
||||
pointer to buffer to store encoded byte string. The *buflen* is
|
||||
capacity of *buf*. The *nva* is a pointer to :type:`nghttp2_nv`,
|
||||
which is an array of header fields to deflate. The *nvlen* is the
|
||||
number of header fields which *nva* contains.
|
||||
|
||||
It is important to initialize and assign all members of
|
||||
:type:`nghttp2_nv`. If a header field should not be inserted in
|
||||
dynamic header table for a security reason, set
|
||||
:macro:`NGHTTP2_NV_FLAG_NO_INDEX` flag in :member:`nghttp2_nv.flags`.
|
||||
|
||||
`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 encoded result, use `nghttp2_hd_deflate_bound()` function::
|
||||
|
||||
size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
|
||||
const nghttp2_nv *nva, size_t nvlen);
|
||||
|
||||
Pass this function with the same parameters *deflater*, *nva* and
|
||||
*nvlen* which will be passed to `nghttp2_hd_deflate_hd()`.
|
||||
|
||||
The subsequent call of `nghttp2_hd_deflate_hd()` will use current
|
||||
encoder state and perform differential encoding which is the
|
||||
fundamental compression gain for HPACK.
|
||||
|
||||
Once `nghttp2_hd_deflate_hd()` fails, it cannot be undone and its
|
||||
further call with the same deflater object shall fail. So it is very
|
||||
important to use `nghttp2_hd_deflate_bound()` to know the required
|
||||
size of buffer.
|
||||
|
||||
To delete :type:`nghttp2_hd_deflater` object, use `nghttp2_hd_deflate_del()`
|
||||
function.
|
||||
|
||||
Inflating (decoding) headers
|
||||
----------------------------
|
||||
|
||||
We use :type:`nghttp2_hd_inflater` object 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_hd()` function::
|
||||
|
||||
ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
|
||||
nghttp2_nv *nv_out, int *inflate_flags,
|
||||
uint8_t *in, size_t inlen, int in_final);
|
||||
|
||||
The *inflater* is the inflater object initialized above. The *nv_out*
|
||||
is a pointer to :type:`nghttp2_nv` to store the result. The *in* is a
|
||||
pointer to input data and *inlen* is its length. The caller is not
|
||||
required to specify whole deflated header data to *in* at once. It
|
||||
can call this function multiple times for portion of the data in
|
||||
streaming way. 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 output parameter and successful call of this
|
||||
function stores a set of flags in it. It will be described later.
|
||||
|
||||
This function returns when each header field is inflated. When this
|
||||
happens, the function sets :macro:`NGHTTP2_HD_INFLATE_EMIT` flag to
|
||||
*inflate_flag* parameter and header field is stored in *nv_out*. The
|
||||
return value indicates the number of data read from *in* to processed
|
||||
so far. It may be less than *inlen*. The caller should call the
|
||||
function repeatedly until all data are processed by adjusting *in* and
|
||||
*inlen* with the processed bytes.
|
||||
|
||||
If *in_final* is nonzero and all given data was processed, the
|
||||
function sets :macro:`NGHTTP2_HD_INFLATE_FINAL` flag to
|
||||
*inflate_flag*. If the caller sees this flag set, call
|
||||
`nghttp2_hd_inflate_end_headers()` function.
|
||||
|
||||
If *in_final* is zero and :macro:`NGHTTP2_HD_INFLATE_EMIT` flag is not
|
||||
set, it indicates that all given data was processed. The caller is
|
||||
required to pass subsequent data.
|
||||
|
||||
It is important to note that the function may produce one or more
|
||||
header fields even if *inlen* is 0 when *in_final* is nonzero, due to
|
||||
differential encoding.
|
||||
|
||||
The example use of `nghttp2_hd_inflate_hd()` is shown in
|
||||
`inflate_header_block()` function in `deflate.c`_.
|
||||
|
||||
To delete :type:`nghttp2_hd_inflater` object, use `nghttp2_hd_inflate_del()`
|
||||
function.
|
||||
@@ -1,45 +1,47 @@
|
||||
Tutorial: HTTP/2 server
|
||||
Tutorial: HTTP/2.0 server
|
||||
=========================
|
||||
|
||||
In this tutorial, we are going to write 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
|
||||
HTTP/2.0 web server, which supports HTTPS only. It can handle
|
||||
concurrent multiple requests, but only GET method is supported. The
|
||||
complete source code, `libevent-server.c`_, is attached at the end of
|
||||
this page. It also resides in examples directory in the archive or
|
||||
repository.
|
||||
|
||||
This simple server takes 3 arguments, a port number to listen to, a path to
|
||||
your SSL/TLS private key file and a path to your certificate file. Its
|
||||
synopsis is like this::
|
||||
This simple server takes 3 arguments, a port number to listen to, a
|
||||
path to SSL/TLS private key file and certificate file. Its synopsis
|
||||
is like this::
|
||||
|
||||
$ 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.
|
||||
|
||||
First we create a setup routine for libevent and OpenSSL in the functions
|
||||
``main()`` and ``run()``. One thing in there you should look at, is the setup
|
||||
of the NPN callback. The NPN callback is used for the server to advertise
|
||||
which application protocols the server supports to a client. In this example
|
||||
program, when creating ``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 life
|
||||
time::
|
||||
First we do some setup routine for libevent and OpenSSL library in
|
||||
function ``main()`` and ``run()``, which is not so relevant to nghttp2
|
||||
library use. The one thing you should look at is setup NPN callback.
|
||||
The NPN callback is used for the server to advertise the application
|
||||
protocols the server supports to a client. In this example program,
|
||||
when creating ``SSL_CTX`` object, we stores the application protocol
|
||||
name in the wire format of NPN in statically allocated buffer. This is
|
||||
safe because we only create 1 ``SSL_CTX`` object in the entire program
|
||||
life time::
|
||||
|
||||
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_) {
|
||||
static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
|
||||
void *arg)
|
||||
{
|
||||
*data = next_proto_list;
|
||||
*len = (unsigned int)next_proto_list_len;
|
||||
*len = next_proto_list_len;
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||
SSL_CTX *ssl_ctx;
|
||||
EC_KEY *ecdh;
|
||||
|
||||
static SSL_CTX* create_ssl_ctx(const char *key_file, const char *cert_file)
|
||||
{
|
||||
SSL_CTX *ssl_ctx;
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
|
||||
...
|
||||
@@ -53,25 +55,25 @@ time::
|
||||
return ssl_ctx;
|
||||
}
|
||||
|
||||
The wire format of NPN is a sequence of length prefixed string. Exactly one
|
||||
byte is 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. The nghttp2 library exports its identifier in
|
||||
: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
|
||||
The wire format of NPN is a sequence of length prefixed string. The
|
||||
exactly one byte is used to specify the length of each protocol
|
||||
identifier. In this tutorial, we advertise the HTTP/2.0 protocol the
|
||||
nghttp2 library supports. The nghttp2 library exports its identifier
|
||||
in :macro:`NGHTTP2_PROTO_VERSION_ID`. The ``next_proto_cb()`` function
|
||||
is the server-side NPN callback. In OpenSSL implementation, we just
|
||||
assign the pointer to the NPN buffers we filled earlier. The NPN
|
||||
callback function is set to ``SSL_CTX`` object using
|
||||
``SSL_CTX_set_next_protos_advertised_cb()``.
|
||||
|
||||
We use the ``app_content`` structure to store application-wide data::
|
||||
We use ``app_content`` structure to store the 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::
|
||||
We use ``http2_session_data`` structure to store the session-level
|
||||
(which corresponds to 1 HTTP/2.0 connection) data::
|
||||
|
||||
typedef struct http2_session_data {
|
||||
struct http2_stream_data root;
|
||||
@@ -79,9 +81,11 @@ We use the ``http2_session_data`` structure to store session-level
|
||||
app_context *app_ctx;
|
||||
nghttp2_session *session;
|
||||
char *client_addr;
|
||||
size_t handshake_leftlen;
|
||||
} http2_session_data;
|
||||
|
||||
We use the ``http2_stream_data`` structure to store stream-level data::
|
||||
We use ``http2_stream_data`` structure to store the stream-level
|
||||
data::
|
||||
|
||||
typedef struct http2_stream_data {
|
||||
struct http2_stream_data *prev, *next;
|
||||
@@ -90,21 +94,26 @@ We use the ``http2_stream_data`` structure to store stream-level data::
|
||||
int fd;
|
||||
} http2_stream_data;
|
||||
|
||||
A single HTTP/2 session can have multiple streams. We manage these
|
||||
multiple streams with 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``. We use libevent's bufferevent
|
||||
structure to perform network I/O. 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 bufferevent in
|
||||
``http2_session_data``.
|
||||
1 HTTP/2.0 session can have multiple streams. We manage these
|
||||
multiple streams by intrusive doubly linked list to add and remove the
|
||||
object in O(1). The first element of this list is pointed by the
|
||||
``root->next`` in ``http2_session_data``. Initially, ``root->next``
|
||||
is ``NULL``. The ``handshake_leftlen`` member of
|
||||
``http2_session_data`` is used to track the number of bytes remaining
|
||||
when receiving first 24 bytes magic value
|
||||
(:macro:`NGHTTP2_CLIENT_CONNECTION_HEADER`) from the client. We use
|
||||
libevent's bufferevent structure to perform network I/O. Notice that
|
||||
bufferevent object is 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
|
||||
bufferevent in ``http2_session_data``.
|
||||
|
||||
We first create a listener object to accept incoming connections. We use
|
||||
libevent's ``struct evconnlistener`` for this purpose::
|
||||
We first create listener object to accept incoming connections.
|
||||
We use libevent's ``struct evconnlistener`` for this purpose::
|
||||
|
||||
static void start_listen(struct event_base *evbase, const char *service,
|
||||
app_context *app_ctx) {
|
||||
app_context *app_ctx)
|
||||
{
|
||||
int rv;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *res, *rp;
|
||||
@@ -115,7 +124,7 @@ libevent's ``struct evconnlistener`` for this purpose::
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
#ifdef AI_ADDRCONFIG
|
||||
hints.ai_flags |= AI_ADDRCONFIG;
|
||||
#endif /* AI_ADDRCONFIG */
|
||||
#endif // AI_ADDRCONFIG
|
||||
|
||||
rv = getaddrinfo(NULL, service, &hints, &res);
|
||||
if(rv != 0) {
|
||||
@@ -123,52 +132,44 @@ libevent's ``struct evconnlistener`` for this purpose::
|
||||
}
|
||||
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, rp->ai_addrlen);
|
||||
listener = evconnlistener_new_bind(evbase, acceptcb, app_ctx,
|
||||
LEV_OPT_CLOSE_ON_FREE |
|
||||
LEV_OPT_REUSEABLE, -1,
|
||||
rp->ai_addr, 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::
|
||||
We specify ``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) {
|
||||
static void acceptcb(struct evconnlistener *listener, 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);
|
||||
bufferevent_setcb(session_data->bev, handshake_readcb, NULL, eventcb,
|
||||
session_data);
|
||||
}
|
||||
|
||||
Here we create the ``http2_session_data`` object. The bufferevent for
|
||||
this connection is also initialized at this time. We specify three
|
||||
callbacks for the bufferevent: ``readcb``, ``writecb`` and
|
||||
``eventcb``.
|
||||
Here we create ``http2_session_data`` object. The bufferevent for this
|
||||
connection is also initialized at this time. We specify 2 callbacks
|
||||
for the bufferevent: ``handshake_readcb`` and ``eventcb``.
|
||||
|
||||
The ``eventcb()`` callback is invoked by the libevent event loop when an event
|
||||
The ``eventcb()`` is invoked by libevent event loop when an event
|
||||
(e.g., connection has been established, timeout, etc) happens on the
|
||||
underlying network socket::
|
||||
|
||||
static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(events & BEV_EVENT_CONNECTED) {
|
||||
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
||||
|
||||
initialize_nghttp2_session(session_data);
|
||||
|
||||
if (send_server_connection_header(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if(events & BEV_EVENT_EOF) {
|
||||
@@ -181,69 +182,87 @@ underlying network socket::
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
|
||||
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 thus also its bufferevent member.
|
||||
As a result, the underlying connection is closed. The
|
||||
For ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and ``BEV_EVENT_TIMEOUT``
|
||||
event, we just simply tear down the connection. The
|
||||
``delete_http2_session_data()`` function destroys
|
||||
``http2_session_data`` object and thus its bufferevent member. As a
|
||||
result, the underlying connection is closed. The
|
||||
``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is
|
||||
finished successfully. Now we are ready to start the HTTP/2
|
||||
communication.
|
||||
finished successfully.
|
||||
|
||||
We initialize a nghttp2 session object which is done in
|
||||
``initialize_nghttp2_session()``::
|
||||
The ``handshake_readcb()`` is a callback function to handle 24 bytes
|
||||
magic byte string from a client, since nghttp2 library does not handle
|
||||
it::
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_option *option;
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
static void handshake_readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
uint8_t data[24];
|
||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||
int readlen = evbuffer_remove(input, data, session_data->handshake_leftlen);
|
||||
const char *conhead = NGHTTP2_CLIENT_CONNECTION_HEADER;
|
||||
|
||||
nghttp2_option_new(&option);
|
||||
|
||||
/* Tells nghttp2_session object that it handles client connection
|
||||
preface */
|
||||
nghttp2_option_set_recv_client_preface(option, 1);
|
||||
|
||||
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_new2(&session_data->session, callbacks, session_data,
|
||||
option);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
nghttp2_option_del(option);
|
||||
if(memcmp(conhead + NGHTTP2_CLIENT_CONNECTION_HEADER_LEN
|
||||
- session_data->handshake_leftlen, data, readlen) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
session_data->handshake_leftlen -= readlen;
|
||||
if(session_data->handshake_leftlen == 0) {
|
||||
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, ptr);
|
||||
/* Process pending data in buffer since they are not notified
|
||||
further */
|
||||
initialize_nghttp2_session(session_data);
|
||||
if(send_server_connection_header(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(session_recv(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Since we are creating a server and uses options, the nghttp2 session
|
||||
object is created using `nghttp2_session_server_new2()` function. We
|
||||
registers five callbacks for nghttp2 session object. We'll talk about
|
||||
these callbacks later. Our server only speaks HTTP/2. In this case,
|
||||
we use `nghttp2_option_set_recv_client_preface()` to make
|
||||
:type:`nghttp2_session` object handle client connection preface, which
|
||||
saves some lines of application code.
|
||||
We check that the received byte string matches
|
||||
:macro:`NGHTTP2_CLIENT_CONNECTION_HEADER`. When they match, the
|
||||
connection state is ready for starting HTTP/2.0 communication. First
|
||||
we change the callback functions for the bufferevent object. We use
|
||||
same ``eventcb`` as before. But we specify new ``readcb`` and
|
||||
``writecb`` function to handle HTTP/2.0 communication. We describe
|
||||
these 2 functions later.
|
||||
|
||||
After initialization of the nghttp2 session object, we are going to send
|
||||
a server connection header in ``send_server_connection_header()``::
|
||||
We initialize nghttp2 session object which is done in
|
||||
``initialize_nghttp2_session()``::
|
||||
|
||||
static int send_server_connection_header(http2_session_data *session_data) {
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks = {0};
|
||||
|
||||
callbacks.send_callback = send_callback;
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||
callbacks.on_header_callback = on_header_callback;
|
||||
callbacks.on_begin_headers_callback = on_begin_headers_callback;
|
||||
nghttp2_session_server_new(&session_data->session, &callbacks, session_data);
|
||||
}
|
||||
|
||||
Since we are creating server, nghttp2 session object is created using
|
||||
`nghttp2_session_server_new()` function. We registers 5 callbacks to
|
||||
nghttp2 session object. We'll talk about these callbacks later.
|
||||
|
||||
After initialization of nghttp2 session object, we are going to send
|
||||
server connection header in ``send_server_connection_header()``::
|
||||
|
||||
static int send_server_connection_header(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_settings_entry iv[1] = {
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
||||
};
|
||||
int rv;
|
||||
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
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;
|
||||
@@ -251,49 +270,47 @@ a server connection header in ``send_server_connection_header()``::
|
||||
return 0;
|
||||
}
|
||||
|
||||
The server connection header is a SETTINGS frame. We specify
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS to 100 in the SETTINGS frame. To queue
|
||||
The server connection header is SETTINGS frame. We specify
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS to 100 in SETTINGS frame. To queue
|
||||
the SETTINGS frame for the transmission, we use
|
||||
`nghttp2_submit_settings()`. Note that `nghttp2_submit_settings()`
|
||||
function only queues the frame and it does not 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.
|
||||
function only queues the frame and not actually send it. All
|
||||
``nghttp2_submit_*()`` family functions have this property. To
|
||||
actually send the frame, `nghttp2_session_send()` is used, which is
|
||||
described about 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
|
||||
Since bufferevent may buffer more than first 24 bytes from the client,
|
||||
we have to process them here since libevent won't invoke callback
|
||||
functions for these pending data. To process received data, we call
|
||||
``session_recv()`` function::
|
||||
|
||||
static int session_recv(http2_session_data *session_data) {
|
||||
ssize_t readlen;
|
||||
static int session_recv(http2_session_data *session_data)
|
||||
{
|
||||
int rv;
|
||||
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, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
rv = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(rv < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
evbuffer_drain(input, rv);
|
||||
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 invoke the
|
||||
nghttp2 callbacks and also queue outgoing frames. Since there may be pending
|
||||
outgoing frames, we call ``session_send()`` function to send off those
|
||||
frames. The ``session_send()`` function is defined as follows::
|
||||
In this function, we feed all unprocessed, received data to nghttp2
|
||||
session object using `nghttp2_session_mem_recv()` function. The
|
||||
`nghttp2_session_mem_recv()` processes the received data and may
|
||||
invoke nghttp2 callbacks and also queue outgoing frames. Since there
|
||||
may be pending frames, we call ``session_send()`` function to send
|
||||
those frames. The ``session_send()`` function is defined as follows::
|
||||
|
||||
static int session_send(http2_session_data *session_data) {
|
||||
static int session_send(http2_session_data *session_data)
|
||||
{
|
||||
int rv;
|
||||
rv = nghttp2_session_send(session_data->session);
|
||||
if(rv != 0) {
|
||||
@@ -304,12 +321,16 @@ frames. The ``session_send()`` function is defined as follows::
|
||||
}
|
||||
|
||||
The `nghttp2_session_send()` function serializes the frame into wire
|
||||
format and calls ``send_callback()`` of type
|
||||
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
||||
follows::
|
||||
format and call :member:`nghttp2_session_callbacks.send_callback` with
|
||||
it. We set ``send_callback()`` function to
|
||||
:member:`nghttp2_session_callbacks.send_callback` in
|
||||
``initialize_nghttp2_session()`` function described earlier. It 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) {
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length,
|
||||
int flags, 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. */
|
||||
@@ -324,22 +345,24 @@ follows::
|
||||
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 ``write()`` system call
|
||||
in the ``send_callback()``, we would surely get ``EAGAIN`` or
|
||||
``EWOULDBLOCK`` back since the socket has limited send buffer. If that
|
||||
happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the
|
||||
nghttp2 library to stop sending further data. But when writing to the
|
||||
bufferevent, we have to regulate the amount data to get 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` to tell the library to stop
|
||||
calling send_callback.
|
||||
data to the non-blocking socket directly using ``write()`` system call
|
||||
in the :member:`nghttp2_session_callbacks.send_callback`, we will
|
||||
surely get ``EAGAIN`` or ``EWOULDBLOCK`` since the socket has limited
|
||||
send buffer. If that happens, we can return
|
||||
:macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the nghttp2 library to stop
|
||||
sending further data. But writing to the bufferevent, we have to
|
||||
regulate the amount data to be buffered by ourselves to avoid possible
|
||||
huge memory consumption. To achieve this, we check the size of output
|
||||
buffer and if it is more than or equal to
|
||||
``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop writing data and return
|
||||
:macro:`NGHTTP2_ERR_WOULDBLOCK` to tell the library to stop calling
|
||||
send_callback.
|
||||
|
||||
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) {
|
||||
static void readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(session_recv(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
@@ -350,10 +373,11 @@ data is available to read in the bufferevent input buffer::
|
||||
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::
|
||||
The third bufferevent callback is ``writecb()``, which is invoked when
|
||||
all data written in the bufferevent output buffer have been sent::
|
||||
|
||||
static void writecb(struct bufferevent *bev, void *ptr) {
|
||||
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;
|
||||
@@ -369,32 +393,34 @@ data in the bufferevent output buffer has been sent::
|
||||
}
|
||||
}
|
||||
|
||||
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 will tell whether the connection should be dropped or not. More
|
||||
specifically, if both `nghttp2_session_want_read()` and
|
||||
`nghttp2_session_want_write()` return 0, we have no business left in the
|
||||
connection. But since we are using bufferevent and its deferred callback
|
||||
option, the bufferevent output buffer may contain pending data when the
|
||||
``writecb()`` is called. To handle this, we check whether the output buffer is
|
||||
empty or not. If all these conditions are met, we drop connection.
|
||||
First we check whether we should drop connection or not. The nghttp2
|
||||
session object keeps track of reception and transmission of GOAWAY
|
||||
frame and other error conditions as well. Using these information,
|
||||
nghttp2 session object will tell whether the connection should be
|
||||
dropped or not. More specifically, both `nghttp2_session_want_read()`
|
||||
and `nghttp2_session_want_write()` return 0, we have no business in
|
||||
the connection. But since we are using bufferevent and its deferred
|
||||
callback option, the bufferevent output buffer may contain the pending
|
||||
data when the ``writecb()`` is called. To handle this situation, we
|
||||
also check whether the output buffer is empty or not. If 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.
|
||||
Otherwise, we call ``session_send()`` to process pending output
|
||||
data. Remember that in ``send_callback()``, we may not write all data
|
||||
to bufferevent to avoid excessive buffering. We continue process
|
||||
pending data when output buffer becomes empty.
|
||||
|
||||
We have already described the nghttp2 callback ``send_callback()``. Let's
|
||||
learn about the remaining nghttp2 callbacks we setup in
|
||||
We have already described about nghttp2 callback ``send_callback()``.
|
||||
Let's describe remaining nghttp2 callbacks we 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::
|
||||
The ``on_begin_headers_callback()`` function is invoked when reception
|
||||
of 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) {
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
|
||||
@@ -408,24 +434,25 @@ a header block in HEADERS or PUSH_PROMISE frame is started::
|
||||
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()` to get the
|
||||
object without searching through the doubly linked list.
|
||||
We only interested in HEADERS frame in this function. Since HEADERS
|
||||
frame has several roles in HTTP/2.0 protocol, we check that it is a
|
||||
request HEADERS, which opens new stream. If frame is request HEADERS,
|
||||
then we create ``http2_stream_data`` object to store stream related
|
||||
data. We associate created ``http2_stream_data`` object to the stream
|
||||
in nghttp2 session object using `nghttp2_set_stream_user_data()` in
|
||||
order to get the object without searching through 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
|
||||
In this example server, we want to serve files relative to the current
|
||||
working directory 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_) {
|
||||
const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
void *user_data)
|
||||
{
|
||||
http2_stream_data *stream_data;
|
||||
const char PATH[] = ":path";
|
||||
switch(frame->hd.type) {
|
||||
@@ -433,15 +460,14 @@ emitted via ``on_header_callback`` function, which is called after
|
||||
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) {
|
||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
||||
frame->hd.stream_id);
|
||||
if(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)
|
||||
;
|
||||
for(j = 0; j < valuelen && value[j] != '?'; ++j);
|
||||
stream_data->request_path = percent_decode(value, j);
|
||||
}
|
||||
break;
|
||||
@@ -449,16 +475,17 @@ emitted via ``on_header_callback`` function, which is called after
|
||||
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 ``:method`` header field and always treat the request as a
|
||||
GET request.
|
||||
We search ``:path`` header field in request headers and keep the
|
||||
requested path in ``http2_stream_data`` object. In this example
|
||||
program, we ignore ``:method`` header field and always treat the
|
||||
request as 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) {
|
||||
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) {
|
||||
@@ -466,8 +493,8 @@ fully received::
|
||||
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);
|
||||
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) {
|
||||
@@ -482,18 +509,19 @@ fully received::
|
||||
return 0;
|
||||
}
|
||||
|
||||
First we retrieve the ``http2_stream_data`` object associated with the stream
|
||||
in ``on_begin_headers_callback()``. It is done 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,
|
||||
which is done in ``error_reply()``. Otherwise, we open the requested file and
|
||||
send its content. We send the header field ``:status`` as a single response
|
||||
header.
|
||||
First we retrieve ``http2_stream_data`` object associated to the
|
||||
stream in ``on_begin_headers_callback()``. It is done using
|
||||
`nghttp2_session_get_stream_user_data()`. If the requested path cannot
|
||||
be served for some reasons (e.g., file is not found), we send 404
|
||||
response, which is done in ``error_reply()``. Otherwise, we open
|
||||
requested file and send its content. We send 1 header field
|
||||
``:status`` as a response header.
|
||||
|
||||
Sending the content of the file is done in ``send_response()`` function::
|
||||
Sending content of a file is done in ``send_response()`` function::
|
||||
|
||||
static int send_response(nghttp2_session *session, int32_t stream_id,
|
||||
nghttp2_nv *nva, size_t nvlen, int fd) {
|
||||
nghttp2_nv *nva, size_t nvlen, int fd)
|
||||
{
|
||||
int rv;
|
||||
nghttp2_data_provider data_prd;
|
||||
data_prd.source.fd = fd;
|
||||
@@ -507,56 +535,55 @@ Sending the content of the file is done in ``send_response()`` function::
|
||||
return 0;
|
||||
}
|
||||
|
||||
The nghttp2 library uses the :type:`nghttp2_data_provider` structure to
|
||||
The nghttp2 library uses :type:`nghttp2_data_provider` structure to
|
||||
send entity body to the remote peer. The ``source`` member of this
|
||||
structure is a union and it can be either void pointer or int which is
|
||||
intended to be used as file descriptor. In this example server, we use
|
||||
the file descriptor. We also set the ``file_read_callback()`` callback
|
||||
function to read the contents of the file::
|
||||
file descriptor. We also set ``file_read_callback()`` callback
|
||||
function to read content 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_) {
|
||||
static ssize_t file_read_callback
|
||||
(nghttp2_session *session, int32_t stream_id,
|
||||
uint8_t *buf, size_t length, int *eof,
|
||||
nghttp2_data_source *source, void *user_data)
|
||||
{
|
||||
int fd = source->fd;
|
||||
ssize_t r;
|
||||
while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
|
||||
;
|
||||
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;
|
||||
*eof = 1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
If an error happens 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, set
|
||||
the :macro:`NGHTTP2_DATA_FLAG_EOF` flag to ``*data_flags`` to tell the
|
||||
nghttp2 library that we have finished reading the file.
|
||||
If error happens while reading file, we return
|
||||
:macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. This tells the library
|
||||
to send RST_STREAM to the stream. When all data is read, set 1 to
|
||||
``*eof`` to tell the nghttp2 library that we have finished reading
|
||||
file.
|
||||
|
||||
The `nghttp2_submit_response()` function is used to send the response to the
|
||||
remote peer.
|
||||
The `nghttp2_submit_response()` is used to send 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) {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
We destroy the ``http2_stream_data`` object in this function since the stream
|
||||
is about to close and we no longer use that object.
|
||||
We destroy ``http2_stream_data`` object in this function since the
|
||||
stream is about to close and we no longer use that object.
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
.. include:: @top_srcdir@/doc/sources/tutorial-hpack.rst
|
||||
|
||||
deflate.c
|
||||
---------
|
||||
|
||||
.. literalinclude:: @top_srcdir@/examples/deflate.c
|
||||
6
examples/.gitignore
vendored
6
examples/.gitignore
vendored
@@ -1,9 +1,3 @@
|
||||
client
|
||||
libevent-client
|
||||
libevent-server
|
||||
deflate
|
||||
tiny-nghttpd
|
||||
asio-sv
|
||||
asio-sv2
|
||||
asio-cl
|
||||
asio-cl2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
# nghttp2 - HTTP/2.0 C Library
|
||||
|
||||
# Copyright (c) 2012 Tatsuhiro Tsujikawa
|
||||
|
||||
@@ -23,22 +23,22 @@
|
||||
|
||||
if ENABLE_EXAMPLES
|
||||
|
||||
AM_CFLAGS = $(WARNCFLAGS)
|
||||
AM_CPPFLAGS = \
|
||||
-Wall \
|
||||
-I$(top_srcdir)/lib/includes \
|
||||
-I$(top_builddir)/lib/includes \
|
||||
-I$(top_srcdir)/src/includes \
|
||||
-I$(top_srcdir)/third-party \
|
||||
@LIBEVENT_OPENSSL_CFLAGS@ \
|
||||
@OPENSSL_CFLAGS@ \
|
||||
@DEFS@
|
||||
LDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/third-party/libhttp-parser.la \
|
||||
AM_LDFLAGS = \
|
||||
@LIBEVENT_OPENSSL_LIBS@ \
|
||||
@OPENSSL_LIBS@
|
||||
LDADD = \
|
||||
$(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/third-party/libhttp-parser.la
|
||||
|
||||
noinst_PROGRAMS = client libevent-client libevent-server deflate
|
||||
noinst_PROGRAMS = client libevent-client libevent-server
|
||||
|
||||
client_SOURCES = client.c
|
||||
|
||||
@@ -46,50 +46,4 @@ libevent_client_SOURCES = libevent-client.c
|
||||
|
||||
libevent_server_SOURCES = libevent-server.c
|
||||
|
||||
deflate_SOURCES = deflate.c
|
||||
|
||||
if ENABLE_TINY_NGHTTPD
|
||||
|
||||
noinst_PROGRAMS += tiny-nghttpd
|
||||
|
||||
tiny_nghttpd_SOURCES = tiny-nghttpd.c
|
||||
|
||||
endif # ENABLE_TINY_NGHTTPD
|
||||
|
||||
if ENABLE_ASIO_LIB
|
||||
|
||||
noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2
|
||||
|
||||
# AM_CPPFLAGS must be placed first, so that header file (e.g.,
|
||||
# nghttp2/nghttp2.h) in this package is used rather than installed
|
||||
# one.
|
||||
ASIOCPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
|
||||
ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \
|
||||
$(top_builddir)/third-party/libhttp-parser.la \
|
||||
${BOOST_LDFLAGS} \
|
||||
${BOOST_ASIO_LIB} \
|
||||
${BOOST_THREAD_LIB} \
|
||||
${BOOST_SYSTEM_LIB} \
|
||||
@OPENSSL_LIBS@ \
|
||||
@APPLDFLAGS@
|
||||
|
||||
asio_sv_SOURCES = asio-sv.cc
|
||||
asio_sv_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_sv_LDADD = ${ASIOLDADD}
|
||||
|
||||
asio_sv2_SOURCES = asio-sv2.cc
|
||||
asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_sv2_LDADD = ${ASIOLDADD}
|
||||
|
||||
asio_cl_SOURCES = asio-cl.cc
|
||||
asio_cl_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_cl_LDADD = ${ASIOLDADD}
|
||||
|
||||
asio_cl2_SOURCES = asio-cl2.cc
|
||||
asio_cl2_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_cl2_LDADD = ${ASIOLDADD}
|
||||
|
||||
endif # ENABLE_ASIO_LIB
|
||||
|
||||
endif # ENABLE_EXAMPLES
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::client;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
if (argc < 2) {
|
||||
std::cerr << "Usage: asio-cl URI" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
boost::system::error_code ec;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
std::string uri = argv[1];
|
||||
std::string scheme, host, service;
|
||||
|
||||
if (host_service_from_uri(ec, scheme, host, service, uri)) {
|
||||
std::cerr << "error: bad URI: " << ec.message() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23);
|
||||
tls_ctx.set_default_verify_paths();
|
||||
// disabled to make development easier...
|
||||
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||
configure_tls_context(ec, tls_ctx);
|
||||
|
||||
auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service)
|
||||
: session(io_service, host, service);
|
||||
|
||||
sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) {
|
||||
boost::system::error_code ec;
|
||||
auto req = sess.submit(ec, "GET", uri);
|
||||
|
||||
if (ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
req->on_response([&sess](const response &res) {
|
||||
std::cerr << "HTTP/2 " << res.status_code() << std::endl;
|
||||
for (auto &kv : res.header()) {
|
||||
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
|
||||
res.on_data([&sess](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
|
||||
req->on_close([&sess](uint32_t error_code) { sess.shutdown(); });
|
||||
});
|
||||
|
||||
sess.on_error([](const boost::system::error_code &ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::client;
|
||||
|
||||
void print_header(const header_map &h) {
|
||||
for (auto &kv : h) {
|
||||
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
void print_header(const response &res) {
|
||||
std::cerr << "HTTP/2 " << res.status_code() << "\n";
|
||||
print_header(res.header());
|
||||
}
|
||||
|
||||
void print_header(const request &req) {
|
||||
auto &uri = req.uri();
|
||||
std::cerr << req.method() << " " << uri.scheme << "://" << uri.host
|
||||
<< uri.path;
|
||||
if (!uri.raw_query.empty()) {
|
||||
std::cerr << "?" << uri.raw_query;
|
||||
}
|
||||
std::cerr << " HTTP/2\n";
|
||||
print_header(req.header());
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
if (argc < 2) {
|
||||
std::cerr << "Usage: asio-cl URI" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
boost::system::error_code ec;
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
std::string uri = argv[1];
|
||||
std::string scheme, host, service;
|
||||
|
||||
if (host_service_from_uri(ec, scheme, host, service, uri)) {
|
||||
std::cerr << "error: bad URI: " << ec.message() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23);
|
||||
tls_ctx.set_default_verify_paths();
|
||||
// disabled to make development easier...
|
||||
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||
configure_tls_context(ec, tls_ctx);
|
||||
|
||||
auto sess = scheme == "https" ? session(io_service, tls_ctx, host, service)
|
||||
: session(io_service, host, service);
|
||||
|
||||
sess.on_connect([&sess, &uri](tcp::resolver::iterator endpoint_it) {
|
||||
std::cerr << "connected to " << (*endpoint_it).endpoint() << std::endl;
|
||||
boost::system::error_code ec;
|
||||
auto req = sess.submit(ec, "GET", uri, {{"cookie", {"foo=bar", true}}});
|
||||
if (ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
req->on_response([&sess, req](const response &res) {
|
||||
std::cerr << "response header was received" << std::endl;
|
||||
print_header(res);
|
||||
|
||||
res.on_data([&sess](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
|
||||
req->on_close([&sess](uint32_t error_code) {
|
||||
std::cerr << "request done with error_code=" << error_code << std::endl;
|
||||
});
|
||||
|
||||
req->on_push([](const request &push_req) {
|
||||
std::cerr << "push request was received" << std::endl;
|
||||
|
||||
print_header(push_req);
|
||||
|
||||
push_req.on_response([](const response &res) {
|
||||
std::cerr << "push response header was received" << std::endl;
|
||||
|
||||
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
sess.on_error([](const boost::system::error_code &ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2014 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.
|
||||
*/
|
||||
// We wrote this code based on the original code which has the
|
||||
// following license:
|
||||
//
|
||||
// main.cpp
|
||||
// ~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
// Check command line arguments.
|
||||
if (argc < 4) {
|
||||
std::cerr
|
||||
<< "Usage: asio-sv <address> <port> <threads> [<private-key-file> "
|
||||
<< "<cert-file>]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::system::error_code ec;
|
||||
|
||||
std::string addr = argv[1];
|
||||
std::string port = argv[2];
|
||||
std::size_t num_threads = std::stoi(argv[3]);
|
||||
|
||||
http2 server;
|
||||
|
||||
server.num_threads(num_threads);
|
||||
|
||||
server.handle("/", [](const request &req, const response &res) {
|
||||
res.write_head(200, {{"foo", {"bar"}}});
|
||||
res.end("hello, world\n");
|
||||
});
|
||||
server.handle("/secret/", [](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
res.end("under construction!\n");
|
||||
});
|
||||
server.handle("/push", [](const request &req, const response &res) {
|
||||
boost::system::error_code ec;
|
||||
auto push = res.push(ec, "GET", "/push/1");
|
||||
if (!ec) {
|
||||
push->write_head(200);
|
||||
push->end("server push FTW!\n");
|
||||
}
|
||||
|
||||
res.write_head(200);
|
||||
res.end("you'll receive server push!\n");
|
||||
});
|
||||
server.handle("/delay", [](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
|
||||
auto timer = std::make_shared<boost::asio::deadline_timer>(
|
||||
res.io_service(), boost::posix_time::seconds(3));
|
||||
auto closed = std::make_shared<bool>();
|
||||
|
||||
res.on_close([timer, closed](uint32_t error_code) {
|
||||
timer->cancel();
|
||||
*closed = true;
|
||||
});
|
||||
|
||||
timer->async_wait([&res, closed](const boost::system::error_code &ec) {
|
||||
if (ec || *closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
res.end("finally!\n");
|
||||
});
|
||||
});
|
||||
server.handle("/trailer", [](const request &req, const response &res) {
|
||||
// send trailer part.
|
||||
res.write_head(200, {{"trailers", {"digest"}}});
|
||||
|
||||
std::string body = "nghttp2 FTW!\n";
|
||||
auto left = std::make_shared<size_t>(body.size());
|
||||
|
||||
res.end([&res, body, left](uint8_t *dst, std::size_t len,
|
||||
uint32_t *data_flags) {
|
||||
auto n = std::min(len, *left);
|
||||
std::copy_n(body.c_str() + (body.size() - *left), n, dst);
|
||||
*left -= n;
|
||||
if (*left == 0) {
|
||||
*data_flags |=
|
||||
NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||
// RFC 3230 Instance Digests in HTTP. The digest value is
|
||||
// SHA-256 message digest of body.
|
||||
res.write_trailer(
|
||||
{{"digest",
|
||||
{"SHA-256=qqXqskW7F3ueBSvmZRCiSwl2ym4HRO0M/pvQCBlSDis="}}});
|
||||
}
|
||||
return n;
|
||||
});
|
||||
});
|
||||
|
||||
if (argc >= 6) {
|
||||
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||
tls.use_private_key_file(argv[4], boost::asio::ssl::context::pem);
|
||||
tls.use_certificate_chain_file(argv[5]);
|
||||
|
||||
configure_tls_context_easy(ec, tls);
|
||||
|
||||
if (server.listen_and_serve(ec, tls, addr, port)) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
} else {
|
||||
if (server.listen_and_serve(ec, addr, port)) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2014 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.
|
||||
*/
|
||||
// We wrote this code based on the original code which has the
|
||||
// following license:
|
||||
//
|
||||
// main.cpp
|
||||
// ~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif // HAVE_UNISTD_H
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif // HAVE_FCNTL_H
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
// Check command line arguments.
|
||||
if (argc < 5) {
|
||||
std::cerr << "Usage: asio-sv2 <address> <port> <threads> <doc-root> "
|
||||
<< "[<private-key-file> <cert-file>]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::system::error_code ec;
|
||||
|
||||
std::string addr = argv[1];
|
||||
std::string port = argv[2];
|
||||
std::size_t num_threads = std::stoi(argv[3]);
|
||||
std::string docroot = argv[4];
|
||||
|
||||
http2 server;
|
||||
|
||||
server.num_threads(num_threads);
|
||||
|
||||
server.handle("/", [&docroot](const request &req, const response &res) {
|
||||
auto path = percent_decode(req.uri().path);
|
||||
if (!check_path(path)) {
|
||||
res.write_head(404);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (path == "/") {
|
||||
path = "/index.html";
|
||||
}
|
||||
|
||||
path = docroot + path;
|
||||
auto fd = open(path.c_str(), O_RDONLY);
|
||||
if (fd == -1) {
|
||||
res.write_head(404);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
auto header = header_map();
|
||||
|
||||
struct stat stbuf;
|
||||
if (stat(path.c_str(), &stbuf) == 0) {
|
||||
header.emplace("content-length",
|
||||
header_value{std::to_string(stbuf.st_size)});
|
||||
header.emplace("last-modified",
|
||||
header_value{http_date(stbuf.st_mtime)});
|
||||
}
|
||||
res.write_head(200, std::move(header));
|
||||
res.end(file_generator_from_fd(fd));
|
||||
});
|
||||
|
||||
if (argc >= 7) {
|
||||
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
|
||||
tls.use_private_key_file(argv[5], boost::asio::ssl::context::pem);
|
||||
tls.use_certificate_chain_file(argv[6]);
|
||||
|
||||
configure_tls_context_easy(ec, tls);
|
||||
|
||||
if (server.listen_and_serve(ec, tls, addr, port)) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
} else {
|
||||
if (server.listen_and_serve(ec, addr, port)) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -26,28 +26,14 @@
|
||||
* This program is written to show how to use nghttp2 API in C and
|
||||
* intentionally made simple.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif /* HAVE_FCNTL_H */
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif /* HAVE_SYS_SOCKET_H */
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif /* HAVE_NETDB_H */
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
#include <netinet/tcp.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
@@ -58,21 +44,20 @@
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/conf.h>
|
||||
|
||||
enum { IO_NONE, WANT_READ, WANT_WRITE };
|
||||
enum {
|
||||
IO_NONE,
|
||||
WANT_READ,
|
||||
WANT_WRITE
|
||||
};
|
||||
|
||||
#define MAKE_NV(NAME, VALUE) \
|
||||
{ \
|
||||
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
{(uint8_t*)NAME, (uint8_t*)VALUE, \
|
||||
(uint16_t)(sizeof(NAME) - 1), (uint16_t)(sizeof(VALUE) - 1) }
|
||||
|
||||
#define MAKE_NV_CS(NAME, VALUE) \
|
||||
{ \
|
||||
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, strlen(VALUE), \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
{(uint8_t*)NAME, (uint8_t*)VALUE, \
|
||||
(uint16_t)(sizeof(NAME) - 1), (uint16_t)(strlen(VALUE)) }
|
||||
|
||||
struct Connection {
|
||||
SSL *ssl;
|
||||
@@ -87,6 +72,8 @@ struct Connection {
|
||||
};
|
||||
|
||||
struct Request {
|
||||
/* The gzip stream inflater for the compressed response. */
|
||||
nghttp2_gzip *inflater;
|
||||
char *host;
|
||||
/* In this program, path contains query component as well. */
|
||||
char *path;
|
||||
@@ -113,7 +100,8 @@ struct URI {
|
||||
* Returns copy of string |s| with the length |len|. The returned
|
||||
* string is NULL-terminated.
|
||||
*/
|
||||
static char *strcopy(const char *s, size_t len) {
|
||||
static char* strcopy(const char *s, size_t len)
|
||||
{
|
||||
char *dst;
|
||||
dst = malloc(len+1);
|
||||
memcpy(dst, s, len);
|
||||
@@ -124,7 +112,8 @@ static char *strcopy(const char *s, size_t len) {
|
||||
/*
|
||||
* Prints error message |msg| and exit.
|
||||
*/
|
||||
static void die(const char *msg) {
|
||||
static void die(const char *msg)
|
||||
{
|
||||
fprintf(stderr, "FATAL: %s\n", msg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -133,7 +122,8 @@ static void die(const char *msg) {
|
||||
* Prints error containing the function name |func| and message |msg|
|
||||
* and exit.
|
||||
*/
|
||||
static void dief(const char *func, const char *msg) {
|
||||
static void dief(const char *func, const char *msg)
|
||||
{
|
||||
fprintf(stderr, "FATAL: %s: %s\n", func, msg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -142,31 +132,64 @@ static void dief(const char *func, const char *msg) {
|
||||
* Prints error containing the function name |func| and error code
|
||||
* |error_code| and exit.
|
||||
*/
|
||||
static void diec(const char *func, int error_code) {
|
||||
static void diec(const char *func, int error_code)
|
||||
{
|
||||
fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
|
||||
nghttp2_strerror(error_code));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static char CONTENT_LENGTH[] = "content-encoding";
|
||||
static size_t CONTENT_LENGTH_LEN = sizeof(CONTENT_LENGTH) - 1;
|
||||
static char GZIP[] = "gzip";
|
||||
static size_t GZIP_LEN = sizeof(GZIP) - 1;
|
||||
|
||||
/*
|
||||
* Check response is content-encoding: gzip. We need this because
|
||||
* HTTP/2.0 client is required to support gzip.
|
||||
*/
|
||||
static void check_gzip(struct Request *req, nghttp2_nv *nva, size_t nvlen)
|
||||
{
|
||||
size_t i;
|
||||
if(req->inflater) {
|
||||
return;
|
||||
}
|
||||
for(i = 0; i < nvlen; ++i) {
|
||||
if(CONTENT_LENGTH_LEN == nva[i].namelen &&
|
||||
memcmp(CONTENT_LENGTH, nva[i].name, nva[i].namelen) == 0 &&
|
||||
GZIP_LEN == nva[i].valuelen &&
|
||||
memcmp(GZIP, nva[i].value, nva[i].valuelen) == 0) {
|
||||
int rv;
|
||||
rv = nghttp2_gzip_inflate_new(&req->inflater);
|
||||
if(rv != 0) {
|
||||
die("Can't allocate inflate stream.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The implementation of nghttp2_send_callback type. Here we write
|
||||
* |data| with size |length| to the network and return the number of
|
||||
* bytes actually written. See the documentation of
|
||||
* nghttp2_send_callback for the details.
|
||||
*/
|
||||
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length, int flags,
|
||||
void *user_data)
|
||||
{
|
||||
struct Connection *connection;
|
||||
int rv;
|
||||
ssize_t rv;
|
||||
connection = (struct Connection*)user_data;
|
||||
connection->want_io = IO_NONE;
|
||||
ERR_clear_error();
|
||||
rv = SSL_write(connection->ssl, data, (int)length);
|
||||
if (rv <= 0) {
|
||||
rv = SSL_write(connection->ssl, data, length);
|
||||
if(rv < 0) {
|
||||
int err = SSL_get_error(connection->ssl, rv);
|
||||
if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
connection->want_io =
|
||||
(err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
|
||||
connection->want_io = (err == SSL_ERROR_WANT_READ ?
|
||||
WANT_READ : WANT_WRITE);
|
||||
rv = NGHTTP2_ERR_WOULDBLOCK;
|
||||
} else {
|
||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
@@ -181,19 +204,21 @@ static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
* |length| bytes. Returns the number of bytes stored in |buf|. See
|
||||
* the documentation of nghttp2_recv_callback for the details.
|
||||
*/
|
||||
static ssize_t recv_callback(nghttp2_session *session _U_, uint8_t *buf,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
static ssize_t recv_callback(nghttp2_session *session,
|
||||
uint8_t *buf, size_t length, int flags,
|
||||
void *user_data)
|
||||
{
|
||||
struct Connection *connection;
|
||||
int rv;
|
||||
ssize_t rv;
|
||||
connection = (struct Connection*)user_data;
|
||||
connection->want_io = IO_NONE;
|
||||
ERR_clear_error();
|
||||
rv = SSL_read(connection->ssl, buf, (int)length);
|
||||
rv = SSL_read(connection->ssl, buf, length);
|
||||
if(rv < 0) {
|
||||
int err = SSL_get_error(connection->ssl, rv);
|
||||
if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
connection->want_io =
|
||||
(err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
|
||||
connection->want_io = (err == SSL_ERROR_WANT_READ ?
|
||||
WANT_READ : WANT_WRITE);
|
||||
rv = NGHTTP2_ERR_WOULDBLOCK;
|
||||
} else {
|
||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
@@ -204,9 +229,32 @@ static ssize_t recv_callback(nghttp2_session *session _U_, uint8_t *buf,
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int on_frame_send_callback(nghttp2_session *session,
|
||||
/*
|
||||
* The implementation of nghttp2_before_frame_send_callback type. We
|
||||
* use this function to get stream ID of the request. This is because
|
||||
* stream ID is not known when we submit the request
|
||||
* (nghttp2_submit_request).
|
||||
*/
|
||||
static int before_frame_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data _U_) {
|
||||
void *user_data)
|
||||
{
|
||||
if(frame->hd.type == NGHTTP2_HEADERS &&
|
||||
frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||
struct Request *req;
|
||||
int32_t stream_id = frame->hd.stream_id;
|
||||
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(req && req->stream_id == -1) {
|
||||
req->stream_id = stream_id;
|
||||
printf("[INFO] Stream ID = %d\n", stream_id);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_frame_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
size_t i;
|
||||
switch(frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
@@ -232,8 +280,8 @@ static int on_frame_send_callback(nghttp2_session *session,
|
||||
}
|
||||
|
||||
static int on_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data _U_) {
|
||||
const nghttp2_frame *frame, void *user_data)
|
||||
{
|
||||
size_t i;
|
||||
switch(frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
@@ -242,6 +290,7 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
||||
struct Request *req;
|
||||
req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||
if(req) {
|
||||
check_gzip(req, frame->headers.nva, frame->headers.nvlen);
|
||||
printf("[INFO] C <---------------------------- S (HEADERS)\n");
|
||||
for(i = 0; i < frame->headers.nvlen; ++i) {
|
||||
fwrite(nva[i].name, nva[i].namelen, 1, stdout);
|
||||
@@ -268,17 +317,19 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
||||
* fetch 1 resource in this program, after reception of the response,
|
||||
* we submit GOAWAY and close the session.
|
||||
*/
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code _U_,
|
||||
void *user_data _U_) {
|
||||
static int on_stream_close_callback(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
void *user_data)
|
||||
{
|
||||
struct Request *req;
|
||||
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(req) {
|
||||
int rv;
|
||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||
|
||||
rv = nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, NGHTTP2_NO_ERROR,
|
||||
NULL, 0);
|
||||
if(rv != 0) {
|
||||
diec("nghttp2_session_terminate_session", rv);
|
||||
diec("nghttp2_submit_goaway", rv);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -290,17 +341,35 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
* The implementation of nghttp2_on_data_chunk_recv_callback type. We
|
||||
* use this function to print the received response body.
|
||||
*/
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session,
|
||||
uint8_t flags _U_, int32_t stream_id,
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const uint8_t *data, size_t len,
|
||||
void *user_data _U_) {
|
||||
void *user_data)
|
||||
{
|
||||
struct Request *req;
|
||||
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(req) {
|
||||
printf("[INFO] C <---------------------------- S (DATA chunk)\n"
|
||||
"%lu bytes\n",
|
||||
(unsigned long int)len);
|
||||
"%lu bytes\n", (unsigned long int)len);
|
||||
if(req->inflater) {
|
||||
while(len > 0) {
|
||||
uint8_t out[MAX_OUTLEN];
|
||||
size_t outlen = MAX_OUTLEN;
|
||||
size_t tlen = len;
|
||||
int rv;
|
||||
rv = nghttp2_gzip_inflate(req->inflater, out, &outlen, data, &tlen);
|
||||
if(rv == -1) {
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
|
||||
NGHTTP2_INTERNAL_ERROR);
|
||||
break;
|
||||
}
|
||||
fwrite(out, 1, outlen, stdout);
|
||||
data += tlen;
|
||||
len -= tlen;
|
||||
}
|
||||
} else {
|
||||
fwrite(data, 1, len, stdout);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
return 0;
|
||||
@@ -312,38 +381,34 @@ static int on_data_chunk_recv_callback(nghttp2_session *session,
|
||||
* always required. Since we use nghttp2_session_recv(), the
|
||||
* recv_callback is also required.
|
||||
*/
|
||||
static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) {
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
|
||||
on_frame_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_data_chunk_recv_callback(
|
||||
callbacks, on_data_chunk_recv_callback);
|
||||
static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks)
|
||||
{
|
||||
memset(callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||
callbacks->send_callback = send_callback;
|
||||
callbacks->recv_callback = recv_callback;
|
||||
callbacks->before_frame_send_callback = before_frame_send_callback;
|
||||
callbacks->on_frame_send_callback = on_frame_send_callback;
|
||||
callbacks->on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks->on_stream_close_callback = on_stream_close_callback;
|
||||
callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback function for TLS NPN. Since this program only supports
|
||||
* HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2
|
||||
* HTTP/2.0 protocol, if server does not offer HTTP/2.0 the nghttp2
|
||||
* library supports, we terminate program.
|
||||
*/
|
||||
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_) {
|
||||
static int select_next_proto_cb(SSL* ssl,
|
||||
unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen,
|
||||
void *arg)
|
||||
{
|
||||
int rv;
|
||||
/* nghttp2_select_next_protocol() selects HTTP/2 protocol the
|
||||
/* nghttp2_select_next_protocol() selects HTTP/2.0 protocol the
|
||||
nghttp2 library supports. */
|
||||
rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
|
||||
if(rv <= 0) {
|
||||
die("Server did not advertise HTTP/2 protocol");
|
||||
die("Server did not advertise HTTP/2.0 protocol");
|
||||
}
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
@@ -351,7 +416,8 @@ static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
|
||||
/*
|
||||
* Setup SSL/TLS context.
|
||||
*/
|
||||
static void init_ssl_ctx(SSL_CTX *ssl_ctx) {
|
||||
static void init_ssl_ctx(SSL_CTX *ssl_ctx)
|
||||
{
|
||||
/* Disable SSLv2 and enable all workarounds for buggy servers */
|
||||
SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
|
||||
@@ -360,7 +426,8 @@ static void init_ssl_ctx(SSL_CTX *ssl_ctx) {
|
||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
||||
}
|
||||
|
||||
static void ssl_handshake(SSL *ssl, int fd) {
|
||||
static void ssl_handshake(SSL *ssl, int fd)
|
||||
{
|
||||
int rv;
|
||||
if(SSL_set_fd(ssl, fd) == 0) {
|
||||
dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
|
||||
@@ -376,7 +443,8 @@ static void ssl_handshake(SSL *ssl, int fd) {
|
||||
* Connects to the host |host| and port |port|. This function returns
|
||||
* the file descriptor of the client socket.
|
||||
*/
|
||||
static int connect_to(const char *host, uint16_t port) {
|
||||
static int connect_to(const char *host, uint16_t port)
|
||||
{
|
||||
struct addrinfo hints;
|
||||
int fd = -1;
|
||||
int rv;
|
||||
@@ -396,8 +464,7 @@ static int connect_to(const char *host, uint16_t port) {
|
||||
continue;
|
||||
}
|
||||
while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
|
||||
errno == EINTR)
|
||||
;
|
||||
errno == EINTR);
|
||||
if(rv == 0) {
|
||||
break;
|
||||
}
|
||||
@@ -408,21 +475,21 @@ static int connect_to(const char *host, uint16_t port) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void make_non_block(int fd) {
|
||||
static void make_non_block(int fd)
|
||||
{
|
||||
int flags, rv;
|
||||
while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR)
|
||||
;
|
||||
while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
|
||||
if(flags == -1) {
|
||||
dief("fcntl", strerror(errno));
|
||||
}
|
||||
while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR)
|
||||
;
|
||||
while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
|
||||
if(rv == -1) {
|
||||
dief("fcntl", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void set_tcp_nodelay(int fd) {
|
||||
static void set_tcp_nodelay(int fd)
|
||||
{
|
||||
int val = 1;
|
||||
int rv;
|
||||
rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
|
||||
@@ -434,7 +501,8 @@ static void set_tcp_nodelay(int fd) {
|
||||
/*
|
||||
* Update |pollfd| based on the state of |connection|.
|
||||
*/
|
||||
static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) {
|
||||
static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
|
||||
{
|
||||
pollfd->events = 0;
|
||||
if(nghttp2_session_want_read(connection->session) ||
|
||||
connection->want_io == WANT_READ) {
|
||||
@@ -451,31 +519,31 @@ static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) {
|
||||
* function does not send packets; just append the request to the
|
||||
* internal queue in |connection->session|.
|
||||
*/
|
||||
static void submit_request(struct Connection *connection, struct Request *req) {
|
||||
int32_t stream_id;
|
||||
static void submit_request(struct Connection *connection, struct Request *req)
|
||||
{
|
||||
int pri = 0;
|
||||
int rv;
|
||||
const nghttp2_nv nva[] = {
|
||||
/* Make sure that the last item is NULL */
|
||||
const nghttp2_nv nva[] = {MAKE_NV(":method", "GET"),
|
||||
MAKE_NV(":method", "GET"),
|
||||
MAKE_NV_CS(":path", req->path),
|
||||
MAKE_NV(":scheme", "https"),
|
||||
MAKE_NV_CS(":authority", req->hostport),
|
||||
MAKE_NV("accept", "*/*"),
|
||||
MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)};
|
||||
|
||||
stream_id = nghttp2_submit_request(connection->session, NULL, nva,
|
||||
sizeof(nva) / sizeof(nva[0]), NULL, req);
|
||||
|
||||
if (stream_id < 0) {
|
||||
diec("nghttp2_submit_request", stream_id);
|
||||
MAKE_NV("user-agent", "nghttp2/"NGHTTP2_VERSION)
|
||||
};
|
||||
rv = nghttp2_submit_request(connection->session, pri,
|
||||
nva, sizeof(nva)/sizeof(nva[0]), NULL, req);
|
||||
if(rv != 0) {
|
||||
diec("nghttp2_submit_request", rv);
|
||||
}
|
||||
|
||||
req->stream_id = stream_id;
|
||||
printf("[INFO] Stream ID = %d\n", stream_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs the network I/O.
|
||||
*/
|
||||
static void exec_io(struct Connection *connection) {
|
||||
static void exec_io(struct Connection *connection)
|
||||
{
|
||||
int rv;
|
||||
rv = nghttp2_session_recv(connection->session);
|
||||
if(rv != 0) {
|
||||
@@ -487,25 +555,30 @@ static void exec_io(struct Connection *connection) {
|
||||
}
|
||||
}
|
||||
|
||||
static void request_init(struct Request *req, const struct URI *uri) {
|
||||
static void request_init(struct Request *req, const struct URI *uri)
|
||||
{
|
||||
req->host = strcopy(uri->host, uri->hostlen);
|
||||
req->port = uri->port;
|
||||
req->path = strcopy(uri->path, uri->pathlen);
|
||||
req->hostport = strcopy(uri->hostport, uri->hostportlen);
|
||||
req->stream_id = -1;
|
||||
req->inflater = NULL;
|
||||
}
|
||||
|
||||
static void request_free(struct Request *req) {
|
||||
static void request_free(struct Request *req)
|
||||
{
|
||||
free(req->host);
|
||||
free(req->path);
|
||||
free(req->hostport);
|
||||
nghttp2_gzip_inflate_del(req->inflater);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetches the resource denoted by |uri|.
|
||||
*/
|
||||
static void fetch_uri(const struct URI *uri) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
static void fetch_uri(const struct URI *uri)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks;
|
||||
int fd;
|
||||
SSL_CTX *ssl_ctx;
|
||||
SSL *ssl;
|
||||
@@ -517,6 +590,8 @@ static void fetch_uri(const struct URI *uri) {
|
||||
|
||||
request_init(&req, uri);
|
||||
|
||||
setup_nghttp2_callbacks(&callbacks);
|
||||
|
||||
/* Establish connection and setup SSL */
|
||||
fd = connect_to(req.host, req.port);
|
||||
if(fd == -1) {
|
||||
@@ -539,37 +614,20 @@ static void fetch_uri(const struct URI *uri) {
|
||||
connection.want_io = IO_NONE;
|
||||
|
||||
/* Send connection header in blocking I/O mode */
|
||||
rv = SSL_write(ssl, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
if (rv <= 0) {
|
||||
dief("SSL_write failed: could not send connection preface",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_write(ssl, NGHTTP2_CLIENT_CONNECTION_HEADER,
|
||||
NGHTTP2_CLIENT_CONNECTION_HEADER_LEN);
|
||||
|
||||
/* Here make file descriptor non-block */
|
||||
make_non_block(fd);
|
||||
set_tcp_nodelay(fd);
|
||||
|
||||
printf("[INFO] SSL/TLS handshake completed\n");
|
||||
|
||||
rv = nghttp2_session_callbacks_new(&callbacks);
|
||||
|
||||
if (rv != 0) {
|
||||
diec("nghttp2_session_callbacks_new", rv);
|
||||
}
|
||||
|
||||
setup_nghttp2_callbacks(callbacks);
|
||||
|
||||
rv = nghttp2_session_client_new(&connection.session, callbacks, &connection);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
|
||||
rv = nghttp2_session_client_new(&connection.session, &callbacks,
|
||||
&connection);
|
||||
if(rv != 0) {
|
||||
diec("nghttp2_session_client_new", rv);
|
||||
}
|
||||
|
||||
nghttp2_submit_settings(connection.session, NGHTTP2_FLAG_NONE, NULL, 0);
|
||||
|
||||
/* Submit the HTTP request to the outbound queue. */
|
||||
submit_request(&connection, &req);
|
||||
|
||||
@@ -602,7 +660,8 @@ static void fetch_uri(const struct URI *uri) {
|
||||
request_free(&req);
|
||||
}
|
||||
|
||||
static int parse_uri(struct URI *res, const char *uri) {
|
||||
static int parse_uri(struct URI *res, const char *uri)
|
||||
{
|
||||
/* We only interested in https */
|
||||
size_t len, i, offset;
|
||||
int ipv6addr = 0;
|
||||
@@ -684,7 +743,8 @@ static int parse_uri(struct URI *res, const char *uri) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct URI uri;
|
||||
struct sigaction act;
|
||||
int rv;
|
||||
@@ -697,8 +757,6 @@ int main(int argc, char **argv) {
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, 0);
|
||||
|
||||
OPENSSL_config(NULL);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
|
||||
|
||||
@@ -1,206 +0,0 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2014 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* !HAVE_CONFIG_H */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#define MAKE_NV(K, V) \
|
||||
{ \
|
||||
(uint8_t *) K, (uint8_t *)V, sizeof(K) - 1, sizeof(V) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
|
||||
static void deflate(nghttp2_hd_deflater *deflater,
|
||||
nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva,
|
||||
size_t nvlen);
|
||||
|
||||
static int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
|
||||
size_t inlen, int final);
|
||||
|
||||
int main(int argc _U_, char **argv _U_) {
|
||||
int rv;
|
||||
nghttp2_hd_deflater *deflater;
|
||||
nghttp2_hd_inflater *inflater;
|
||||
/* Define 1st header set. This is looks like a HTTP request. */
|
||||
nghttp2_nv nva1[] = {
|
||||
MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"),
|
||||
MAKE_NV(":path", "/"), MAKE_NV("user-agent", "libnghttp2"),
|
||||
MAKE_NV("accept-encoding", "gzip, deflate")};
|
||||
/* Define 2nd header set */
|
||||
nghttp2_nv nva2[] = {MAKE_NV(":scheme", "https"),
|
||||
MAKE_NV(":authority", "example.org"),
|
||||
MAKE_NV(":path", "/stylesheet/style.css"),
|
||||
MAKE_NV("user-agent", "libnghttp2"),
|
||||
MAKE_NV("accept-encoding", "gzip, deflate"),
|
||||
MAKE_NV("referer", "https://example.org")};
|
||||
|
||||
rv = nghttp2_hd_deflate_new(&deflater, 4096);
|
||||
|
||||
if (rv != 0) {
|
||||
fprintf(stderr, "nghttp2_hd_deflate_init failed with error: %s\n",
|
||||
nghttp2_strerror(rv));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
rv = nghttp2_hd_inflate_new(&inflater);
|
||||
|
||||
if (rv != 0) {
|
||||
fprintf(stderr, "nghttp2_hd_inflate_init failed with error: %s\n",
|
||||
nghttp2_strerror(rv));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Encode and decode 1st header set */
|
||||
deflate(deflater, inflater, nva1, sizeof(nva1) / sizeof(nva1[0]));
|
||||
|
||||
/* Encode and decode 2nd header set, using differential encoding
|
||||
using state after encoding 1st header set. */
|
||||
deflate(deflater, inflater, nva2, sizeof(nva2) / sizeof(nva2[0]));
|
||||
|
||||
nghttp2_hd_inflate_del(inflater);
|
||||
nghttp2_hd_deflate_del(deflater);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void deflate(nghttp2_hd_deflater *deflater,
|
||||
nghttp2_hd_inflater *inflater, const nghttp2_nv *const nva,
|
||||
size_t nvlen) {
|
||||
ssize_t rv;
|
||||
uint8_t *buf;
|
||||
size_t buflen;
|
||||
size_t outlen;
|
||||
size_t i;
|
||||
size_t sum;
|
||||
|
||||
sum = 0;
|
||||
|
||||
for (i = 0; i < nvlen; ++i) {
|
||||
sum += nva[i].namelen + nva[i].valuelen;
|
||||
}
|
||||
|
||||
printf("Input (%zu byte(s)):\n\n", sum);
|
||||
|
||||
for (i = 0; i < nvlen; ++i) {
|
||||
fwrite(nva[i].name, nva[i].namelen, 1, stdout);
|
||||
printf(": ");
|
||||
fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
buflen = nghttp2_hd_deflate_bound(deflater, nva, nvlen);
|
||||
buf = malloc(buflen);
|
||||
|
||||
rv = nghttp2_hd_deflate_hd(deflater, buf, buflen, nva, nvlen);
|
||||
|
||||
if (rv < 0) {
|
||||
fprintf(stderr, "nghttp2_hd_deflate_hd() failed with error: %s\n",
|
||||
nghttp2_strerror((int)rv));
|
||||
|
||||
free(buf);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
outlen = rv;
|
||||
|
||||
printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n", outlen,
|
||||
sum == 0 ? 0 : (double)outlen / sum);
|
||||
|
||||
for (i = 0; i < outlen; ++i) {
|
||||
if ((i & 0x0fu) == 0) {
|
||||
printf("%08zX: ", i);
|
||||
}
|
||||
|
||||
printf("%02X ", buf[i]);
|
||||
|
||||
if (((i + 1) & 0x0fu) == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n\nInflate:\n\n");
|
||||
|
||||
/* We pass 1 to final parameter, because buf contains whole deflated
|
||||
header data. */
|
||||
rv = inflate_header_block(inflater, buf, outlen, 1);
|
||||
|
||||
if (rv != 0) {
|
||||
free(buf);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("\n-----------------------------------------------------------"
|
||||
"--------------------\n");
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
int inflate_header_block(nghttp2_hd_inflater *inflater, uint8_t *in,
|
||||
size_t inlen, int final) {
|
||||
ssize_t rv;
|
||||
|
||||
for (;;) {
|
||||
nghttp2_nv nv;
|
||||
int inflate_flags = 0;
|
||||
size_t proclen;
|
||||
|
||||
rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, in, inlen, final);
|
||||
|
||||
if (rv < 0) {
|
||||
fprintf(stderr, "inflate failed with error code %zd", rv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
proclen = rv;
|
||||
|
||||
in += proclen;
|
||||
inlen -= proclen;
|
||||
|
||||
if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
|
||||
fwrite(nv.name, nv.namelen, 1, stderr);
|
||||
fprintf(stderr, ": ");
|
||||
fwrite(nv.value, nv.valuelen, 1, stderr);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
|
||||
nghttp2_hd_inflate_end_headers(inflater);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -22,27 +22,16 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif /* HAVE_SYS_SOCKET_H */
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
#include <netinet/tcp.h>
|
||||
#include <err.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/conf.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <event2/event.h>
|
||||
@@ -56,11 +45,11 @@
|
||||
#define ARRLEN(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
typedef struct {
|
||||
/* The NULL-terminated URI string to retrieve. */
|
||||
/* The NULL-terminated URI string to retreive. */
|
||||
const char *uri;
|
||||
/* Parsed result of the |uri| */
|
||||
struct http_parser_url *u;
|
||||
/* The authority portion of the |uri|, not NULL-terminated */
|
||||
/* The authroity portion of the |uri|, not NULL-terminated */
|
||||
char *authority;
|
||||
/* The path portion of the |uri|, including query, not
|
||||
NULL-terminated */
|
||||
@@ -81,7 +70,8 @@ typedef struct {
|
||||
} http2_session_data;
|
||||
|
||||
static http2_stream_data* create_http2_stream_data(const char *uri,
|
||||
struct http_parser_url *u) {
|
||||
struct http_parser_url *u)
|
||||
{
|
||||
/* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */
|
||||
size_t extra = 7;
|
||||
http2_stream_data *stream_data = malloc(sizeof(http2_stream_data));
|
||||
@@ -92,8 +82,8 @@ static http2_stream_data *create_http2_stream_data(const char *uri,
|
||||
|
||||
stream_data->authoritylen = u->field_data[UF_HOST].len;
|
||||
stream_data->authority = malloc(stream_data->authoritylen + extra);
|
||||
memcpy(stream_data->authority, &uri[u->field_data[UF_HOST].off],
|
||||
u->field_data[UF_HOST].len);
|
||||
memcpy(stream_data->authority,
|
||||
&uri[u->field_data[UF_HOST].off], u->field_data[UF_HOST].len);
|
||||
if(u->field_set & (1 << UF_PORT)) {
|
||||
stream_data->authoritylen +=
|
||||
snprintf(stream_data->authority + u->field_data[UF_HOST].len, extra,
|
||||
@@ -111,8 +101,8 @@ static http2_stream_data *create_http2_stream_data(const char *uri,
|
||||
if(stream_data->pathlen > 0) {
|
||||
stream_data->path = malloc(stream_data->pathlen);
|
||||
if(u->field_set & (1 << UF_PATH)) {
|
||||
memcpy(stream_data->path, &uri[u->field_data[UF_PATH].off],
|
||||
u->field_data[UF_PATH].len);
|
||||
memcpy(stream_data->path,
|
||||
&uri[u->field_data[UF_PATH].off], u->field_data[UF_PATH].len);
|
||||
}
|
||||
if(u->field_set & (1 << UF_QUERY)) {
|
||||
memcpy(stream_data->path + u->field_data[UF_PATH].len + 1,
|
||||
@@ -124,15 +114,16 @@ static http2_stream_data *create_http2_stream_data(const char *uri,
|
||||
return stream_data;
|
||||
}
|
||||
|
||||
static void delete_http2_stream_data(http2_stream_data *stream_data) {
|
||||
static void delete_http2_stream_data(http2_stream_data *stream_data)
|
||||
{
|
||||
free(stream_data->path);
|
||||
free(stream_data->authority);
|
||||
free(stream_data);
|
||||
}
|
||||
|
||||
/* Initializes |session_data| */
|
||||
static http2_session_data *
|
||||
create_http2_session_data(struct event_base *evbase) {
|
||||
static http2_session_data *create_http2_session_data(struct event_base *evbase)
|
||||
{
|
||||
http2_session_data *session_data = malloc(sizeof(http2_session_data));
|
||||
|
||||
memset(session_data, 0, sizeof(http2_session_data));
|
||||
@@ -140,7 +131,8 @@ create_http2_session_data(struct event_base *evbase) {
|
||||
return session_data;
|
||||
}
|
||||
|
||||
static void delete_http2_session_data(http2_session_data *session_data) {
|
||||
static void delete_http2_session_data(http2_session_data *session_data)
|
||||
{
|
||||
SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
|
||||
|
||||
if(ssl) {
|
||||
@@ -159,8 +151,10 @@ static void delete_http2_session_data(http2_session_data *session_data) {
|
||||
free(session_data);
|
||||
}
|
||||
|
||||
static void print_header(FILE *f, const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen) {
|
||||
static void print_header(FILE *f,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen)
|
||||
{
|
||||
fwrite(name, namelen, 1, f);
|
||||
fprintf(f, ": ");
|
||||
fwrite(value, valuelen, 1, f);
|
||||
@@ -170,10 +164,13 @@ static void print_header(FILE *f, const uint8_t *name, size_t namelen,
|
||||
/* Print HTTP headers to |f|. Please note that this function does not
|
||||
take into account that header name and value are sequence of
|
||||
octets, therefore they may contain non-printable characters. */
|
||||
static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen) {
|
||||
static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen)
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i < nvlen; ++i) {
|
||||
print_header(f, nva[i].name, nva[i].namelen, nva[i].value, nva[i].valuelen);
|
||||
print_header(f,
|
||||
nva[i].name, nva[i].namelen,
|
||||
nva[i].value, nva[i].valuelen);
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
@@ -181,21 +178,46 @@ static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen) {
|
||||
/* nghttp2_send_callback. Here we transmit the |data|, |length| bytes,
|
||||
to the network. Because we are using libevent bufferevent, we just
|
||||
write those bytes into bufferevent buffer. */
|
||||
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length,
|
||||
int flags, 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 length;
|
||||
}
|
||||
|
||||
/* nghttp2_before_frame_send_callback: Called when nghttp2 library is
|
||||
about to send a frame. We use this callback to get stream ID of new
|
||||
stream. Since HEADERS in HTTP/2.0 has several roles, we check that
|
||||
it is a HTTP request HEADERS. */
|
||||
static int before_frame_send_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) {
|
||||
stream_data =
|
||||
(http2_stream_data*)nghttp2_session_get_stream_user_data
|
||||
(session, frame->hd.stream_id);
|
||||
if(stream_data == session_data->stream_data) {
|
||||
stream_data->stream_id = frame->hd.stream_id;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
||||
single header 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) {
|
||||
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,
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
switch(frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
@@ -211,9 +233,10 @@ static int on_header_callback(nghttp2_session *session _U_,
|
||||
|
||||
/* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets
|
||||
started to receive header block. */
|
||||
static int on_begin_headers_callback(nghttp2_session *session _U_,
|
||||
static int on_begin_headers_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data) {
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
switch(frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
@@ -229,8 +252,9 @@ static int on_begin_headers_callback(nghttp2_session *session _U_,
|
||||
|
||||
/* nghttp2_on_frame_recv_callback: Called when nghttp2 library
|
||||
received a complete frame from the remote peer. */
|
||||
static int on_frame_recv_callback(nghttp2_session *session _U_,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
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;
|
||||
switch(frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
@@ -248,10 +272,11 @@ static int on_frame_recv_callback(nghttp2_session *session _U_,
|
||||
is meant to the stream we initiated, print the received data in
|
||||
stdout, so that the user can redirect its output to the file
|
||||
easily. */
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
|
||||
uint8_t flags _U_, int32_t stream_id,
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const uint8_t *data, size_t len,
|
||||
void *user_data) {
|
||||
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);
|
||||
@@ -263,14 +288,17 @@ static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
|
||||
closed. This example program only deals with 1 HTTP request (1
|
||||
stream), if it is closed, we send GOAWAY and tear down the
|
||||
session */
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code, void *user_data) {
|
||||
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);
|
||||
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;
|
||||
@@ -280,11 +308,13 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
}
|
||||
|
||||
/* NPN TLS extension client callback. We check that server advertised
|
||||
the HTTP/2 protocol the nghttp2 library supports. If not, exit
|
||||
the HTTP/2.0 protocol the nghttp2 library supports. If not, exit
|
||||
the program. */
|
||||
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_) {
|
||||
static int select_next_proto_cb(SSL* ssl,
|
||||
unsigned char **out, unsigned char *outlen,
|
||||
const unsigned char *in, unsigned int inlen,
|
||||
void *arg)
|
||||
{
|
||||
if(nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
||||
}
|
||||
@@ -292,7 +322,8 @@ static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
|
||||
}
|
||||
|
||||
/* Create SSL_CTX. */
|
||||
static SSL_CTX *create_ssl_ctx(void) {
|
||||
static SSL_CTX* create_ssl_ctx(void)
|
||||
{
|
||||
SSL_CTX *ssl_ctx;
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if(!ssl_ctx) {
|
||||
@@ -300,15 +331,15 @@ static SSL_CTX *create_ssl_ctx(void) {
|
||||
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_ALL | SSL_OP_NO_SSLv2 | 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);
|
||||
return ssl_ctx;
|
||||
}
|
||||
|
||||
/* Create SSL object */
|
||||
static SSL *create_ssl(SSL_CTX *ssl_ctx) {
|
||||
static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
||||
{
|
||||
SSL *ssl;
|
||||
ssl = SSL_new(ssl_ctx);
|
||||
if(!ssl) {
|
||||
@@ -318,85 +349,70 @@ static SSL *create_ssl(SSL_CTX *ssl_ctx) {
|
||||
return ssl;
|
||||
}
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks = {0};
|
||||
|
||||
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);
|
||||
callbacks.send_callback = send_callback;
|
||||
callbacks.before_frame_send_callback = before_frame_send_callback;
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||
callbacks.on_header_callback = on_header_callback;
|
||||
callbacks.on_begin_headers_callback = on_begin_headers_callback;
|
||||
nghttp2_session_client_new(&session_data->session, &callbacks, session_data);
|
||||
}
|
||||
|
||||
static void send_client_connection_header(http2_session_data *session_data) {
|
||||
static void send_client_connection_header(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_settings_entry iv[1] = {
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
||||
};
|
||||
int rv;
|
||||
|
||||
bufferevent_write(session_data->bev, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
bufferevent_write(session_data->bev,
|
||||
NGHTTP2_CLIENT_CONNECTION_HEADER,
|
||||
NGHTTP2_CLIENT_CONNECTION_HEADER_LEN);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
#define MAKE_NV(NAME, VALUE, VALUELEN) \
|
||||
{ \
|
||||
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, VALUELEN, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, VALUELEN }
|
||||
|
||||
#define MAKE_NV2(NAME, VALUE) \
|
||||
{ \
|
||||
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1 }
|
||||
|
||||
/* Send HTTP request to the remote peer */
|
||||
static void submit_request(http2_session_data *session_data) {
|
||||
int32_t stream_id;
|
||||
static void submit_request(http2_session_data *session_data)
|
||||
{
|
||||
int rv;
|
||||
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(":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)};
|
||||
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));
|
||||
rv = nghttp2_submit_request(session_data->session, NGHTTP2_PRI_DEFAULT,
|
||||
hdrs, ARRLEN(hdrs), NULL, stream_data);
|
||||
if(rv != 0) {
|
||||
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(rv));
|
||||
}
|
||||
|
||||
stream_data->stream_id = stream_id;
|
||||
}
|
||||
|
||||
/* Serialize the frame and send (or buffer) the data to
|
||||
bufferevent. */
|
||||
static int session_send(http2_session_data *session_data) {
|
||||
static int session_send(http2_session_data *session_data)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = nghttp2_session_send(session_data->session);
|
||||
@@ -411,24 +427,20 @@ static int session_send(http2_session_data *session_data) {
|
||||
of bufferevent and feed them to nghttp2 library. This may invoke
|
||||
nghttp2 callbacks. It may also queues the frame in nghttp2 session
|
||||
context. To send them, we call session_send() in the end. */
|
||||
static void readcb(struct bufferevent *bev, void *ptr) {
|
||||
static void readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
ssize_t readlen;
|
||||
int rv;
|
||||
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, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
rv = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(rv < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
evbuffer_drain(input, rv);
|
||||
if(session_send(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
@@ -439,7 +451,8 @@ static void readcb(struct bufferevent *bev, void *ptr) {
|
||||
receiving GOAWAY, we check the some conditions on the nghttp2
|
||||
library and output buffer of bufferevent. If it indicates we have
|
||||
no business to this session, tear down the connection. */
|
||||
static void writecb(struct bufferevent *bev _U_, void *ptr) {
|
||||
static void writecb(struct bufferevent *bev, 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 &&
|
||||
@@ -453,7 +466,8 @@ static void writecb(struct bufferevent *bev _U_, void *ptr) {
|
||||
peer verification. After SSL/TLS handshake is over, initialize
|
||||
nghttp2 library session, and send client connection header. Then
|
||||
send HTTP request. */
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr) {
|
||||
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);
|
||||
@@ -479,17 +493,20 @@ static void eventcb(struct bufferevent *bev, short events, void *ptr) {
|
||||
}
|
||||
|
||||
/* Start connecting to the remote peer |host:port| */
|
||||
static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
|
||||
static void initiate_connection(struct event_base *evbase,
|
||||
SSL_CTX *ssl_ctx,
|
||||
const char *host, uint16_t port,
|
||||
http2_session_data *session_data) {
|
||||
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);
|
||||
bev = bufferevent_openssl_socket_new(evbase, -1, ssl,
|
||||
BUFFEREVENT_SSL_CONNECTING,
|
||||
BEV_OPT_DEFER_CALLBACKS |
|
||||
BEV_OPT_CLOSE_ON_FREE);
|
||||
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
||||
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
||||
AF_UNSPEC, host, port);
|
||||
@@ -502,7 +519,8 @@ static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
|
||||
|
||||
/* Get resource denoted by the |uri|. The debug and error messages are
|
||||
printed in stderr, while the response body is printed in stdout. */
|
||||
static void run(const char *uri) {
|
||||
static void run(const char *uri)
|
||||
{
|
||||
struct http_parser_url u;
|
||||
char *host;
|
||||
uint16_t port;
|
||||
@@ -540,7 +558,8 @@ static void run(const char *uri) {
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct sigaction act;
|
||||
|
||||
if(argc < 2) {
|
||||
@@ -552,8 +571,6 @@ int main(int argc, char **argv) {
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
|
||||
OPENSSL_config(NULL);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
@@ -22,35 +22,20 @@
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif /* HAVE_SYS_SOCKET_H */
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif /* HAVE_NETDB_H */
|
||||
#include <signal.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif /* HAVE_FCNTL_H */
|
||||
#include <ctype.h>
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
#include <netinet/tcp.h>
|
||||
#include <err.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/conf.h>
|
||||
|
||||
#include <event.h>
|
||||
#include <event2/event.h>
|
||||
@@ -64,10 +49,7 @@
|
||||
#define ARRLEN(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
#define MAKE_NV(NAME, VALUE) \
|
||||
{ \
|
||||
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
|
||||
NGHTTP2_NV_FLAG_NONE \
|
||||
}
|
||||
{ (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1 }
|
||||
|
||||
struct app_context;
|
||||
typedef struct app_context app_context;
|
||||
@@ -85,6 +67,7 @@ typedef struct http2_session_data {
|
||||
app_context *app_ctx;
|
||||
nghttp2_session *session;
|
||||
char *client_addr;
|
||||
size_t handshake_leftlen;
|
||||
} http2_session_data;
|
||||
|
||||
struct app_context {
|
||||
@@ -95,37 +78,29 @@ struct app_context {
|
||||
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_) {
|
||||
static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
|
||||
void *arg)
|
||||
{
|
||||
*data = next_proto_list;
|
||||
*len = (unsigned int)next_proto_list_len;
|
||||
*len = next_proto_list_len;
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
|
||||
/* Create SSL_CTX. */
|
||||
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||
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());
|
||||
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_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
|
||||
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (!ecdh) {
|
||||
errx(1, "EC_KEY_new_by_curv_name failed: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
|
||||
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) {
|
||||
if(SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file,
|
||||
SSL_FILETYPE_PEM) != 1) {
|
||||
errx(1, "Could not read private key file %s", key_file);
|
||||
}
|
||||
if(SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
|
||||
@@ -142,7 +117,8 @@ static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||
}
|
||||
|
||||
/* Create SSL object */
|
||||
static SSL *create_ssl(SSL_CTX *ssl_ctx) {
|
||||
static SSL* create_ssl(SSL_CTX *ssl_ctx)
|
||||
{
|
||||
SSL *ssl;
|
||||
ssl = SSL_new(ssl_ctx);
|
||||
if(!ssl) {
|
||||
@@ -153,7 +129,8 @@ static SSL *create_ssl(SSL_CTX *ssl_ctx) {
|
||||
}
|
||||
|
||||
static void add_stream(http2_session_data *session_data,
|
||||
http2_stream_data *stream_data) {
|
||||
http2_stream_data *stream_data)
|
||||
{
|
||||
stream_data->next = session_data->root.next;
|
||||
session_data->root.next = stream_data;
|
||||
stream_data->prev = &session_data->root;
|
||||
@@ -162,16 +139,18 @@ static void add_stream(http2_session_data *session_data,
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_stream(http2_session_data *session_data _U_,
|
||||
http2_stream_data *stream_data) {
|
||||
static void remove_stream(http2_session_data *session_data,
|
||||
http2_stream_data *stream_data)
|
||||
{
|
||||
stream_data->prev->next = stream_data->next;
|
||||
if(stream_data->next) {
|
||||
stream_data->next->prev = stream_data->prev;
|
||||
}
|
||||
}
|
||||
|
||||
static http2_stream_data *
|
||||
create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) {
|
||||
static http2_stream_data* create_http2_stream_data
|
||||
(http2_session_data *session_data, int32_t stream_id)
|
||||
{
|
||||
http2_stream_data *stream_data;
|
||||
stream_data = malloc(sizeof(http2_stream_data));
|
||||
memset(stream_data, 0, sizeof(http2_stream_data));
|
||||
@@ -182,7 +161,8 @@ create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) {
|
||||
return stream_data;
|
||||
}
|
||||
|
||||
static void delete_http2_stream_data(http2_stream_data *stream_data) {
|
||||
static void delete_http2_stream_data(http2_stream_data *stream_data)
|
||||
{
|
||||
if(stream_data->fd != -1) {
|
||||
close(stream_data->fd);
|
||||
}
|
||||
@@ -193,7 +173,8 @@ static void delete_http2_stream_data(http2_stream_data *stream_data) {
|
||||
static http2_session_data* create_http2_session_data(app_context *app_ctx,
|
||||
int fd,
|
||||
struct sockaddr *addr,
|
||||
int addrlen) {
|
||||
int addrlen)
|
||||
{
|
||||
int rv;
|
||||
http2_session_data *session_data;
|
||||
SSL *ssl;
|
||||
@@ -205,9 +186,11 @@ static http2_session_data *create_http2_session_data(app_context *app_ctx,
|
||||
memset(session_data, 0, sizeof(http2_session_data));
|
||||
session_data->app_ctx = app_ctx;
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
|
||||
session_data->bev = bufferevent_openssl_socket_new(
|
||||
app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
|
||||
session_data->bev = bufferevent_openssl_socket_new
|
||||
(app_ctx->evbase, fd, ssl,
|
||||
BUFFEREVENT_SSL_ACCEPTING,
|
||||
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
||||
session_data->handshake_leftlen = NGHTTP2_CLIENT_CONNECTION_HEADER_LEN;
|
||||
rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
|
||||
if(rv != 0) {
|
||||
session_data->client_addr = strdup("(unknown)");
|
||||
@@ -218,7 +201,8 @@ static http2_session_data *create_http2_session_data(app_context *app_ctx,
|
||||
return session_data;
|
||||
}
|
||||
|
||||
static void delete_http2_session_data(http2_session_data *session_data) {
|
||||
static void delete_http2_session_data(http2_session_data *session_data)
|
||||
{
|
||||
http2_stream_data *stream_data;
|
||||
SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
|
||||
fprintf(stderr, "%s disconnected\n", session_data->client_addr);
|
||||
@@ -238,7 +222,8 @@ static void delete_http2_session_data(http2_session_data *session_data) {
|
||||
|
||||
/* Serialize the frame and send (or buffer) the data to
|
||||
bufferevent. */
|
||||
static int session_send(http2_session_data *session_data) {
|
||||
static int session_send(http2_session_data *session_data)
|
||||
{
|
||||
int rv;
|
||||
rv = nghttp2_session_send(session_data->session);
|
||||
if(rv != 0) {
|
||||
@@ -252,29 +237,28 @@ static int session_send(http2_session_data *session_data) {
|
||||
function. Invocation of nghttp2_session_mem_recv() may make
|
||||
additional pending frames, so call session_send() at the end of the
|
||||
function. */
|
||||
static int session_recv(http2_session_data *session_data) {
|
||||
ssize_t readlen;
|
||||
static int session_recv(http2_session_data *session_data)
|
||||
{
|
||||
int rv;
|
||||
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, readlen) != 0) {
|
||||
warnx("Fatal error: evbuffer_drain failed");
|
||||
rv = nghttp2_session_mem_recv(session_data->session, data, datalen);
|
||||
if(rv < 0) {
|
||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||
return -1;
|
||||
}
|
||||
evbuffer_drain(input, rv);
|
||||
if(session_send(session_data) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
size_t length, int flags _U_, void *user_data) {
|
||||
static ssize_t send_callback(nghttp2_session *session,
|
||||
const uint8_t *data, size_t length,
|
||||
int flags, 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. */
|
||||
@@ -287,7 +271,8 @@ static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||
}
|
||||
|
||||
/* Returns nonzero if the string |s| ends with the substring |sub| */
|
||||
static int ends_with(const char *s, const char *sub) {
|
||||
static int ends_with(const char *s, const char *sub)
|
||||
{
|
||||
size_t slen = strlen(s);
|
||||
size_t sublen = strlen(sub);
|
||||
if(slen < sublen) {
|
||||
@@ -297,7 +282,8 @@ static int ends_with(const char *s, const char *sub) {
|
||||
}
|
||||
|
||||
/* Returns int value of hex string character |c| */
|
||||
static uint8_t hex_to_uint(uint8_t c) {
|
||||
static uint8_t hex_to_uint(uint8_t c)
|
||||
{
|
||||
if('0' <= c && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
@@ -314,15 +300,16 @@ static uint8_t hex_to_uint(uint8_t c) {
|
||||
and returns the decoded byte string in allocated buffer. The return
|
||||
value is NULL terminated. The caller must free the returned
|
||||
string. */
|
||||
static char *percent_decode(const uint8_t *value, size_t valuelen) {
|
||||
static char* percent_decode(const uint8_t *value, size_t valuelen)
|
||||
{
|
||||
char *res;
|
||||
|
||||
res = malloc(valuelen + 1);
|
||||
if(valuelen > 3) {
|
||||
size_t i, j;
|
||||
for(i = 0, j = 0; i < valuelen - 2;) {
|
||||
if (value[i] != '%' || !isxdigit(value[i + 1]) ||
|
||||
!isxdigit(value[i + 2])) {
|
||||
if(value[i] != '%' ||
|
||||
!isxdigit(value[i + 1]) || !isxdigit(value[i + 2])) {
|
||||
res[j++] = value[i++];
|
||||
continue;
|
||||
}
|
||||
@@ -338,26 +325,26 @@ static char *percent_decode(const uint8_t *value, size_t valuelen) {
|
||||
return res;
|
||||
}
|
||||
|
||||
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_) {
|
||||
static ssize_t file_read_callback
|
||||
(nghttp2_session *session, int32_t stream_id,
|
||||
uint8_t *buf, size_t length, int *eof,
|
||||
nghttp2_data_source *source, void *user_data)
|
||||
{
|
||||
int fd = source->fd;
|
||||
ssize_t r;
|
||||
while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
|
||||
;
|
||||
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;
|
||||
*eof = 1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int send_response(nghttp2_session *session, int32_t stream_id,
|
||||
nghttp2_nv *nva, size_t nvlen, int fd) {
|
||||
nghttp2_nv *nva, size_t nvlen, int fd)
|
||||
{
|
||||
int rv;
|
||||
nghttp2_data_provider data_prd;
|
||||
data_prd.source.fd = fd;
|
||||
@@ -375,11 +362,13 @@ const char ERROR_HTML[] = "<html><head><title>404</title></head>"
|
||||
"<body><h1>404 Not Found</h1></body></html>";
|
||||
|
||||
static int error_reply(nghttp2_session *session,
|
||||
http2_stream_data *stream_data) {
|
||||
http2_stream_data *stream_data)
|
||||
{
|
||||
int rv;
|
||||
ssize_t writelen;
|
||||
int pipefd[2];
|
||||
nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")};
|
||||
nghttp2_nv hdrs[] = {
|
||||
MAKE_NV(":status", "404")
|
||||
};
|
||||
|
||||
rv = pipe(pipefd);
|
||||
if(rv != 0) {
|
||||
@@ -393,17 +382,9 @@ static int error_reply(nghttp2_session *session,
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
|
||||
write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
|
||||
close(pipefd[1]);
|
||||
|
||||
if (writelen != sizeof(ERROR_HTML) - 1) {
|
||||
close(pipefd[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stream_data->fd = pipefd[0];
|
||||
|
||||
if(send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
|
||||
pipefd[0]) != 0) {
|
||||
close(pipefd[0]);
|
||||
@@ -415,10 +396,11 @@ static int error_reply(nghttp2_session *session,
|
||||
/* nghttp2_on_header_callback: Called when nghttp2 library emits
|
||||
single header name/value pair. */
|
||||
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_) {
|
||||
const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
void *user_data)
|
||||
{
|
||||
http2_stream_data *stream_data;
|
||||
const char PATH[] = ":path";
|
||||
switch(frame->hd.type) {
|
||||
@@ -426,15 +408,14 @@ static int on_header_callback(nghttp2_session *session,
|
||||
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) {
|
||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
||||
frame->hd.stream_id);
|
||||
if(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)
|
||||
;
|
||||
for(j = 0; j < valuelen && value[j] != '?'; ++j);
|
||||
stream_data->request_path = percent_decode(value, j);
|
||||
}
|
||||
break;
|
||||
@@ -444,7 +425,8 @@ static int on_header_callback(nghttp2_session *session,
|
||||
|
||||
static int on_begin_headers_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data) {
|
||||
void *user_data)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)user_data;
|
||||
http2_stream_data *stream_data;
|
||||
|
||||
@@ -460,18 +442,24 @@ static int on_begin_headers_callback(nghttp2_session *session,
|
||||
|
||||
/* Minimum check for directory traversal. Returns nonzero if it is
|
||||
safe. */
|
||||
static int check_path(const char *path) {
|
||||
static int check_path(const char *path)
|
||||
{
|
||||
/* We don't like '\' in url. */
|
||||
return path[0] && path[0] == '/' && strchr(path, '\\') == NULL &&
|
||||
strstr(path, "/../") == NULL && strstr(path, "/./") == NULL &&
|
||||
return path[0] && path[0] == '/' &&
|
||||
strchr(path, '\\') == NULL &&
|
||||
strstr(path, "/../") == NULL &&
|
||||
strstr(path, "/./") == NULL &&
|
||||
!ends_with(path, "/..") && !ends_with(path, "/.");
|
||||
}
|
||||
|
||||
static int on_request_recv(nghttp2_session *session,
|
||||
http2_session_data *session_data,
|
||||
http2_stream_data *stream_data) {
|
||||
http2_stream_data *stream_data)
|
||||
{
|
||||
int fd;
|
||||
nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")};
|
||||
nghttp2_nv hdrs[] = {
|
||||
MAKE_NV(":status", "200")
|
||||
};
|
||||
char *rel_path;
|
||||
|
||||
if(!stream_data->request_path) {
|
||||
@@ -488,8 +476,7 @@ static int on_request_recv(nghttp2_session *session,
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path)
|
||||
;
|
||||
for(rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path);
|
||||
fd = open(rel_path, O_RDONLY);
|
||||
if(fd == -1) {
|
||||
if(error_reply(session, stream_data) != 0) {
|
||||
@@ -499,8 +486,8 @@ static int on_request_recv(nghttp2_session *session,
|
||||
}
|
||||
stream_data->fd = fd;
|
||||
|
||||
if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) !=
|
||||
0) {
|
||||
if(send_response(session, stream_data->stream_id, hdrs,
|
||||
ARRLEN(hdrs), fd) != 0) {
|
||||
close(fd);
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
@@ -508,7 +495,8 @@ static int on_request_recv(nghttp2_session *session,
|
||||
}
|
||||
|
||||
static int on_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
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) {
|
||||
@@ -516,8 +504,8 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
||||
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);
|
||||
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) {
|
||||
@@ -532,62 +520,43 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code _U_, void *user_data) {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||
nghttp2_option *option;
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_session_callbacks callbacks = {0};
|
||||
|
||||
nghttp2_option_new(&option);
|
||||
|
||||
/* Tells nghttp2_session object that it handles client connection
|
||||
preface */
|
||||
nghttp2_option_set_recv_client_preface(option, 1);
|
||||
|
||||
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_new2(&session_data->session, callbacks, session_data,
|
||||
option);
|
||||
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
nghttp2_option_del(option);
|
||||
callbacks.send_callback = send_callback;
|
||||
callbacks.on_frame_recv_callback = on_frame_recv_callback;
|
||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||
callbacks.on_header_callback = on_header_callback;
|
||||
callbacks.on_begin_headers_callback = on_begin_headers_callback;
|
||||
nghttp2_session_server_new(&session_data->session, &callbacks, session_data);
|
||||
}
|
||||
|
||||
/* Send HTTP/2 client connection header, which includes 24 bytes
|
||||
/* Send HTTP/2.0 client connection header, which includes 24 bytes
|
||||
magic octets and SETTINGS frame */
|
||||
static int send_server_connection_header(http2_session_data *session_data) {
|
||||
static int send_server_connection_header(http2_session_data *session_data)
|
||||
{
|
||||
nghttp2_settings_entry iv[1] = {
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
||||
};
|
||||
int rv;
|
||||
|
||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||
ARRLEN(iv));
|
||||
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;
|
||||
@@ -597,7 +566,8 @@ static int send_server_connection_header(http2_session_data *session_data) {
|
||||
|
||||
/* readcb for bufferevent after client connection header was
|
||||
checked. */
|
||||
static void readcb(struct bufferevent *bev _U_, void *ptr) {
|
||||
static void readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(session_recv(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
@@ -613,7 +583,8 @@ static void readcb(struct bufferevent *bev _U_, void *ptr) {
|
||||
process pending data in the output buffer. This is necessary
|
||||
because we have a threshold on the buffer size to avoid too much
|
||||
buffering. See send_callback(). */
|
||||
static void writecb(struct bufferevent *bev, void *ptr) {
|
||||
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;
|
||||
@@ -630,18 +601,11 @@ static void writecb(struct bufferevent *bev, void *ptr) {
|
||||
}
|
||||
|
||||
/* eventcb for bufferevent */
|
||||
static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
if(events & BEV_EVENT_CONNECTED) {
|
||||
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
||||
|
||||
initialize_nghttp2_session(session_data);
|
||||
|
||||
if (send_server_connection_header(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if(events & BEV_EVENT_EOF) {
|
||||
@@ -654,19 +618,53 @@ static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
||||
delete_http2_session_data(session_data);
|
||||
}
|
||||
|
||||
/* readcb for bufferevent to check first 24 bytes client connection
|
||||
header. */
|
||||
static void handshake_readcb(struct bufferevent *bev, void *ptr)
|
||||
{
|
||||
http2_session_data *session_data = (http2_session_data*)ptr;
|
||||
uint8_t data[24];
|
||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||
int readlen = evbuffer_remove(input, data, session_data->handshake_leftlen);
|
||||
const char *conhead = NGHTTP2_CLIENT_CONNECTION_HEADER;
|
||||
|
||||
if(memcmp(conhead + NGHTTP2_CLIENT_CONNECTION_HEADER_LEN
|
||||
- session_data->handshake_leftlen, data, readlen) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
session_data->handshake_leftlen -= readlen;
|
||||
if(session_data->handshake_leftlen == 0) {
|
||||
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, ptr);
|
||||
/* Process pending data in buffer since they are not notified
|
||||
further */
|
||||
initialize_nghttp2_session(session_data);
|
||||
if(send_server_connection_header(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
if(session_recv(session_data) != 0) {
|
||||
delete_http2_session_data(session_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* callback for evconnlistener */
|
||||
static void acceptcb(struct evconnlistener *listener _U_, int fd,
|
||||
struct sockaddr *addr, int addrlen, void *arg) {
|
||||
static void acceptcb(struct evconnlistener *listener, 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);
|
||||
bufferevent_setcb(session_data->bev, handshake_readcb, NULL, eventcb,
|
||||
session_data);
|
||||
}
|
||||
|
||||
static void start_listen(struct event_base *evbase, const char *service,
|
||||
app_context *app_ctx) {
|
||||
app_context *app_ctx)
|
||||
{
|
||||
int rv;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *res, *rp;
|
||||
@@ -677,7 +675,7 @@ static void start_listen(struct event_base *evbase, const char *service,
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
#ifdef AI_ADDRCONFIG
|
||||
hints.ai_flags |= AI_ADDRCONFIG;
|
||||
#endif /* AI_ADDRCONFIG */
|
||||
#endif // AI_ADDRCONFIG
|
||||
|
||||
rv = getaddrinfo(NULL, service, &hints, &res);
|
||||
if(rv != 0) {
|
||||
@@ -685,12 +683,11 @@ static void start_listen(struct event_base *evbase, const char *service,
|
||||
}
|
||||
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, rp->ai_addrlen);
|
||||
listener = evconnlistener_new_bind(evbase, acceptcb, app_ctx,
|
||||
LEV_OPT_CLOSE_ON_FREE |
|
||||
LEV_OPT_REUSEABLE, 16,
|
||||
rp->ai_addr, rp->ai_addrlen);
|
||||
if(listener) {
|
||||
freeaddrinfo(res);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -698,14 +695,16 @@ static void start_listen(struct event_base *evbase, const char *service,
|
||||
}
|
||||
|
||||
static void initialize_app_context(app_context *app_ctx, SSL_CTX *ssl_ctx,
|
||||
struct event_base *evbase) {
|
||||
struct event_base *evbase)
|
||||
{
|
||||
memset(app_ctx, 0, sizeof(app_context));
|
||||
app_ctx->ssl_ctx = ssl_ctx;
|
||||
app_ctx->evbase = evbase;
|
||||
}
|
||||
|
||||
static void run(const char *service, const char *key_file,
|
||||
const char *cert_file) {
|
||||
static void run(const char *service,
|
||||
const char *key_file, const char *cert_file)
|
||||
{
|
||||
SSL_CTX *ssl_ctx;
|
||||
app_context app_ctx;
|
||||
struct event_base *evbase;
|
||||
@@ -721,7 +720,8 @@ static void run(const char *service, const char *key_file,
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct sigaction act;
|
||||
|
||||
if(argc < 4) {
|
||||
@@ -733,8 +733,6 @@ int main(int argc, char **argv) {
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
|
||||
OPENSSL_config(NULL);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
|
||||
def name(i):
|
||||
if i < 0x20:
|
||||
return \
|
||||
['NUL ', 'SOH ', 'STX ', 'ETX ', 'EOT ', 'ENQ ', 'ACK ', 'BEL ',
|
||||
'BS ', 'HT ', 'LF ', 'VT ', 'FF ', 'CR ', 'SO ', 'SI ',
|
||||
'DLE ', 'DC1 ', 'DC2 ', 'DC3 ', 'DC4 ', 'NAK ', 'SYN ', 'ETB ',
|
||||
'CAN ', 'EM ', 'SUB ', 'ESC ', 'FS ', 'GS ', 'RS ', 'US '][i]
|
||||
elif i == 0x7f:
|
||||
return 'DEL '
|
||||
|
||||
for i in range(256):
|
||||
if chr(i) == ' ':
|
||||
sys.stdout.write('{} /* SPC */, '.format(i))
|
||||
elif chr(i) == '\t':
|
||||
sys.stdout.write('{} /* HT */, '.format(i))
|
||||
elif 'A' <= chr(i) and chr(i) <= 'Z':
|
||||
sys.stdout.write('{} /* {} */, '.format(i - ord('A') + ord('a'), chr(i)))
|
||||
elif (0x21 <= i and i < 0x7f):
|
||||
sys.stdout.write('{} /* {} */, '.format(i, chr(i)))
|
||||
elif 0x80 <= i:
|
||||
sys.stdout.write('{} /* {} */, '.format(i, hex(i)))
|
||||
elif 0 == i:
|
||||
sys.stdout.write('{} /* NUL */, '.format(i))
|
||||
else:
|
||||
sys.stdout.write('{} /* {} */, '.format(i, name(i)))
|
||||
if (i + 1)%4 == 0:
|
||||
sys.stdout.write('\n')
|
||||
103
genheaderfunc.py
103
genheaderfunc.py
@@ -1,103 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
HEADERS = [
|
||||
':authority',
|
||||
':method',
|
||||
':path',
|
||||
':scheme',
|
||||
':status',
|
||||
':host', # for spdy
|
||||
'expect',
|
||||
'host',
|
||||
'if-modified-since',
|
||||
"te",
|
||||
"cookie",
|
||||
"http2-settings",
|
||||
"server",
|
||||
"via",
|
||||
"x-forwarded-for",
|
||||
"x-forwarded-proto",
|
||||
"alt-svc",
|
||||
"content-length",
|
||||
"location",
|
||||
"trailer",
|
||||
"link",
|
||||
"accept-encoding",
|
||||
"accept-language",
|
||||
"cache-control",
|
||||
"user-agent",
|
||||
# disallowed h1 headers
|
||||
'connection',
|
||||
'keep-alive',
|
||||
'proxy-connection',
|
||||
'transfer-encoding',
|
||||
'upgrade'
|
||||
]
|
||||
|
||||
def to_enum_hd(k):
|
||||
res = 'HD_'
|
||||
for c in k.upper():
|
||||
if c == ':' or c == '-':
|
||||
res += '_'
|
||||
continue
|
||||
res += c
|
||||
return res
|
||||
|
||||
def build_header(headers):
|
||||
res = {}
|
||||
for k in headers:
|
||||
size = len(k)
|
||||
if size not in res:
|
||||
res[size] = {}
|
||||
ent = res[size]
|
||||
c = k[-1]
|
||||
if c not in ent:
|
||||
ent[c] = []
|
||||
ent[c].append(k)
|
||||
|
||||
return res
|
||||
|
||||
def gen_enum():
|
||||
print '''\
|
||||
enum {'''
|
||||
for k in sorted(HEADERS):
|
||||
print '''\
|
||||
{},'''.format(to_enum_hd(k))
|
||||
print '''\
|
||||
HD_MAXIDX,
|
||||
};'''
|
||||
|
||||
def gen_index_header():
|
||||
print '''\
|
||||
int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {'''
|
||||
b = build_header(HEADERS)
|
||||
for size in sorted(b.keys()):
|
||||
ents = b[size]
|
||||
print '''\
|
||||
case {}:'''.format(size)
|
||||
print '''\
|
||||
switch (name[{}]) {{'''.format(size - 1)
|
||||
for c in sorted(ents.keys()):
|
||||
headers = sorted(ents[c])
|
||||
print '''\
|
||||
case '{}':'''.format(c)
|
||||
for k in headers:
|
||||
print '''\
|
||||
if (util::streq_l("{}", name, {})) {{
|
||||
return {};
|
||||
}}'''.format(k[:-1], size - 1, to_enum_hd(k))
|
||||
print '''\
|
||||
break;'''
|
||||
print '''\
|
||||
}
|
||||
break;'''
|
||||
print '''\
|
||||
}
|
||||
return -1;
|
||||
}'''
|
||||
|
||||
if __name__ == '__main__':
|
||||
gen_enum()
|
||||
print ''
|
||||
gen_index_header()
|
||||
@@ -1,140 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
HEADERS = [
|
||||
(':authority', 0),
|
||||
(':method', 1),
|
||||
(':method', 2),
|
||||
(':path', 3),
|
||||
(':path', 4),
|
||||
(':scheme', 5),
|
||||
(':scheme', 6),
|
||||
(':status', 7),
|
||||
(':status', 8),
|
||||
(':status', 9),
|
||||
(':status', 10),
|
||||
(':status', 11),
|
||||
(':status', 12),
|
||||
(':status', 13),
|
||||
('accept-charset', 14),
|
||||
('accept-encoding', 15),
|
||||
('accept-language', 16),
|
||||
('accept-ranges', 17),
|
||||
('accept', 18),
|
||||
('access-control-allow-origin', 19),
|
||||
('age', 20),
|
||||
('allow', 21),
|
||||
('authorization', 22),
|
||||
('cache-control', 23),
|
||||
('content-disposition', 24),
|
||||
('content-encoding', 25),
|
||||
('content-language', 26),
|
||||
('content-length', 27),
|
||||
('content-location', 28),
|
||||
('content-range', 29),
|
||||
('content-type', 30),
|
||||
('cookie', 31),
|
||||
('date', 32),
|
||||
('etag', 33),
|
||||
('expect', 34),
|
||||
('expires', 35),
|
||||
('from', 36),
|
||||
('host', 37),
|
||||
('if-match', 38),
|
||||
('if-modified-since', 39),
|
||||
('if-none-match', 40),
|
||||
('if-range', 41),
|
||||
('if-unmodified-since', 42),
|
||||
('last-modified', 43),
|
||||
('link', 44),
|
||||
('location', 45),
|
||||
('max-forwards', 46),
|
||||
('proxy-authenticate', 47),
|
||||
('proxy-authorization', 48),
|
||||
('range', 49),
|
||||
('referer', 50),
|
||||
('refresh', 51),
|
||||
('retry-after', 52),
|
||||
('server', 53),
|
||||
('set-cookie', 54),
|
||||
('strict-transport-security', 55),
|
||||
('transfer-encoding', 56),
|
||||
('user-agent', 57),
|
||||
('vary', 58),
|
||||
('via', 59),
|
||||
('www-authenticate', 60),
|
||||
('te', None),
|
||||
('connection', None),
|
||||
('keep-alive',None),
|
||||
('proxy-connection', None),
|
||||
('upgrade', None),
|
||||
]
|
||||
|
||||
def to_enum_hd(k):
|
||||
res = 'NGHTTP2_TOKEN_'
|
||||
for c in k.upper():
|
||||
if c == ':' or c == '-':
|
||||
res += '_'
|
||||
continue
|
||||
res += c
|
||||
return res
|
||||
|
||||
def build_header(headers):
|
||||
res = {}
|
||||
for k, _ in headers:
|
||||
size = len(k)
|
||||
if size not in res:
|
||||
res[size] = {}
|
||||
ent = res[size]
|
||||
c = k[-1]
|
||||
if c not in ent:
|
||||
ent[c] = []
|
||||
ent[c].append(k)
|
||||
|
||||
return res
|
||||
|
||||
def gen_enum():
|
||||
name = ''
|
||||
print 'typedef enum {'
|
||||
for k, token in HEADERS:
|
||||
if token is None:
|
||||
print ' {},'.format(to_enum_hd(k))
|
||||
else:
|
||||
if name != k:
|
||||
name = k
|
||||
print ' {} = {},'.format(to_enum_hd(k), token)
|
||||
print '} nghttp2_token;'
|
||||
|
||||
def gen_index_header():
|
||||
print '''\
|
||||
static inline int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {'''
|
||||
b = build_header(HEADERS)
|
||||
for size in sorted(b.keys()):
|
||||
ents = b[size]
|
||||
print '''\
|
||||
case {}:'''.format(size)
|
||||
print '''\
|
||||
switch (name[{}]) {{'''.format(size - 1)
|
||||
for c in sorted(ents.keys()):
|
||||
headers = sorted(ents[c])
|
||||
print '''\
|
||||
case '{}':'''.format(c)
|
||||
for k in headers:
|
||||
print '''\
|
||||
if (lstreq("{}", name, {})) {{
|
||||
return {};
|
||||
}}'''.format(k[:-1], size - 1, to_enum_hd(k))
|
||||
print '''\
|
||||
break;'''
|
||||
print '''\
|
||||
}
|
||||
break;'''
|
||||
print '''\
|
||||
}
|
||||
return -1;
|
||||
}'''
|
||||
|
||||
if __name__ == '__main__':
|
||||
gen_enum()
|
||||
print ''
|
||||
gen_index_header()
|
||||
0
gennmchartbl.py
Executable file → Normal file
0
gennmchartbl.py
Executable file → Normal file
0
genvchartbl.py
Executable file → Normal file
0
genvchartbl.py
Executable file → Normal file
484
git-clang-format
484
git-clang-format
@@ -1,484 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
#===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
r"""
|
||||
clang-format git integration
|
||||
============================
|
||||
|
||||
This file provides a clang-format integration for git. Put it somewhere in your
|
||||
path and ensure that it is executable. Then, "git clang-format" will invoke
|
||||
clang-format on the changes in current files or a specific commit.
|
||||
|
||||
For further details, run:
|
||||
git clang-format -h
|
||||
|
||||
Requires Python 2.7
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import contextlib
|
||||
import errno
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
usage = 'git clang-format [OPTIONS] [<commit>] [--] [<file>...]'
|
||||
|
||||
desc = '''
|
||||
Run clang-format on all lines that differ between the working directory
|
||||
and <commit>, which defaults to HEAD. Changes are only applied to the working
|
||||
directory.
|
||||
|
||||
The following git-config settings set the default of the corresponding option:
|
||||
clangFormat.binary
|
||||
clangFormat.commit
|
||||
clangFormat.extension
|
||||
clangFormat.style
|
||||
'''
|
||||
|
||||
# Name of the temporary index file in which save the output of clang-format.
|
||||
# This file is created within the .git directory.
|
||||
temp_index_basename = 'clang-format-index'
|
||||
|
||||
|
||||
Range = collections.namedtuple('Range', 'start, count')
|
||||
|
||||
|
||||
def main():
|
||||
config = load_git_config()
|
||||
|
||||
# In order to keep '--' yet allow options after positionals, we need to
|
||||
# check for '--' ourselves. (Setting nargs='*' throws away the '--', while
|
||||
# nargs=argparse.REMAINDER disallows options after positionals.)
|
||||
argv = sys.argv[1:]
|
||||
try:
|
||||
idx = argv.index('--')
|
||||
except ValueError:
|
||||
dash_dash = []
|
||||
else:
|
||||
dash_dash = argv[idx:]
|
||||
argv = argv[:idx]
|
||||
|
||||
default_extensions = ','.join([
|
||||
# From clang/lib/Frontend/FrontendOptions.cpp, all lower case
|
||||
'c', 'h', # C
|
||||
'm', # ObjC
|
||||
'mm', # ObjC++
|
||||
'cc', 'cp', 'cpp', 'c++', 'cxx', 'hpp', # C++
|
||||
# Other languages that clang-format supports
|
||||
'proto', 'protodevel', # Protocol Buffers
|
||||
'js', # JavaScript
|
||||
])
|
||||
|
||||
p = argparse.ArgumentParser(
|
||||
usage=usage, formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=desc)
|
||||
p.add_argument('--binary',
|
||||
default=config.get('clangformat.binary', 'clang-format'),
|
||||
help='path to clang-format'),
|
||||
p.add_argument('--commit',
|
||||
default=config.get('clangformat.commit', 'HEAD'),
|
||||
help='default commit to use if none is specified'),
|
||||
p.add_argument('--diff', action='store_true',
|
||||
help='print a diff instead of applying the changes')
|
||||
p.add_argument('--extensions',
|
||||
default=config.get('clangformat.extensions',
|
||||
default_extensions),
|
||||
help=('comma-separated list of file extensions to format, '
|
||||
'excluding the period and case-insensitive')),
|
||||
p.add_argument('-f', '--force', action='store_true',
|
||||
help='allow changes to unstaged files')
|
||||
p.add_argument('-p', '--patch', action='store_true',
|
||||
help='select hunks interactively')
|
||||
p.add_argument('-q', '--quiet', action='count', default=0,
|
||||
help='print less information')
|
||||
p.add_argument('--style',
|
||||
default=config.get('clangformat.style', None),
|
||||
help='passed to clang-format'),
|
||||
p.add_argument('-v', '--verbose', action='count', default=0,
|
||||
help='print extra information')
|
||||
# We gather all the remaining positional arguments into 'args' since we need
|
||||
# to use some heuristics to determine whether or not <commit> was present.
|
||||
# However, to print pretty messages, we make use of metavar and help.
|
||||
p.add_argument('args', nargs='*', metavar='<commit>',
|
||||
help='revision from which to compute the diff')
|
||||
p.add_argument('ignored', nargs='*', metavar='<file>...',
|
||||
help='if specified, only consider differences in these files')
|
||||
opts = p.parse_args(argv)
|
||||
|
||||
opts.verbose -= opts.quiet
|
||||
del opts.quiet
|
||||
|
||||
commit, files = interpret_args(opts.args, dash_dash, opts.commit)
|
||||
changed_lines = compute_diff_and_extract_lines(commit, files)
|
||||
if opts.verbose >= 1:
|
||||
ignored_files = set(changed_lines)
|
||||
filter_by_extension(changed_lines, opts.extensions.lower().split(','))
|
||||
if opts.verbose >= 1:
|
||||
ignored_files.difference_update(changed_lines)
|
||||
if ignored_files:
|
||||
print 'Ignoring changes in the following files (wrong extension):'
|
||||
for filename in ignored_files:
|
||||
print ' ', filename
|
||||
if changed_lines:
|
||||
print 'Running clang-format on the following files:'
|
||||
for filename in changed_lines:
|
||||
print ' ', filename
|
||||
if not changed_lines:
|
||||
print 'no modified files to format'
|
||||
return
|
||||
# The computed diff outputs absolute paths, so we must cd before accessing
|
||||
# those files.
|
||||
cd_to_toplevel()
|
||||
old_tree = create_tree_from_workdir(changed_lines)
|
||||
new_tree = run_clang_format_and_save_to_tree(changed_lines,
|
||||
binary=opts.binary,
|
||||
style=opts.style)
|
||||
if opts.verbose >= 1:
|
||||
print 'old tree:', old_tree
|
||||
print 'new tree:', new_tree
|
||||
if old_tree == new_tree:
|
||||
if opts.verbose >= 0:
|
||||
print 'clang-format did not modify any files'
|
||||
elif opts.diff:
|
||||
print_diff(old_tree, new_tree)
|
||||
else:
|
||||
changed_files = apply_changes(old_tree, new_tree, force=opts.force,
|
||||
patch_mode=opts.patch)
|
||||
if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1:
|
||||
print 'changed files:'
|
||||
for filename in changed_files:
|
||||
print ' ', filename
|
||||
|
||||
|
||||
def load_git_config(non_string_options=None):
|
||||
"""Return the git configuration as a dictionary.
|
||||
|
||||
All options are assumed to be strings unless in `non_string_options`, in which
|
||||
is a dictionary mapping option name (in lower case) to either "--bool" or
|
||||
"--int"."""
|
||||
if non_string_options is None:
|
||||
non_string_options = {}
|
||||
out = {}
|
||||
for entry in run('git', 'config', '--list', '--null').split('\0'):
|
||||
if entry:
|
||||
name, value = entry.split('\n', 1)
|
||||
if name in non_string_options:
|
||||
value = run('git', 'config', non_string_options[name], name)
|
||||
out[name] = value
|
||||
return out
|
||||
|
||||
|
||||
def interpret_args(args, dash_dash, default_commit):
|
||||
"""Interpret `args` as "[commit] [--] [files...]" and return (commit, files).
|
||||
|
||||
It is assumed that "--" and everything that follows has been removed from
|
||||
args and placed in `dash_dash`.
|
||||
|
||||
If "--" is present (i.e., `dash_dash` is non-empty), the argument to its
|
||||
left (if present) is taken as commit. Otherwise, the first argument is
|
||||
checked if it is a commit or a file. If commit is not given,
|
||||
`default_commit` is used."""
|
||||
if dash_dash:
|
||||
if len(args) == 0:
|
||||
commit = default_commit
|
||||
elif len(args) > 1:
|
||||
die('at most one commit allowed; %d given' % len(args))
|
||||
else:
|
||||
commit = args[0]
|
||||
object_type = get_object_type(commit)
|
||||
if object_type not in ('commit', 'tag'):
|
||||
if object_type is None:
|
||||
die("'%s' is not a commit" % commit)
|
||||
else:
|
||||
die("'%s' is a %s, but a commit was expected" % (commit, object_type))
|
||||
files = dash_dash[1:]
|
||||
elif args:
|
||||
if disambiguate_revision(args[0]):
|
||||
commit = args[0]
|
||||
files = args[1:]
|
||||
else:
|
||||
commit = default_commit
|
||||
files = args
|
||||
else:
|
||||
commit = default_commit
|
||||
files = []
|
||||
return commit, files
|
||||
|
||||
|
||||
def disambiguate_revision(value):
|
||||
"""Returns True if `value` is a revision, False if it is a file, or dies."""
|
||||
# If `value` is ambiguous (neither a commit nor a file), the following
|
||||
# command will die with an appropriate error message.
|
||||
run('git', 'rev-parse', value, verbose=False)
|
||||
object_type = get_object_type(value)
|
||||
if object_type is None:
|
||||
return False
|
||||
if object_type in ('commit', 'tag'):
|
||||
return True
|
||||
die('`%s` is a %s, but a commit or filename was expected' %
|
||||
(value, object_type))
|
||||
|
||||
|
||||
def get_object_type(value):
|
||||
"""Returns a string description of an object's type, or None if it is not
|
||||
a valid git object."""
|
||||
cmd = ['git', 'cat-file', '-t', value]
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate()
|
||||
if p.returncode != 0:
|
||||
return None
|
||||
return stdout.strip()
|
||||
|
||||
|
||||
def compute_diff_and_extract_lines(commit, files):
|
||||
"""Calls compute_diff() followed by extract_lines()."""
|
||||
diff_process = compute_diff(commit, files)
|
||||
changed_lines = extract_lines(diff_process.stdout)
|
||||
diff_process.stdout.close()
|
||||
diff_process.wait()
|
||||
if diff_process.returncode != 0:
|
||||
# Assume error was already printed to stderr.
|
||||
sys.exit(2)
|
||||
return changed_lines
|
||||
|
||||
|
||||
def compute_diff(commit, files):
|
||||
"""Return a subprocess object producing the diff from `commit`.
|
||||
|
||||
The return value's `stdin` file object will produce a patch with the
|
||||
differences between the working directory and `commit`, filtered on `files`
|
||||
(if non-empty). Zero context lines are used in the patch."""
|
||||
cmd = ['git', 'diff-index', '-p', '-U0', commit, '--']
|
||||
cmd.extend(files)
|
||||
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
p.stdin.close()
|
||||
return p
|
||||
|
||||
|
||||
def extract_lines(patch_file):
|
||||
"""Extract the changed lines in `patch_file`.
|
||||
|
||||
The return value is a dictionary mapping filename to a list of (start_line,
|
||||
line_count) pairs.
|
||||
|
||||
The input must have been produced with ``-U0``, meaning unidiff format with
|
||||
zero lines of context. The return value is a dict mapping filename to a
|
||||
list of line `Range`s."""
|
||||
matches = {}
|
||||
for line in patch_file:
|
||||
match = re.search(r'^\+\+\+\ [^/]+/(.*)', line)
|
||||
if match:
|
||||
filename = match.group(1).rstrip('\r\n')
|
||||
match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line)
|
||||
if match:
|
||||
start_line = int(match.group(1))
|
||||
line_count = 1
|
||||
if match.group(3):
|
||||
line_count = int(match.group(3))
|
||||
if line_count > 0:
|
||||
matches.setdefault(filename, []).append(Range(start_line, line_count))
|
||||
return matches
|
||||
|
||||
|
||||
def filter_by_extension(dictionary, allowed_extensions):
|
||||
"""Delete every key in `dictionary` that doesn't have an allowed extension.
|
||||
|
||||
`allowed_extensions` must be a collection of lowercase file extensions,
|
||||
excluding the period."""
|
||||
allowed_extensions = frozenset(allowed_extensions)
|
||||
for filename in dictionary.keys():
|
||||
base_ext = filename.rsplit('.', 1)
|
||||
if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions:
|
||||
del dictionary[filename]
|
||||
|
||||
|
||||
def cd_to_toplevel():
|
||||
"""Change to the top level of the git repository."""
|
||||
toplevel = run('git', 'rev-parse', '--show-toplevel')
|
||||
os.chdir(toplevel)
|
||||
|
||||
|
||||
def create_tree_from_workdir(filenames):
|
||||
"""Create a new git tree with the given files from the working directory.
|
||||
|
||||
Returns the object ID (SHA-1) of the created tree."""
|
||||
return create_tree(filenames, '--stdin')
|
||||
|
||||
|
||||
def run_clang_format_and_save_to_tree(changed_lines, binary='clang-format',
|
||||
style=None):
|
||||
"""Run clang-format on each file and save the result to a git tree.
|
||||
|
||||
Returns the object ID (SHA-1) of the created tree."""
|
||||
def index_info_generator():
|
||||
for filename, line_ranges in changed_lines.iteritems():
|
||||
mode = oct(os.stat(filename).st_mode)
|
||||
blob_id = clang_format_to_blob(filename, line_ranges, binary=binary,
|
||||
style=style)
|
||||
yield '%s %s\t%s' % (mode, blob_id, filename)
|
||||
return create_tree(index_info_generator(), '--index-info')
|
||||
|
||||
|
||||
def create_tree(input_lines, mode):
|
||||
"""Create a tree object from the given input.
|
||||
|
||||
If mode is '--stdin', it must be a list of filenames. If mode is
|
||||
'--index-info' is must be a list of values suitable for "git update-index
|
||||
--index-info", such as "<mode> <SP> <sha1> <TAB> <filename>". Any other mode
|
||||
is invalid."""
|
||||
assert mode in ('--stdin', '--index-info')
|
||||
cmd = ['git', 'update-index', '--add', '-z', mode]
|
||||
with temporary_index_file():
|
||||
p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
|
||||
for line in input_lines:
|
||||
p.stdin.write('%s\0' % line)
|
||||
p.stdin.close()
|
||||
if p.wait() != 0:
|
||||
die('`%s` failed' % ' '.join(cmd))
|
||||
tree_id = run('git', 'write-tree')
|
||||
return tree_id
|
||||
|
||||
|
||||
def clang_format_to_blob(filename, line_ranges, binary='clang-format',
|
||||
style=None):
|
||||
"""Run clang-format on the given file and save the result to a git blob.
|
||||
|
||||
Returns the object ID (SHA-1) of the created blob."""
|
||||
clang_format_cmd = [binary, filename]
|
||||
if style:
|
||||
clang_format_cmd.extend(['-style='+style])
|
||||
clang_format_cmd.extend([
|
||||
'-lines=%s:%s' % (start_line, start_line+line_count-1)
|
||||
for start_line, line_count in line_ranges])
|
||||
try:
|
||||
clang_format = subprocess.Popen(clang_format_cmd, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE)
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
die('cannot find executable "%s"' % binary)
|
||||
else:
|
||||
raise
|
||||
clang_format.stdin.close()
|
||||
hash_object_cmd = ['git', 'hash-object', '-w', '--path='+filename, '--stdin']
|
||||
hash_object = subprocess.Popen(hash_object_cmd, stdin=clang_format.stdout,
|
||||
stdout=subprocess.PIPE)
|
||||
clang_format.stdout.close()
|
||||
stdout = hash_object.communicate()[0]
|
||||
if hash_object.returncode != 0:
|
||||
die('`%s` failed' % ' '.join(hash_object_cmd))
|
||||
if clang_format.wait() != 0:
|
||||
die('`%s` failed' % ' '.join(clang_format_cmd))
|
||||
return stdout.rstrip('\r\n')
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def temporary_index_file(tree=None):
|
||||
"""Context manager for setting GIT_INDEX_FILE to a temporary file and deleting
|
||||
the file afterward."""
|
||||
index_path = create_temporary_index(tree)
|
||||
old_index_path = os.environ.get('GIT_INDEX_FILE')
|
||||
os.environ['GIT_INDEX_FILE'] = index_path
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if old_index_path is None:
|
||||
del os.environ['GIT_INDEX_FILE']
|
||||
else:
|
||||
os.environ['GIT_INDEX_FILE'] = old_index_path
|
||||
os.remove(index_path)
|
||||
|
||||
|
||||
def create_temporary_index(tree=None):
|
||||
"""Create a temporary index file and return the created file's path.
|
||||
|
||||
If `tree` is not None, use that as the tree to read in. Otherwise, an
|
||||
empty index is created."""
|
||||
gitdir = run('git', 'rev-parse', '--git-dir')
|
||||
path = os.path.join(gitdir, temp_index_basename)
|
||||
if tree is None:
|
||||
tree = '--empty'
|
||||
run('git', 'read-tree', '--index-output='+path, tree)
|
||||
return path
|
||||
|
||||
|
||||
def print_diff(old_tree, new_tree):
|
||||
"""Print the diff between the two trees to stdout."""
|
||||
# We use the porcelain 'diff' and not plumbing 'diff-tree' because the output
|
||||
# is expected to be viewed by the user, and only the former does nice things
|
||||
# like color and pagination.
|
||||
subprocess.check_call(['git', 'diff', old_tree, new_tree, '--'])
|
||||
|
||||
|
||||
def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
|
||||
"""Apply the changes in `new_tree` to the working directory.
|
||||
|
||||
Bails if there are local changes in those files and not `force`. If
|
||||
`patch_mode`, runs `git checkout --patch` to select hunks interactively."""
|
||||
changed_files = run('git', 'diff-tree', '-r', '-z', '--name-only', old_tree,
|
||||
new_tree).rstrip('\0').split('\0')
|
||||
if not force:
|
||||
unstaged_files = run('git', 'diff-files', '--name-status', *changed_files)
|
||||
if unstaged_files:
|
||||
print >>sys.stderr, ('The following files would be modified but '
|
||||
'have unstaged changes:')
|
||||
print >>sys.stderr, unstaged_files
|
||||
print >>sys.stderr, 'Please commit, stage, or stash them first.'
|
||||
sys.exit(2)
|
||||
if patch_mode:
|
||||
# In patch mode, we could just as well create an index from the new tree
|
||||
# and checkout from that, but then the user will be presented with a
|
||||
# message saying "Discard ... from worktree". Instead, we use the old
|
||||
# tree as the index and checkout from new_tree, which gives the slightly
|
||||
# better message, "Apply ... to index and worktree". This is not quite
|
||||
# right, since it won't be applied to the user's index, but oh well.
|
||||
with temporary_index_file(old_tree):
|
||||
subprocess.check_call(['git', 'checkout', '--patch', new_tree])
|
||||
index_tree = old_tree
|
||||
else:
|
||||
with temporary_index_file(new_tree):
|
||||
run('git', 'checkout-index', '-a', '-f')
|
||||
return changed_files
|
||||
|
||||
|
||||
def run(*args, **kwargs):
|
||||
stdin = kwargs.pop('stdin', '')
|
||||
verbose = kwargs.pop('verbose', True)
|
||||
strip = kwargs.pop('strip', True)
|
||||
for name in kwargs:
|
||||
raise TypeError("run() got an unexpected keyword argument '%s'" % name)
|
||||
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
stdin=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate(input=stdin)
|
||||
if p.returncode == 0:
|
||||
if stderr:
|
||||
if verbose:
|
||||
print >>sys.stderr, '`%s` printed to stderr:' % ' '.join(args)
|
||||
print >>sys.stderr, stderr.rstrip()
|
||||
if strip:
|
||||
stdout = stdout.rstrip('\r\n')
|
||||
return stdout
|
||||
if verbose:
|
||||
print >>sys.stderr, '`%s` returned %s' % (' '.join(args), p.returncode)
|
||||
if stderr:
|
||||
print >>sys.stderr, stderr.rstrip()
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
def die(message):
|
||||
print >>sys.stderr, 'error:', message
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
191
help2rst.py
191
help2rst.py
@@ -1,191 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# script to produce rst file from program's help output.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import sys
|
||||
import re
|
||||
import argparse
|
||||
|
||||
arg_indent = ' ' * 14
|
||||
|
||||
def help2man(infile):
|
||||
# We assume that first line is usage line like this:
|
||||
#
|
||||
# Usage: nghttp [OPTIONS]... URI...
|
||||
#
|
||||
# The second line is description of the command. Multiple lines
|
||||
# are permitted. The blank line signals the end of this section.
|
||||
# After that, we parses positional and optional arguments.
|
||||
#
|
||||
# The positional argument is enclosed with < and >:
|
||||
#
|
||||
# <PRIVATE_KEY>
|
||||
#
|
||||
# We may describe default behavior without any options by encoding
|
||||
# ( and ):
|
||||
#
|
||||
# (default mode)
|
||||
#
|
||||
# "Options:" is treated specially and produces "OPTIONS" section.
|
||||
# We allow subsection under OPTIONS. Lines not starting with (, <
|
||||
# and Options: are treated as subsection name and produces section
|
||||
# one level down:
|
||||
#
|
||||
# TLS/SSL:
|
||||
#
|
||||
# The above is an example of subsection.
|
||||
#
|
||||
# The description of arguments must be indented by len(arg_indent)
|
||||
# characters. The default value should be placed in separate line
|
||||
# and should be start with "Default: " after indentation.
|
||||
|
||||
line = infile.readline().strip()
|
||||
m = re.match(r'^Usage: (.*)', line)
|
||||
if not m:
|
||||
print 'usage line is invalid. Expected following lines:'
|
||||
print 'Usage: cmdname ...'
|
||||
sys.exit(1)
|
||||
synopsis = m.group(1).split(' ', 1)
|
||||
if len(synopsis) == 2:
|
||||
cmdname, args = synopsis
|
||||
else:
|
||||
cmdname, args = synopsis[0], ''
|
||||
|
||||
description = []
|
||||
for line in infile:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
break
|
||||
description.append(line)
|
||||
|
||||
print '''
|
||||
.. GENERATED by help2rst.py. DO NOT EDIT DIRECTLY.
|
||||
|
||||
.. program:: {cmdname}
|
||||
|
||||
{cmdname}(1)
|
||||
{cmdnameunderline}
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
**{cmdname}** {args}
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
{description}
|
||||
'''.format(cmdname=cmdname, args=args,
|
||||
cmdnameunderline='=' * (len(cmdname) + 3),
|
||||
synopsis=synopsis, description=format_text('\n'.join(description)))
|
||||
|
||||
in_arg = False
|
||||
in_footer = False
|
||||
|
||||
for line in infile:
|
||||
line = line.rstrip()
|
||||
|
||||
if not line.strip() and in_arg:
|
||||
print ''
|
||||
continue
|
||||
if line.startswith(' ') and in_arg:
|
||||
if not line.startswith(arg_indent):
|
||||
sys.stderr.write('warning: argument description is not indented correctly. We need {} spaces as indentation.\n'.format(len(arg_indent)))
|
||||
print '{}'.format(format_arg_text(line[len(arg_indent):]))
|
||||
continue
|
||||
|
||||
if in_arg:
|
||||
print ''
|
||||
in_arg = False
|
||||
|
||||
if line == '--':
|
||||
in_footer = True
|
||||
continue
|
||||
|
||||
if in_footer:
|
||||
print line.strip()
|
||||
continue
|
||||
|
||||
if line == 'Options:':
|
||||
print 'OPTIONS'
|
||||
print '-------'
|
||||
print ''
|
||||
continue
|
||||
|
||||
if line.startswith(' <'):
|
||||
# positional argument
|
||||
m = re.match(r'^(?:\s+)([a-zA-Z0-9-_<>]+)(.*)', line)
|
||||
argname, rest = m.group(1), m.group(2)
|
||||
print '.. describe:: {}'.format(argname)
|
||||
print ''
|
||||
print '{}'.format(format_arg_text(rest.strip()))
|
||||
in_arg = True
|
||||
continue
|
||||
|
||||
if line.startswith(' ('):
|
||||
# positional argument
|
||||
m = re.match(r'^(?:\s+)(\([a-zA-Z0-9-_<> ]+\))(.*)', line)
|
||||
argname, rest = m.group(1), m.group(2)
|
||||
print '.. describe:: {}'.format(argname)
|
||||
print ''
|
||||
print '{}'.format(format_arg_text(rest.strip()))
|
||||
in_arg = True
|
||||
continue
|
||||
|
||||
if line.startswith(' -'):
|
||||
# optional argument
|
||||
m = re.match(
|
||||
r'^(?:\s+)(-\S+?(?:, -\S+?)*)($| .*)',
|
||||
line)
|
||||
argname, rest = m.group(1), m.group(2)
|
||||
print '.. option:: {}'.format(argname)
|
||||
print ''
|
||||
rest = rest.strip()
|
||||
if len(rest):
|
||||
print '{}'.format(format_arg_text(rest))
|
||||
in_arg = True
|
||||
continue
|
||||
|
||||
if not line.startswith(' ') and line.endswith(':'):
|
||||
# subsection
|
||||
subsec = line.strip()[:-1]
|
||||
print '{}'.format(subsec)
|
||||
print '{}'.format('~' * len(subsec))
|
||||
print ''
|
||||
continue
|
||||
|
||||
print line.strip()
|
||||
|
||||
def format_text(text):
|
||||
# escape *
|
||||
if len(text) > len(arg_indent):
|
||||
text = text[:len(arg_indent) + 1] + re.sub(r'\*', r'\*', text[len(arg_indent) + 1:])
|
||||
else:
|
||||
text = re.sub(r'\*', r'\*', text)
|
||||
# markup option reference
|
||||
text = re.sub(r'(^|\s)(-[a-zA-Z0-9-]+)', r'\1:option:`\2`', text)
|
||||
# sphinx does not like markup like ':option:`-f`='. We need
|
||||
# backslash between ` and =.
|
||||
text = re.sub(r'(:option:`.*?`)(\S)', r'\1\\\2', text)
|
||||
# file path should be italic
|
||||
text = re.sub(r'(^|\s|\'|")(/[^\s\'"]*)', r'\1*\2*', text)
|
||||
return text
|
||||
|
||||
def format_arg_text(text):
|
||||
if text.strip().startswith('Default: '):
|
||||
return '\n ' + re.sub(r'^(\s*Default: )(.*)$', r'\1``\2``', text)
|
||||
return ' {}'.format(format_text(text))
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Produces rst document from help output.')
|
||||
parser.add_argument('-i', '--include', metavar='FILE',
|
||||
help='include content of <FILE> as verbatim. It should be ReST formatted text.')
|
||||
args = parser.parse_args()
|
||||
help2man(sys.stdin)
|
||||
if args.include:
|
||||
print ''
|
||||
with open(args.include) as f:
|
||||
sys.stdout.write(f.read())
|
||||
2
integration-tests/.gitignore
vendored
2
integration-tests/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
# generated files
|
||||
config.go
|
||||
@@ -1,43 +0,0 @@
|
||||
# nghttp2 - HTTP/2 C Library
|
||||
|
||||
# Copyright (c) 2015 Tatsuhiro Tsujikawa
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
EXTRA_DIST = \
|
||||
nghttpx_http1_test.go \
|
||||
nghttpx_http2_test.go \
|
||||
nghttpx_spdy_test.go \
|
||||
server_tester.go \
|
||||
server.key \
|
||||
server.crt \
|
||||
alt-server.key \
|
||||
alt-server.crt \
|
||||
setenv
|
||||
|
||||
.PHONY: itprep it
|
||||
|
||||
itprep:
|
||||
go get -d -v github.com/bradfitz/http2
|
||||
go get -d -v github.com/tatsuhiro-t/go-nghttp2
|
||||
go get -d -v golang.org/x/net/spdy
|
||||
|
||||
it:
|
||||
sh setenv go test -v
|
||||
@@ -1,21 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDhzCCAm+gAwIBAgIJANfuEldiquMNMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV
|
||||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||
aWRnaXRzIFB0eSBMdGQxEzARBgNVBAMMCmFsdC1kb21haW4wHhcNMTUwMTI1MDYy
|
||||
NTQxWhcNMjUwMTIyMDYyNTQxWjBaMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29t
|
||||
ZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYD
|
||||
VQQDDAphbHQtZG9tYWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||
0IwhDOGDipGrJQ9IoRSzPdkU/Ii4aJgGKHlXminym42X0VI3IW61RLvOHRlHVmVH
|
||||
JQjFuDo2x+y81t9NlDg3HGUbSpzOzpm6StiutB7c4hreT5G4r0YKya1ugiemN0+p
|
||||
qjIPJWm2jVnf448eZvUKRKEQ9W0MLZjiNjVGKrKlwo7fIlXg4N3+YixLYffAT1NV
|
||||
d1T6V5jzlbruj15gK2nGjMQ9D1h1t9vTbTxY+mtk72aX0Y64IE6pPBWLFSSH8ozU
|
||||
idDoL3AZwz2Jker+ALKK8CM4uho/RPpyW1C06HH+HLdH2MqEjDOROde/Nzxm668O
|
||||
gK/JWGIEyUqYiUXx0yhFxwIDAQABo1AwTjAdBgNVHQ4EFgQU/Y0GDN2uPjbyePcu
|
||||
95ZvYEK/gHIwHwYDVR0jBBgwFoAU/Y0GDN2uPjbyePcu95ZvYEK/gHIwDAYDVR0T
|
||||
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAodD6LVCzL3wfsZ6TxTzf9TfgIdbj
|
||||
ilL3SEMT/xnfTXT3SLYScTRqQIAI29Y7dOLMq89p4hY2wmeUEhBUAz+y9G2JVr8o
|
||||
6EbxXrQpWgNJogELqoNnMdrDxB5RsmDDKEJ/rLjDfSkjWbK7B2PZsqVTDgjekCFw
|
||||
u6FqTIjn/O1O/L5tjwxwxjHmQod/maFCvXoDOVBuwdHnkp298tqlvsHfHO8m++Wj
|
||||
+XYB8plMIjpeTh9v4w9Jc4QZ59lK/3Tt4qaENeQrMEubKSY/Zen7L2bzhk+cChWT
|
||||
GSGz9uNXieoZaH79D0wnyZaSZ5Ds4ActMevnGg3iYXuzuFqx8Pungn74Vg==
|
||||
-----END CERTIFICATE-----
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user