Compare commits

..

3 Commits

Author SHA1 Message Date
Tatsuhiro Tsujikawa
e24e29ff14 Bump up version number to 0.3.2; LT revision to 2:2:0 2014-02-26 23:52:38 +09:00
Tatsuhiro Tsujikawa
2f23eac179 nghttp2_hd: Fail inflate immediately if ctx.bad is nonzero
Doing inflation after error produces invalid results, especially, if
it is in NGHTTP2_HD_STATE_READ_INDEX, the inflater->left could be 0,
which causes assertion error.  Add sanity assertion for index
2014-02-26 23:36:28 +09:00
Tatsuhiro Tsujikawa
b3f6664bc6 nghttp2_hd: Fix integer decoding bug 2014-02-26 23:36:28 +09:00
383 changed files with 34491 additions and 79154 deletions

View File

@@ -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
View File

@@ -1,18 +1,14 @@
# emacs backup file
*~ *~
# autotools
*.la
*.lo
*.m4
*.o *.o
.deps/ *.lo
.libs/ *.la
INSTALL depcomp
*.m4
Makefile Makefile
Makefile.in Makefile.in
libtool
missing
autom4te.cache/ autom4te.cache/
compile
config.guess config.guess
config.h config.h
config.h.in config.h.in
@@ -20,14 +16,22 @@ config.log
config.status config.status
config.sub config.sub
configure configure
depcomp
install-sh install-sh
libtool .deps/
.libs
lib/includes/nghttp2/nghttp2ver.h
lib/libnghttp2.pc
ltmain.sh ltmain.sh
missing
stamp-h1 stamp-h1
.deps/
INSTALL
.DS_STORE
compile
test-driver test-driver
.dirstamp
# test logs generated by `make check` doc/index.rst
*.log doc/nghttp2.h.rst
*.trs doc/nghttp2ver.h.rst
doc/package_README.rst
doc/tutorial-client.rst
doc/tutorial-server.rst

View File

@@ -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

View File

@@ -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!

View File

@@ -1,6 +1,6 @@
The MIT License 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 Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the

View File

@@ -1,128 +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:vivid
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-r10d-linux-x86_64.bin && \
chmod a+x android-ndk-r10d-linux-x86_64.bin && \
./android-ndk-r10d-linux-x86_64.bin && \
rm android-ndk-r10d-linux-x86_64.bin
WORKDIR /root/build/android-ndk-r10d
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 curl -L -O http://zlib.net/zlib-1.2.8.tar.gz && \
tar xf zlib-1.2.8.tar.gz && \
rm zlib-1.2.8.tar.gz
WORKDIR /root/build/zlib-1.2.8
RUN HOST=arm-linux-androideabi \
CC=$HOST-gcc \
AR=$HOST-ar \
LD=$HOST-ld \
RANLIB=$HOST-ranlib \
STRIP=$HOST-strip \
./configure \
--prefix=$PREFIX \
--libdir=$PREFIX/lib \
--includedir=$PREFIX/include \
--static && \
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

View File

@@ -1,4 +1,4 @@
# nghttp2 - HTTP/2 C Library # nghttp2 - HTTP/2.0 C Library
# Copyright (c) 2012 Tatsuhiro Tsujikawa # Copyright (c) 2012 Tatsuhiro Tsujikawa
@@ -20,24 +20,10 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
SUBDIRS = lib third-party src examples python tests integration-tests \ SUBDIRS = lib third-party src examples python tests doc
doc contrib
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
dist_doc_DATA = README.rst dist_doc_DATA = README.rst
EXTRA_DIST = nghttpx.conf.sample proxy.pac.sample android-config android-make \ 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}

1175
README.rst

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
# #
# nghttp2 - HTTP/2 C Library # nghttp2 - HTTP/2.0 C Library
# #
# Copyright (c) 2013 Tatsuhiro Tsujikawa # Copyright (c) 2013 Tatsuhiro Tsujikawa
# #
@@ -36,12 +36,6 @@ PATH=$TOOLCHAIN/bin:$PATH
--host=arm-linux-androideabi \ --host=arm-linux-androideabi \
--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \ --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \
--with-xml-prefix="$PREFIX" \ --with-xml-prefix="$PREFIX" \
--without-libxml2 \ CPPFLAGS="-I$PREFIX/include" \
--disable-python-bindings \
--disable-examples \
--enable-werror \
CC=clang \
CXX=clang++ \
CPPFLAGS="-fPIE -I$PREFIX/include" \
PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \ PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig" \
LDFLAGS="-fPIE -pie -L$PREFIX/lib" LDFLAGS="-L$PREFIX/lib"

View File

@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
# #
# nghttp2 - HTTP/2 C Library # nghttp2 - HTTP/2.0 C Library
# #
# Copyright (c) 2013 Tatsuhiro Tsujikawa # Copyright (c) 2013 Tatsuhiro Tsujikawa
# #

View File

@@ -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 Permission is hereby granted, free of charge, to any person obtaining
dnl a copy of this software and associated documentation files (the dnl a copy of this software and associated documentation files (the
@@ -20,35 +20,14 @@ 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 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 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 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_PREREQ(2.61)
AC_INIT([nghttp2], [1.0.1], [t-tujikawa@users.sourceforge.net]) AC_INIT([nghttp2], [0.3.2], [t-tujikawa@users.sourceforge.net])
AC_USE_SYSTEM_EXTENSIONS
LT_PREREQ([2.2.6]) LT_PREREQ([2.2.6])
LT_INIT() 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 See versioning rule:
dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
AC_SUBST(LT_CURRENT, 14) AC_SUBST(LT_CURRENT, 2)
AC_SUBST(LT_REVISION, 1) AC_SUBST(LT_REVISION, 2)
AC_SUBST(LT_AGE, 0) AC_SUBST(LT_AGE, 0)
major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"` major=`echo $PACKAGE_VERSION |cut -d. -f1 | 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_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 dnl Checks for command-line options
AC_ARG_ENABLE([werror], AC_ARG_ENABLE([maintainer-mode],
[AS_HELP_STRING([--enable-werror], [AS_HELP_STRING([--enable-maintainer-mode],
[Turn on compile time warnings])], [Turn on compile time warnings])],
[werror=$enableval], [werror=no]) [maintainer_mode=$enableval], [maintainer_mode=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])
AC_ARG_ENABLE([app], AC_ARG_ENABLE([app],
[AS_HELP_STRING([--enable-app], [AS_HELP_STRING([--enable-app],
@@ -85,11 +63,6 @@ AC_ARG_ENABLE([hpack-tools],
[Build HPACK tools [default=check]])], [Build HPACK tools [default=check]])],
[request_hpack_tools=$enableval], [request_hpack_tools=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], AC_ARG_ENABLE([examples],
[AS_HELP_STRING([--enable-examples], [AS_HELP_STRING([--enable-examples],
[Build examples [default=check]])], [Build examples [default=check]])],
@@ -101,25 +74,15 @@ AC_ARG_ENABLE([python-bindings],
[request_python_bindings=$enableval], [request_python_bindings=check]) [request_python_bindings=$enableval], [request_python_bindings=check])
AC_ARG_ENABLE([failmalloc], AC_ARG_ENABLE([failmalloc],
[AS_HELP_STRING([--disable-failmalloc], [AS_HELP_STRING([--enable-failmalloc],
[Do not build failmalloc test program])], [Build failmalloc test program [default=no]])],
[request_failmalloc=$enableval], [request_failmalloc=yes]) [request_failmalloc=$enableval], [request_failmalloc=no])
AC_ARG_WITH([libxml2], AC_ARG_WITH([libxml2],
[AS_HELP_STRING([--with-libxml2], [AS_HELP_STRING([--with-libxml2],
[Use libxml2 [default=check]])], [Use libxml2 [default=check]])],
[request_libxml2=$withval], [request_libxml2=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], AC_ARG_WITH([cython],
[AS_HELP_STRING([--with-cython=PATH], [AS_HELP_STRING([--with-cython=PATH],
[Use cython in given PATH])], [Use cython in given PATH])],
@@ -131,12 +94,10 @@ AC_ARG_VAR([CYTHON], [the Cython executable])
dnl Checks for programs dnl Checks for programs
AC_PROG_CC AC_PROG_CC
AC_PROG_CXX AC_PROG_CXX
AC_PROG_CPP
AC_PROG_INSTALL AC_PROG_INSTALL
AC_PROG_LN_S AC_PROG_LN_S
AC_PROG_MAKE_SET AC_PROG_MAKE_SET
AC_PROG_MKDIR_P AM_PROG_CC_C_O
PKG_PROG_PKG_CONFIG([0.20]) PKG_PROG_PKG_CONFIG([0.20])
if [test "x$request_python_bindings" != "xno"]; then if [test "x$request_python_bindings" != "xno"]; then
@@ -151,29 +112,14 @@ else
AC_SUBST([CYTHON]) AC_SUBST([CYTHON])
fi 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]) AX_CXX_COMPILE_STDCXX_11([noext], [optional])
AC_LANG_PUSH(C++)
# Check that std::chrono::steady_clock is available. In particular, # 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 # 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 # name existed in the pre-standard draft. If steady_clock is not
# available, don't define HAVE_STEADY_CLOCK and replace steady_clock # available, don't define HAVE_STEADY_CLOCK and replace steady_clock
# with monotonic_clock. # with monotonic_clock.
AC_LANG_PUSH(C++)
AC_MSG_CHECKING([whether std::chrono::steady_clock is available]) AC_MSG_CHECKING([whether std::chrono::steady_clock is available])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM( AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
[[ [[
@@ -186,81 +132,49 @@ auto tp = std::chrono::steady_clock::now();
[Define to 1 if you have the `std::chrono::steady_clock`.]) [Define to 1 if you have the `std::chrono::steady_clock`.])
AC_MSG_RESULT([yes])], AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])]) [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() AC_LANG_POP()
# Checks for libraries. # Checks for libraries.
# Additional libraries required for tests. # Additional libraries required for tests.
TESTLDADD= TESTS_LIBS=
# Additional libraries required for programs under src directory. # Additional libraries required for programs under src directory.
APPLDFLAGS= SRC_LIBS=
LIBS_OLD=$LIBS LIBS_OLD=$LIBS
# Search for dlsym function, which is used in tests. Linux needs -ldl, # Search for dlsym function, which is used in tests. Linux needs -ldl,
# but netbsd does not need it. # but netbsd does not need it.
AC_SEARCH_LIBS([dlsym], [dl]) AC_SEARCH_LIBS([dlsym], [dl])
TESTLDADD="$LIBS $TESTLDADD" TESTS_LIBS="$LIBS $TESTS_LIBS"
LIBS=$LIBS_OLD LIBS=$LIBS_OLD
LIBS_OLD=$LIBS LIBS_OLD=$LIBS
AC_SEARCH_LIBS([clock_gettime], [rt], AC_SEARCH_LIBS([clock_gettime], [rt],
[AC_DEFINE([HAVE_CLOCK_GETTIME], [1], [AC_DEFINE([HAVE_CLOCK_GETTIME], [1],
[Define to 1 if you have the `clock_gettime`.])]) [Define to 1 if you have the `clock_gettime`.])])
APPLDFLAGS="$LIBS $APPLDFLAGS" SRC_LIBS="$LIBS $SRC_LIBS"
LIBS=$LIBS_OLD LIBS=$LIBS_OLD
case "$host" in case "$host" in
*android*) *android*)
android_build=yes android_build=yes
# android does not need -pthread, but needs followng 3 libs for C++ # android does not need -pthread, but needs followng 2 libs for C++
APPLDFLAGS="$APPLDFLAGS -lstdc++ -latomic -lsupc++" SRC_LIBS="$SRC_LIBS -lstdc++ -lsupc++"
;; ;;
*) *)
PTHREAD_LDFLAGS="-pthread" SRC_LIBS="$SRC_LIBS -pthread"
APPLDFLAGS="$APPLDFLAGS $PTHREAD_LDFLAGS"
;; ;;
esac esac
# zlib # zlib
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3], [have_zlib=yes], [have_zlib=no]) if test "x$android_build" = "xyes"; then
# Use zlib provided by NDK
if test "x${have_zlib}" = "xno"; then LIBS="-lz $LIBS"
AC_MSG_NOTICE($ZLIB_PKG_ERRORS) else
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3])
LIBS="$ZLIB_LIBS $LIBS"
CFLAGS="$CFLAGS $ZLIB_CFLAGS"
fi fi
# cunit # cunit
@@ -292,22 +206,6 @@ fi
AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ]) 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) # openssl (for src)
PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1], PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1],
[have_openssl=yes], [have_openssl=no]) [have_openssl=yes], [have_openssl=no])
@@ -315,7 +213,7 @@ if test "x${have_openssl}" = "xno"; then
AC_MSG_NOTICE($OPENSSL_PKG_ERRORS) AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
fi fi
# libevent_openssl (for examples) # libevent_openssl (for src)
# 2.0.8 is required because we use evconnlistener_set_error_cb() # 2.0.8 is required because we use evconnlistener_set_error_cb()
PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8], PKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],
[have_libevent_openssl=yes], [have_libevent_openssl=no]) [have_libevent_openssl=yes], [have_libevent_openssl=no])
@@ -323,13 +221,10 @@ if test "x${have_libevent_openssl}" = "xno"; then
AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS) AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS)
fi fi
# jansson (for src/nghttp, src/deflatehd and src/inflatehd) # jansson (for hdtest/deflatehd and hdtest/inflatehd)
PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5], PKG_CHECK_MODULES([JANSSON], [jansson >= 2.5],
[have_jansson=yes], [have_jansson=no]) [have_jansson=yes], [have_jansson=no])
if test "x${have_jansson}" == "xyes"; then if test "x${have_jansson}" == "xno"; then
AC_DEFINE([HAVE_JANSSON], [1],
[Define to 1 if you have `libjansson` library.])
else
AC_MSG_NOTICE($JANSSON_PKG_ERRORS) AC_MSG_NOTICE($JANSSON_PKG_ERRORS)
fi fi
@@ -349,85 +244,23 @@ fi
AM_CONDITIONAL([HAVE_LIBXML2], [ test "x${have_libxml2}" = "xyes" ]) AM_CONDITIONAL([HAVE_LIBXML2], [ test "x${have_libxml2}" = "xyes" ])
# jemalloc # spdylay (for src/nghttpx)
have_jemalloc=no PKG_CHECK_MODULES([LIBSPDYLAY], [libspdylay >= 1.2.3],
if test "x${request_jemalloc}" != "xno"; then [have_spdylay=yes], [have_spdylay=no])
LIBS_OLD=$LIBS if test "x${have_spdylay}" = "xyes"; then
AC_SEARCH_LIBS([malloc_stats_print], [jemalloc], [have_jemalloc=yes], [], AC_DEFINE([HAVE_SPDYLAY], [1], [Define to 1 if you have `spdylay` library.])
[$PTHREAD_LDFLAGS]) else
LIBS=$LIBS_OLD AC_MSG_NOTICE($LIBSPDYLAY_PKG_ERRORS)
AC_MSG_NOTICE([The SPDY support in nghttpx will be disabled.])
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 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],
[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.])
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" ]) AM_CONDITIONAL([HAVE_SPDYLAY], [ test "x${have_spdylay}" = "xyes" ])
# Check Boost Asio library # The nghttp, nghttpd and nghttpx under src depend on OpenSSL and
have_asio_lib=no # libevent_openssl
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
enable_app=no enable_app=no
if test "x${request_app}" != "xno" && if test "x${request_app}" != "xno" &&
test "x${have_zlib}" = "xyes" &&
test "x${have_openssl}" = "xyes" && test "x${have_openssl}" = "xyes" &&
test "x${have_libev}" = "xyes"; then test "x${have_libevent_openssl}" = "xyes"; then
enable_app=yes enable_app=yes
fi fi
@@ -452,16 +285,6 @@ fi
AM_CONDITIONAL([ENABLE_HPACK_TOOLS], [ test "x${enable_hpack_tools}" = "xyes" ]) 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 # The example programs depend on OpenSSL and libevent_openssl
enable_examples=no enable_examples=no
if test "x${request_examples}" != "xno" && if test "x${request_examples}" != "xno" &&
@@ -494,39 +317,30 @@ fi
AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS], AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS],
[test "x${enable_python_bindings}" = "xyes"]) [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 # 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" ]) AM_CONDITIONAL([ENABLE_FAILMALLOC], [ test "x${enable_failmalloc}" = "xyes" ])
# Checks for header files. # Checks for header files.
AC_HEADER_ASSERT AC_HEADER_ASSERT
AC_CHECK_HEADERS([ \ AC_CHECK_HEADERS([ \
arpa/inet.h \ arpa/inet.h \
fcntl.h \
inttypes.h \
limits.h \
netdb.h \
netinet/in.h \ netinet/in.h \
pwd.h \ pwd.h \
stddef.h \ stddef.h \
stdint.h \ stdint.h \
stdlib.h \ stdlib.h \
string.h \ string.h \
sys/socket.h \
sys/time.h \
syslog.h \
time.h \ time.h \
unistd.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. # Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T AC_TYPE_SSIZE_T
@@ -534,133 +348,42 @@ AC_TYPE_UINT8_T
AC_TYPE_UINT16_T AC_TYPE_UINT16_T
AC_TYPE_UINT32_T AC_TYPE_UINT32_T
AC_TYPE_UINT64_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_CHECK_TYPES([ptrdiff_t])
AC_C_BIGENDIAN AC_C_BIGENDIAN
AC_C_INLINE
AC_SYS_LARGEFILE 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. # Checks for library functions.
if test "x$cross_compiling" != "xyes"; then if test "x$cross_compiling" != "xyes"; then
AC_FUNC_MALLOC AC_FUNC_MALLOC
fi 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([ \ AC_CHECK_FUNCS([ \
_Exit \
accept4 \
dup2 \
getcwd \
getpwnam \ getpwnam \
localtime_r \
memchr \
memmove \ memmove \
memset \ memset \
socket \
sqrt \
strchr \
strdup \
strerror \
strndup \
strstr \
strtol \
strtoul \
timegm \ 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], if test "x$maintainer_mode" != "xno"; then
[have_timerfd_create=yes], [have_timerfd_create=no]) CFLAGS="$CFLAGS -Wall -Wextra -Werror"
CFLAGS="$CFLAGS -Wmissing-prototypes -Wstrict-prototypes"
CFLAGS="$CFLAGS -Wmissing-declarations -Wpointer-arith"
# Checks for epoll availability, primarily for examples/tiny-nghttpd CFLAGS="$CFLAGS -Wdeclaration-after-statement"
AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no]) CFLAGS="$CFLAGS -Wformat-security"
CFLAGS="$CFLAGS -Wwrite-strings -Wshadow -Winline -Wnested-externs"
AM_CONDITIONAL([ENABLE_TINY_NGHTTPD], CFLAGS="$CFLAGS -Wfloat-equal -Wundef -Wendif-labels -Wempty-body"
[ test "x${have_epoll}" = "xyes" && CFLAGS="$CFLAGS -Wcast-align -Wclobbered -Wvla"
test "x${have_timerfd_create}" = "xyes"]) CFLAGS="$CFLAGS -Wno-unused-parameter"
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"])
fi fi
WARNCFLAGS=$CFLAGS AC_SUBST([TESTS_LIBS])
CFLAGS=$ac_save_CFLAGS AC_SUBST([SRC_LIBS])
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_CONFIG_FILES([ AC_CONFIG_FILES([
Makefile Makefile
@@ -672,33 +395,16 @@ AC_CONFIG_FILES([
tests/testdata/Makefile tests/testdata/Makefile
third-party/Makefile third-party/Makefile
src/Makefile src/Makefile
src/includes/Makefile
src/libnghttp2_asio.pc
examples/Makefile examples/Makefile
python/Makefile python/Makefile
python/setup.py
integration-tests/Makefile
integration-tests/config.go
integration-tests/setenv
doc/Makefile doc/Makefile
doc/conf.py doc/conf.py
doc/index.rst doc/index.rst
doc/package_README.rst doc/package_README.rst
doc/tutorial-client.rst doc/tutorial-client.rst
doc/tutorial-server.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/nghttp2.h.rst
doc/nghttp2ver.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 AC_OUTPUT
@@ -709,7 +415,6 @@ AC_MSG_NOTICE([summary of build options:
Install prefix: ${prefix} Install prefix: ${prefix}
C compiler: ${CC} C compiler: ${CC}
CFLAGS: ${CFLAGS} CFLAGS: ${CFLAGS}
WARNCFLAGS: ${WARNCFLAGS}
LDFLAGS: ${LDFLAGS} LDFLAGS: ${LDFLAGS}
LIBS: ${LIBS} LIBS: ${LIBS}
CPPFLAGS: ${CPPFLAGS} CPPFLAGS: ${CPPFLAGS}
@@ -718,36 +423,22 @@ AC_MSG_NOTICE([summary of build options:
CXXFLAGS: ${CXXFLAGS} CXXFLAGS: ${CXXFLAGS}
CXXCPP: ${CXXCPP} CXXCPP: ${CXXCPP}
Library types: Shared=${enable_shared}, Static=${enable_static} Library types: Shared=${enable_shared}, Static=${enable_static}
Python: Python: ${PYTHON}
Python: ${PYTHON} PYTHON_VERSION: ${PYTHON_VERSION}
PYTHON_VERSION: ${PYTHON_VERSION} pyexecdir: ${pyexecdir}
pyexecdir: ${pyexecdir} Python-dev: ${have_python_dev}
Python-dev: ${have_python_dev} PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS}
PYTHON_CPPFLAGS:${PYTHON_CPPFLAGS} PYTHON_LDFLAGS: ${PYTHON_LDFLAGS}
PYTHON_LDFLAGS: ${PYTHON_LDFLAGS} Cython: ${CYTHON}
Cython: ${CYTHON} CUnit: ${have_cunit}
Test: OpenSSL: ${have_openssl}
CUnit: ${have_cunit} Libxml2: ${have_libxml2}
Failmalloc: ${enable_failmalloc} Libevent(SSL): ${have_libevent_openssl}
Libs: Spdylay: ${have_spdylay}
OpenSSL: ${have_openssl} Jansson: ${have_jansson}
Libxml2: ${have_libxml2} Applications: ${enable_app}
Libev: ${have_libev} HPACK tools: ${enable_hpack_tools}
Libevent(SSL): ${have_libevent_openssl} Examples: ${enable_examples}
Spdylay: ${have_spdylay} Python bindings:${enable_python_bindings}
Jansson: ${have_jansson} Failmalloc: ${request_failmalloc}
Jemalloc: ${have_jemalloc}
Zlib: ${have_zlib}
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}
]) ])

3
contrib/.gitignore vendored
View File

@@ -1,3 +0,0 @@
nghttpx-init
nghttpx.service
nghttpx-upstart.conf

View File

@@ -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)

View File

@@ -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
:

View File

@@ -1,11 +0,0 @@
/var/log/nghttpx/*.log {
weekly
rotate 52
missingok
compress
delaycompress
notifempty
postrotate
killall -USR1 nghttpx 2> /dev/null || true
endscript
}

View File

@@ -1,8 +0,0 @@
# vim: ft=upstart:
description "HTTP/2 reverse proxy"
start on runlevel [2]
stop on runlevel [016]
exec @bindir@/nghttpx

View File

@@ -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
View File

@@ -1,24 +1,3 @@
# generated files
apiref.rst apiref.rst
asio_http2.h.rst
asio_http2_client.h.rst
asio_http2_server.h.rst
building-android-binary.rst
conf.py conf.py
contribute.rst manual
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

View File

@@ -1,4 +1,4 @@
# nghttp2 - HTTP/2 C Library # nghttp2 - HTTP/2.0 C Library
# Copyright (c) 2012 Tatsuhiro Tsujikawa # Copyright (c) 2012 Tatsuhiro Tsujikawa
@@ -21,149 +21,33 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
man_MANS = nghttp.1 nghttpd.1 nghttpx.1 h2load.1 man_MANS = nghttp.1 nghttpd.1 nghttpx.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_no_recv_client_magic.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_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
EXTRA_DIST = \ EXTRA_DIST = \
mkapiref.py \ mkapiref.py \
README.rst \ README.rst \
programmers-guide.rst \ apiref-header.rst \
$(APIDOCS) \
nghttp.1.rst \
nghttpd.1.rst \
nghttpx.1.rst \
h2load.1.rst \
sources/index.rst \ sources/index.rst \
sources/tutorial-client.rst \ sources/tutorial-client.rst \
sources/tutorial-server.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/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/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 \ _themes/sphinx_rtd_theme/versions.html \
$(man_MANS) \ _themes/sphinx_rtd_theme/searchbox.html \
bash_completion/nghttp \ _themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \
bash_completion/nghttpd \ _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \
bash_completion/nghttpx \ _themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \
bash_completion/h2load _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 # Makefile for Sphinx documentation
# #
@@ -200,33 +84,16 @@ help:
@echo " linkcheck to check all external links for integrity" @echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " doctest to run all doctests embedded in the documentation (if enabled)"
apiref.rst macros.rst enums.rst types.rst: \ apiref.rst: $(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
$(top_builddir)/lib/includes/nghttp2/nghttp2ver.h \
$(top_builddir)/lib/includes/nghttp2/nghttp2.h $(top_builddir)/lib/includes/nghttp2/nghttp2.h
$(PYTHON) $(top_srcdir)/doc/mkapiref.py \ $(PYTHON) $(top_srcdir)/doc/mkapiref.py \
$@ macros.rst enums.rst types.rst . $^ --header $(top_srcdir)/doc/apiref-header.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
clean-local: clean-local:
-rm $(APIDOCS) -rm apiref.rst
-rm -rf $(BUILDDIR)/* -rm -rf $(BUILDDIR)/*
html-local: apiref.rst html: apiref.rst
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo @echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html." @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
@@ -298,7 +165,7 @@ text:
@echo @echo
@echo "Build finished. The text files are in $(BUILDDIR)/text." @echo "Build finished. The text files are in $(BUILDDIR)/text."
man: apiref.rst man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo @echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man." @echo "Build finished. The manual pages are in $(BUILDDIR)/man."

View File

@@ -5,7 +5,7 @@ From https://github.com/ryan-roemer/sphinx-bootstrap-theme.
""" """
import os import os
VERSION = (0, 1, 8) VERSION = (0, 1, 5)
__version__ = ".".join(str(v) for v in VERSION) __version__ = ".".join(str(v) for v in VERSION)
__version_full__ = __version__ __version_full__ = __version__

View File

@@ -6,16 +6,12 @@
{% endfor %} {% endfor %}
<li>{{ title }}</li> <li>{{ title }}</li>
<li class="wy-breadcrumbs-aside"> <li class="wy-breadcrumbs-aside">
{% if pagename != "search" %} {% if display_github %}
{% if display_github %} <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>
<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> {% elif display_bitbucket %}
{% elif display_bitbucket %} <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>
<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 has_source and sourcename %}
{% elif show_source and source_url_prefix %} <a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
<a href="{{ source_url_prefix }}{{ pagename }}{{ source_suffix }}">View page source</a>
{% elif show_source and has_source and sourcename %}
<a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> View page source</a>
{% endif %}
{% endif %} {% endif %}
</li> </li>
</ul> </ul>

View File

@@ -2,10 +2,10 @@
{% if next or prev %} {% if next or prev %}
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation"> <div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
{% if next %} {% 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 %} {% endif %}
{% if prev %} {% 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 %} {% endif %}
</div> </div>
{% endif %} {% endif %}
@@ -28,9 +28,5 @@
</p> </p>
</div> </div>
{%- if show_sphinx %} {% 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 %}
{% 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 %}
</footer> </footer>

View File

@@ -12,7 +12,6 @@
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]--> <!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
{{ metatags }}
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
{% block htmltitle %} {% block htmltitle %}
<title>{{ title|striptags|e }}{{ titlesuffix }}</title> <title>{{ title|striptags|e }}{{ titlesuffix }}</title>
@@ -24,28 +23,49 @@
{% endif %} {% endif %}
{# CSS #} {# 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 %} {% 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 %} {% 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) }}"/> <link rel="search" type="application/opensearchdescription+xml" title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}" href="{{ pathto('_static/opensearch.xml', 1) }}"/>
{% endif %} {% endif %}
{% 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 %} {% if not READTHEDOCS %}
<link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" /> <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 %} {% endif %}
{% for cssfile in css_files %} {% for cssfile in css_files %}
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" /> <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
{% endfor %} {% endfor %}
{% for cssfile in extra_css_files %}
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
{% endfor %}
{%- block linktags %} {%- block linktags %}
{%- if hasdoc('about') %} {%- if hasdoc('about') %}
<link rel="author" title="{{ _('About these documents') }}" <link rel="author" title="{{ _('About these documents') }}"
@@ -74,8 +94,7 @@
{%- endblock %} {%- endblock %}
{%- block extrahead %} {% endblock %} {%- block extrahead %} {% endblock %}
{# Keep modernizr in head - http://modernizr.com/docs/#installing #} <script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js"></script>
<script src="_static/js/modernizr.min.js"></script>
</head> </head>
@@ -86,35 +105,18 @@
{# SIDE NAV, TOGGLES ON MOBILE #} {# SIDE NAV, TOGGLES ON MOBILE #}
<nav data-toggle="wy-nav-shift" class="wy-nav-side"> <nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-nav-search"> <div class="wy-side-nav-search">
{% block sidebartitle %} <a href="{{ pathto(master_doc) }}" class="fa fa-home"> {{ project }}</a>
{% 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>
{% include "searchbox.html" %} {% include "searchbox.html" %}
{% endblock %}
</div> </div>
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation"> <div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
{% block menu %} {% set toctree = toctree(maxdepth=2, collapse=False, includehidden=True) %}
{% set toctree = toctree(maxdepth=4, collapse=False, includehidden=True) %} {% if toctree %}
{% if toctree %} {{ toctree }}
{{ toctree }} {% else %}
{% else %} <!-- Local TOC -->
<!-- Local TOC --> <div class="local-toc">{{ toc }}</div>
<div class="local-toc">{{ toc }}</div> {% endif %}
{% endif %}
{% endblock %}
</div> </div>
&nbsp; &nbsp;
</nav> </nav>
@@ -132,7 +134,7 @@
<div class="wy-nav-content"> <div class="wy-nav-content">
<div class="rst-content"> <div class="rst-content">
{% include "breadcrumbs.html" %} {% include "breadcrumbs.html" %}
<div role="main" class="document"> <div role="main">
{% block body %}{% endblock %} {% block body %}{% endblock %}
</div> </div>
{% include "footer.html" %} {% include "footer.html" %}
@@ -143,39 +145,5 @@
</div> </div>
{% include "versions.html" %} {% 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> </body>
</html> </html>

View File

@@ -10,7 +10,7 @@
{%- extends "layout.html" %} {%- extends "layout.html" %}
{% set title = _('Search') %} {% set title = _('Search') %}
{% set script_files = script_files + ['_static/searchtools.js'] %} {% set script_files = script_files + ['_static/searchtools.js'] %}
{% block footer %} {% block extrahead %}
<script type="text/javascript"> <script type="text/javascript">
jQuery(function() { Search.loadIndex("{{ pathto('searchindex.js', 1) }}"); }); jQuery(function() { Search.loadIndex("{{ pathto('searchindex.js', 1) }}"); });
</script> </script>

View File

@@ -1,9 +1,7 @@
{%- if builder != 'singlehtml' %}
<div role="search"> <div role="search">
<form id="rtd-search-form" class="wy-form" action="{{ pathto('search') }}" method="get"> <form id ="rtd-search-form" class="wy-form" action="{{ pathto('search') }}" method="get">
<input type="text" name="q" placeholder="Search docs" /> <input type="text" name="q" placeholder="Search docs" />
<input type="hidden" name="check_keywords" value="yes" /> <input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" /> <input type="hidden" name="area" value="default" />
</form> </form>
</div> </div>
{%- endif %}

View File

@@ -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}} .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}}
/*# sourceMappingURL=badge_only.css.map */

View File

@@ -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

File diff suppressed because one or more lines are too long

View File

@@ -1,113 +1,47 @@
function toggleCurrent (elem) { $( document ).ready(function() {
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. // Shift nav in mobile when clicking the menu.
$(document).on('click', "[data-toggle='wy-nav-top']", function() { $(document).on('click', "[data-toggle='wy-nav-top']", function() {
$("[data-toggle='wy-nav-shift']").toggleClass("shift"); $("[data-toggle='wy-nav-shift']").toggleClass("shift");
$("[data-toggle='rst-versions']").toggleClass("shift"); $("[data-toggle='rst-versions']").toggleClass("shift");
}); });
// Nav menu link click operations // Close menu when you click a link.
$(document).on('click', ".wy-menu-vertical .current ul li a", function() { $(document).on('click', ".wy-menu-vertical .current ul li a", function() {
var target = $(this); $("[data-toggle='wy-nav-shift']").removeClass("shift");
// Close menu when you click a link. $("[data-toggle='rst-versions']").toggleClass("shift");
$("[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() { $(document).on('click', "[data-toggle='rst-current-version']", function() {
$("[data-toggle='rst-versions']").toggleClass("shift-up"); $("[data-toggle='rst-versions']").toggleClass("shift-up");
}); });
// Make tables responsive // Make tables responsive
$("table.docutils:not(.field-list)").wrap("<div class='wy-table-responsive'></div>"); $("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) { window.SphinxRtdTheme = (function (jquery) {
var stickyNav = (function () { var stickyNav = (function () {
var navBar, var navBar,
win, win,
winScroll = false, stickyNavCssClass = 'stickynav',
linkScroll = false, applyStickNav = function () {
winPosition = 0, if (navBar.height() <= win.height()) {
navBar.addClass(stickyNavCssClass);
} else {
navBar.removeClass(stickyNavCssClass);
}
},
enable = function () { enable = function () {
init(); applyStickNav();
reset(); win.on('resize', applyStickNav);
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);
}, },
init = function () { init = function () {
navBar = jquery('nav.wy-nav-side:first'); navBar = jquery('nav.wy-nav-side:first');
win = jquery(window); 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); jquery(init);
return { return {
enable: enable, enable : enable
hashChange: hashChange
}; };
}()); }());
return { return {
StickyNav: stickyNav StickyNav : stickyNav
}; };
}($)); }($));

View File

@@ -6,4 +6,3 @@ stylesheet = css/theme.css
typekit_id = hiw1hhg typekit_id = hiw1hhg
analytics_id = analytics_id =
sticky_navigation = False sticky_navigation = False
logo_only =

22
doc/apiref-header.rst Normal file
View 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.

View File

@@ -1,5 +0,0 @@
asio_http2.h
============
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2.h
:language: cpp

View File

@@ -1,5 +0,0 @@
asio_http2_client.h
===================
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_client.h
:language: cpp

View File

@@ -1,5 +0,0 @@
asio_http2_server.h
===================
.. literalinclude:: @top_srcdir@/src/includes/nghttp2/asio_http2_server.h
:language: cpp

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1 +0,0 @@
.. include:: @top_srcdir@/doc/sources/building-android-binary.rst

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# nghttp2 - HTTP/2 C Library # nghttp2 - HTTP/2.0 C Library
# Copyright (c) 2012 Tatsuhiro Tsujikawa # Copyright (c) 2012 Tatsuhiro Tsujikawa
@@ -64,7 +64,7 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = u'nghttp2' 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 # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |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 # If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities. # typographically correct entities.
html_use_smartypants = False #html_use_smartypants = True
# Custom sidebar templates, maps document names to template names. # Custom sidebar templates, maps document names to template names.
html_sidebars = { html_sidebars = {
@@ -242,12 +242,6 @@ latex_documents = [
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [ man_pages = [
('nghttp.1', 'nghttp', u'HTTP/2 experimental client', ('index', 'nghttp2', u'nghttp2 Documentation',
[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',
[u'Tatsuhiro Tsujikawa'], 1) [u'Tatsuhiro Tsujikawa'], 1)
] ]

View File

@@ -1 +0,0 @@
.. include:: @top_srcdir@/doc/sources/contribute.rst

View File

@@ -1 +0,0 @@
.. include:: @top_srcdir@/doc/sources/h2load-howto.rst

View File

@@ -1,273 +0,0 @@
.\" Man page generated from reStructuredText.
.
.TH "H2LOAD" "1" "May 24, 2015" "1.0.1" "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
.sp
Default: \fBh2c\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.
.

View File

@@ -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
Default: ``h2c``
.. 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)`

View File

@@ -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)`

View File

@@ -1 +0,0 @@
.. include:: @top_srcdir@/doc/sources/libnghttp2_asio.rst

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
# nghttp2 - HTTP/2 C Library # nghttp2 - HTTP/2.0 C Library
# Copyright (c) 2012 Tatsuhiro Tsujikawa # Copyright (c) 2012 Tatsuhiro Tsujikawa
@@ -24,21 +24,19 @@
# Generates API reference from C source code. # Generates API reference from C source code.
from __future__ import print_function # At least python 2.6 is required from __future__ import print_function # At least python 2.6 is required
import re, sys, argparse, os.path import re, sys, argparse
class FunctionDoc: class FunctionDoc:
def __init__(self, name, content, domain): def __init__(self, name, content, domain):
self.name = name self.name = name
self.content = content self.content = content
self.domain = domain self.domain = domain
if self.domain == 'function':
self.funcname = re.search(r'(nghttp2_[^ )]+)\(', self.name).group(1)
def write(self, out): def write(self, out):
out.write('.. {}:: {}\n'.format(self.domain, self.name)) print('''.. {}:: {}'''.format(self.domain, self.name))
out.write('\n') print('')
for line in self.content: for line in self.content:
out.write(' {}\n'.format(line)) print(' {}'.format(line))
class StructDoc: class StructDoc:
def __init__(self, name, content, members, member_domain): def __init__(self, name, content, members, member_domain):
@@ -49,17 +47,17 @@ class StructDoc:
def write(self, out): def write(self, out):
if self.name: if self.name:
out.write('.. type:: {}\n'.format(self.name)) print('''.. type:: {}'''.format(self.name))
out.write('\n') print('')
for line in self.content: for line in self.content:
out.write(' {}\n'.format(line)) print(' {}'.format(line))
out.write('\n') print('')
for name, content in self.members: for name, content in self.members:
out.write(' .. {}:: {}\n'.format(self.member_domain, name)) print(''' .. {}:: {}'''.format(self.member_domain, name))
out.write('\n') print('')
for line in content: for line in content:
out.write(' {}\n'.format(line)) print(''' {}'''.format(line))
out.write('\n') print('')
class MacroDoc: class MacroDoc:
def __init__(self, name, content): def __init__(self, name, content):
@@ -67,10 +65,10 @@ class MacroDoc:
self.content = content self.content = content
def write(self, out): def write(self, out):
out.write('''.. macro:: {}\n'''.format(self.name)) print('''.. macro:: {}'''.format(self.name))
out.write('\n') print('')
for line in self.content: for line in self.content:
out.write(' {}\n'.format(line)) print(' {}'.format(line))
def make_api_ref(infiles): def make_api_ref(infiles):
macros = [] macros = []
@@ -95,65 +93,19 @@ def make_api_ref(infiles):
enums.append(process_enum(infile)) enums.append(process_enum(infile))
elif doctype == '@macro': elif doctype == '@macro':
macros.append(process_macro(infile)) macros.append(process_macro(infile))
return macros, enums, types, functions
alldocs = [('Macros', macros), alldocs = [('Macros', macros),
('Enums', enums), ('Enums', enums),
('Types (structs, unions and typedefs)', types), ('Types (structs, unions and typedefs)', types),
('Functions', functions)] ('Functions', functions)]
for title, docs in alldocs:
def output( if not docs:
indexfile, macrosfile, enumsfile, typesfile, funcsdir, continue
macros, enums, types, functions): print(title)
indexfile.write(''' print('-'*len(title))
API Reference for doc in docs:
============= doc.write(sys.stdout)
print('')
.. toctree:: print('')
: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)
def process_macro(infile): def process_macro(infile):
content = read_content(infile) content = read_content(infile)
@@ -222,7 +174,6 @@ def process_function(domain, infile):
func_proto = ''.join(func_proto) func_proto = ''.join(func_proto)
func_proto = re.sub(r';\n$', '', func_proto) func_proto = re.sub(r';\n$', '', func_proto)
func_proto = re.sub(r'\s+', ' ', 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) return FunctionDoc(func_proto, content, domain)
def read_content(infile): def read_content(infile):
@@ -248,30 +199,12 @@ def transform_content(content):
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Generate API reference") parser = argparse.ArgumentParser(description="Generate API reference")
parser.add_argument('index', type=argparse.FileType('w'), parser.add_argument('--header', type=argparse.FileType('r'),
help='index output file') help='header inserted at the top of the page')
parser.add_argument('macros', type=argparse.FileType('w'),
help='macros section output file. The filename should be macros.rst')
parser.add_argument('enums', type=argparse.FileType('w'),
help='enums section output file. The filename should be enums.rst')
parser.add_argument('types', type=argparse.FileType('w'),
help='types section output file. The filename should be types.rst')
parser.add_argument('funcsdir',
help='functions doc output dir')
parser.add_argument('files', nargs='+', type=argparse.FileType('r'), parser.add_argument('files', nargs='+', type=argparse.FileType('r'),
help='source file') help='source file')
args = parser.parse_args() args = parser.parse_args()
macros = [] if args.header:
enums = [] print(args.header.read())
types = []
funcs = []
for infile in args.files: for infile in args.files:
m, e, t, f = make_api_ref(args.files) 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)

View File

@@ -1,289 +1,90 @@
.\" Man page generated from reStructuredText. .\" nghttp2 manual page
. .TH nghttp2 "1" "January 2014" "nghttp2" "User Commands"
.TH "NGHTTP" "1" "May 24, 2015" "1.0.1" "nghttp2"
.SH NAME .SH NAME
nghttp \- HTTP/2 experimental client nghttp2 \- HTTP2 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
..
.SH SYNOPSIS .SH SYNOPSIS
.sp \fBnghttp\fP [\fIOPTIONS\fP] \fIURI\fP...
\fBnghttp\fP [OPTIONS]... <URI>...
.SH DESCRIPTION .SH DESCRIPTION
.sp Experimental client for HTTP 2.0.
HTTP/2 experimental client
.INDENT 0.0
.TP
.B <URI>
Specify URI to access.
.UNINDENT
.SH OPTIONS .SH OPTIONS
.INDENT 0.0
.TP .TP
.B \-v, \-\-verbose \fB\-v\fR, \fB\-\-verbose\fR
Print debug information such as reception and Print debug information such as reception/
transmission of frames and name/value pairs. Specifying transmission of frames and name/value pairs.
this option multiple times increases verbosity.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-n, \-\-null\-out \fB\-n\fR, \fB\-\-null\-out\fR
Discard downloaded data. Discard downloaded data.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-O, \-\-remote\-name \fB\-O\fR, \fB\-\-remote\-name\fR
Save download data in the current directory. The Save download data in the current directory.
filename is dereived from URI. If URI ends with \(aq\fI/\fP\(aq, The filename is dereived from URI. If URI
\(aqindex.html\(aq is used as a filename. Not implemented ends with '/', 'index.html' is used as a
yet. filename. Not implemented yet.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-t, \-\-timeout=<DURATION> \fB\-t\fR, \fB\-\-timeout=\fR<N>
Timeout each request after <DURATION>. Set 0 to disable Timeout each request after <N> seconds.
timeout.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-w, \-\-window\-bits=<N> \fB\-w\fR, \fB\-\-window\-bits=\fR<N>
Sets the stream level initial window size to 2**<N>\-1. Sets the stream level initial window size
.UNINDENT to 2**<N>\-1.
.INDENT 0.0
.TP .TP
.B \-W, \-\-connection\-window\-bits=<N> \fB\-W\fR, \fB\-\-connection\-window\-bits=\fR<N>
Sets the connection level initial window size to Sets the connection level initial window
2**<N>\-1. size to 2**<N>\-1.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-a, \-\-get\-assets \fB\-a\fR, \fB\-\-get\-assets\fR
Download assets such as stylesheets, images and script Download assets such as stylesheets, images
files linked from the downloaded resource. Only links and script files linked from the downloaded
whose origins are the same with the linking resource resource. Only links whose origins are the
will be downloaded. nghttp prioritizes resources using same with the linking resource will be
HTTP/2 dependency based priority. The priority order, downloaded.
from highest to lowest, is html itself, css, javascript
and images.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-s, \-\-stat \fB\-s\fR, \fB\-\-stat\fR
Print statistics. Print statistics.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-H, \-\-header=<HEADER> \fB\-H\fR, \fB\-\-header\fR
Add a header to the requests. Example: \fI\%\-H\fP\(aq:method: PUT\(aq Add a header to the requests.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-\-trailer=<HEADER> \fB\-\-cert=\fR<CERT>
Add a trailer header to the requests. <HEADER> must not Use the specified client certificate file.
include pseudo header field (header field name starting The file must be in PEM format.
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
.TP .TP
.B \-\-cert=<CERT> \fB\-\-key=\fR<KEY>
Use the specified client certificate file. The file Use the client private key file. The file
must be in PEM format. must be in PEM format.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-\-key=<KEY> \fB\-d\fR, \fB\-\-data=\fR<FILE>
Use the client private key file. The file must be in Post FILE to server. If \- is given, data
PEM format. will be read from stdin.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-d, \-\-data=<FILE> \fB\-m\fR, \fB\-\-multiply=\fR<N> Request each URI <N> times. By default, same
Post FILE to server. If \(aq\-\(aq is given, data will be read URI is not requested twice. This option
from stdin. disables it too.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-m, \-\-multiply=<N> \fB\-f\fR, \fB\-\-no\-flow\-control\fR
Request each URI <N> times. By default, same URI is not Disables connection and stream level flow
requested twice. This option disables it too. controls.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-u, \-\-upgrade \fB\-u\fR, \fB\-\-upgrade\fR
Perform HTTP Upgrade for HTTP/2. This option is ignored Perform HTTP Upgrade for HTTP/2.0. This
if the request URI has https scheme. If \fI\%\-d\fP is used, the option is ignored if the request URI has
HTTP upgrade request is performed with OPTIONS method. https scheme.
.UNINDENT If \fB\-d\fR is used, the HTTP upgrade request is
.INDENT 0.0 performed with OPTIONS method.
.TP .TP
.B \-p, \-\-weight=<WEIGHT> \fB\-p\fR, \fB\-\-pri=\fR<PRIORITY>
Sets priority group weight. The valid value range is Sets stream priority. Default: 1073741824
[1, 256], inclusive.
.sp
Default: \fB16\fP
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-M, \-\-peer\-max\-concurrent\-streams=<N> \fB\-M\fR, \fB\-\-peer\-max\-concurrent\-streams=\fR<N>
Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS value of Use <N> as SETTINGS_MAX_CONCURRENT_STREAMS
remote endpoint as if it is received in SETTINGS frame. value of remote endpoint as if it is
The default is large enough as it is seen as unlimited. received in SETTINGS frame. The default
.UNINDENT is large enough as it is seen as unlimited.
.INDENT 0.0
.TP .TP
.B \-c, \-\-header\-table\-size=<SIZE> \fB\-c\fR, \fB\-\-header\-table\-size=\fR<N>
Specify decoder header table size. Specify decoder header table size.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-b, \-\-padding=<N> \fB\-\-color\fR
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
Force colored log output. Force colored log output.
.UNINDENT .SH "SEE ALSO"
.INDENT 0.0 nghttpd(1), nghttpx(1)
.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.
.

View File

@@ -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)`

View File

@@ -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)`

View File

@@ -1,194 +1,55 @@
.\" Man page generated from reStructuredText. .\" nghttpd manual page
. .TH nghttpd "1" "January 2014" "nghttpd" "User Commands"
.TH "NGHTTPD" "1" "May 24, 2015" "1.0.1" "nghttp2"
.SH NAME .SH NAME
nghttpd \- HTTP/2 experimental server nghttpd \- HTTP 2.0 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
..
.SH SYNOPSIS .SH SYNOPSIS
.sp \fBnghttpd\fP [\fIOPTIONS\fP...] [\fIPRIVATE_KEY\fP \fICERT\fP]
\fBnghttpd\fP [OPTION]... <PORT> [<PRIVATE_KEY> <CERT>]
.SH DESCRIPTION .SH DESCRIPTION
.sp Experimental HTTP 2.0 server.
HTTP/2 experimental server .SH "Positional arguments"
.INDENT 0.0
.TP .TP
.B <PORT> \fIPRIVATE_KEY\fP
Specify listening port number. Set path to server's private key. Required
.UNINDENT unless either \fB\-p\fR or \fB\-\-client\fR is specified.
.INDENT 0.0
.TP .TP
.B <PRIVATE_KEY> \fICERT\fP
Set path to server\(aqs private key. Required unless Set path to server's certificate. Required
\fI\%\-\-no\-tls\fP is specified. unless either \fB\-p\fR or \fB\-\-client\fR is specified.
.UNINDENT
.INDENT 0.0
.TP
.B <CERT>
Set path to server\(aqs certificate. Required unless
\fI\%\-\-no\-tls\fP is specified.
.UNINDENT
.SH OPTIONS .SH OPTIONS
.INDENT 0.0
.TP .TP
.B \-a, \-\-address=<ADDR> \fB\-D\fR, \fB\-\-daemon\fR
The address to bind to. If not specified the default IP Run in a background. If \fB\-D\fR is used, the
address determined by getaddrinfo is used. current working directory is changed to '/'.
.UNINDENT Therefore if this option is used, \fB\-d\fR option
.INDENT 0.0 must be specified.
.TP .TP
.B \-D, \-\-daemon \fB\-V\fR, \fB\-\-verify\-client\fR
Run in a background. If \fI\%\-D\fP is used, the current working The server sends a client certificate
directory is changed to \(aq\fI/\fP\(aq. Therefore if this option request. If the client did not return a
is used, \fI\%\-d\fP option must be specified. certificate, the handshake is terminated.
.UNINDENT Currently, this option just requests a
.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
client certificate and does not verify it. client certificate and does not verify it.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-d, \-\-htdocs=<PATH> \fB\-d\fR, \fB\-\-htdocs=\fR<PATH>
Specify document root. If this option is not specified, Specify document root. If this option is
the document root is the current working directory. not specified, the document root is the
.UNINDENT current working directory.
.INDENT 0.0
.TP .TP
.B \-v, \-\-verbose \fB\-v\fR, \fB\-\-verbose\fR
Print debug information such as reception/ transmission Print debug information such as reception/
of frames and name/value pairs. transmission of frames and name/value pairs.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-\-no\-tls \fB\-\-no\-tls\fR
Disable SSL/TLS. Disable SSL/TLS.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-c, \-\-header\-table\-size=<SIZE> \fB\-f\fR, \fB\-\-no\-flow\-control\fR
Specify decoder header table size. Disables connection and stream level flow
.UNINDENT controls.
.INDENT 0.0
.TP .TP
.B \-\-color \fB\-\-color\fR
Force colored log output. Force colored log output.
.UNINDENT
.INDENT 0.0
.TP .TP
.B \-p, \-\-push=<PATH>=<PUSH_PATH,...> \fB\-h\fR, \fB\-\-help\fR
Push resources <PUSH_PATH>s when <PATH> is requested. Print this help.
This option can be used repeatedly to specify multiple .SH "SEE ALSO"
push configurations. <PATH> and <PUSH_PATH>s are nghttp(1), nghttpx(1)
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.
.

View File

@@ -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)`

View File

@@ -1,4 +0,0 @@
SEE ALSO
--------
:manpage:`nghttp(1)`, :manpage:`nghttpx(1)`, :manpage:`h2load(1)`

View File

@@ -1 +0,0 @@
.. include:: @top_srcdir@/doc/sources/nghttpx-howto.rst

File diff suppressed because it is too large Load Diff

View File

@@ -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 only in
HTTP/1.1 frontend. 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)`

View File

@@ -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)`

View File

@@ -1,105 +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.
`nghttp2_session_send()` and `nghttp2_session_mem_send()` send first
24 bytes of client magic string (MAGIC)
(:macro:`NGHTTP2_CLIENT_MAGIC`) on client configuration. The
applications are responsible to send SETTINGS frame as part of
connection preface using `nghttp2_submit_settings()`. Similarly,
`nghttp2_session_recv()` and `nghttp2_session_mem_recv()` consume
MAGIC on server configuration unless
`nghttp2_option_set_no_recv_client_magic()` is used with nonzero
option value.
.. _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.

View File

@@ -1 +0,0 @@
.. include:: @top_srcdir@/doc/sources/python-apiref.rst

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -3,11 +3,11 @@
You can adapt this file completely to your liking, but it should at least You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive. 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 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>`_. The project is hosted at `github.com/tatsuhiro-t/nghttp2 <https://github.com/tatsuhiro-t/nghttp2>`_.
@@ -17,37 +17,24 @@ Contents:
:maxdepth: 2 :maxdepth: 2
package_README package_README
contribute
building-android-binary
tutorial-client tutorial-client
tutorial-server tutorial-server
tutorial-hpack
nghttp.1
nghttpd.1
nghttpx.1
h2load.1
nghttpx-howto
h2load-howto
programmers-guide
apiref apiref
libnghttp2_asio
python-apiref
nghttp2.h nghttp2.h
nghttp2ver.h nghttp2ver.h
asio_http2_server.h
asio_http2_client.h
asio_http2.h
Source <https://github.com/tatsuhiro-t/nghttp2> Source <https://github.com/tatsuhiro-t/nghttp2>
Issues <https://github.com/tatsuhiro-t/nghttp2/issues> Issues <https://github.com/tatsuhiro-t/nghttp2/issues>
nghttp2.org <https://nghttp2.org/>
Released Versions 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 Resources
--------- ---------
* HTTP/2 https://tools.ietf.org/html/rfc7540 * http://tools.ietf.org/html/draft-ietf-httpbis-http2-09
* HPACK https://tools.ietf.org/html/rfc7541 * http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05

View File

@@ -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.

View File

@@ -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.

View File

@@ -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()

View File

@@ -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 client. The complete source code, `libevent-client.c`_, is attached at
the end of this page. It also resides in examples directory in the the end of this page. It also resides in examples directory in the
archive or repository. archive or repository.
@@ -19,13 +19,15 @@ function ``main()`` and ``run()``, which is not so relevant to nghttp2
library use. The one thing you should look at is setup NPN callback. 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 The NPN callback is used for the client to select the next application
protocol over the SSL/TLS transport. In this tutorial, we use 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:: protocol the library supports::
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out, static int select_next_proto_cb(SSL* ssl,
unsigned char *outlen, const unsigned char *in, unsigned char **out, unsigned char *outlen,
unsigned int inlen, void *arg _U_) { const unsigned char *in, unsigned int inlen,
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) { void *arg)
{
if(nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID); errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
} }
return SSL_TLSEXT_ERR_OK; return SSL_TLSEXT_ERR_OK;
@@ -34,23 +36,23 @@ protocol the library supports::
The callback is set to the SSL_CTX object using The callback is set to the SSL_CTX object using
``SSL_CTX_set_next_proto_select_cb()`` function:: ``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;
ssl_ctx = SSL_CTX_new(SSLv23_client_method()); ssl_ctx = SSL_CTX_new(SSLv23_client_method());
if (!ssl_ctx) { if(!ssl_ctx) {
errx(1, "Could not create SSL/TLS context: %s", errx(1, "Could not create SSL/TLS context: %s",
ERR_error_string(ERR_get_error(), NULL)); ERR_error_string(ERR_get_error(), NULL));
} }
SSL_CTX_set_options(ssl_ctx, SSL_CTX_set_options(ssl_ctx,
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
return ssl_ctx; return ssl_ctx;
} }
We use ``http2_session_data`` structure to store the data related to We use ``http2_session_data`` structure to store the data related to
the HTTP/2 session:: the HTTP/2.0 session::
typedef struct { typedef struct {
nghttp2_session *session; nghttp2_session *session;
@@ -65,11 +67,11 @@ its stream specific data in ``http2_stream_data`` structure and the
defined as follows:: defined as follows::
typedef struct { typedef struct {
/* The NULL-terminated URI string to retrieve. */ /* The NULL-terminated URI string to retreive. */
const char *uri; const char *uri;
/* Parsed result of the |uri| */ /* Parsed result of the |uri| */
struct http_parser_url *u; 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; char *authority;
/* The path portion of the |uri|, including query, not /* The path portion of the |uri|, including query, not
NULL-terminated */ NULL-terminated */
@@ -89,22 +91,25 @@ respectively.
Then we call function ``initiate_connection()`` to start connecting to Then we call function ``initiate_connection()`` to start connecting to
the remote server:: 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, const char *host, uint16_t port,
http2_session_data *session_data) { http2_session_data *session_data)
{
int rv; int rv;
struct bufferevent *bev; struct bufferevent *bev;
SSL *ssl; SSL *ssl;
ssl = create_ssl(ssl_ctx); ssl = create_ssl(ssl_ctx);
bev = bufferevent_openssl_socket_new( bev = bufferevent_openssl_socket_new(evbase, -1, ssl,
evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING, BUFFEREVENT_SSL_CONNECTING,
BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE); BEV_OPT_DEFER_CALLBACKS |
BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data); bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase, rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
AF_UNSPEC, host, port); AF_UNSPEC, host, port);
if (rv != 0) { if(rv != 0) {
errx(1, "Could not connect to the remote host %s", host); errx(1, "Could not connect to the remote host %s", host);
} }
session_data->bev = bev; session_data->bev = bev;
@@ -117,9 +122,10 @@ The ``eventcb()`` is invoked by libevent event loop when an event
(e.g., connection has been established, timeout, etc) happens on the (e.g., connection has been established, timeout, etc) happens on the
underlying network socket:: 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) { http2_session_data *session_data = (http2_session_data*)ptr;
if(events & BEV_EVENT_CONNECTED) {
int fd = bufferevent_getfd(bev); int fd = bufferevent_getfd(bev);
int val = 1; int val = 1;
fprintf(stderr, "Connected\n"); fprintf(stderr, "Connected\n");
@@ -127,16 +133,16 @@ underlying network socket::
initialize_nghttp2_session(session_data); initialize_nghttp2_session(session_data);
send_client_connection_header(session_data); send_client_connection_header(session_data);
submit_request(session_data); submit_request(session_data);
if (session_send(session_data) != 0) { if(session_send(session_data) != 0) {
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
} }
return; return;
} }
if (events & BEV_EVENT_EOF) { if(events & BEV_EVENT_EOF) {
warnx("Disconnected from the remote host"); warnx("Disconnected from the remote host");
} else if (events & BEV_EVENT_ERROR) { } else if(events & BEV_EVENT_ERROR) {
warnx("Network error"); warnx("Network error");
} else if (events & BEV_EVENT_TIMEOUT) { } else if(events & BEV_EVENT_TIMEOUT) {
warnx("Timeout"); warnx("Timeout");
} }
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
@@ -148,31 +154,18 @@ event, we just simply tear down the connection. The
finished successfully. We first initialize nghttp2 session object in finished successfully. We first initialize nghttp2 session object in
``initialize_nghttp2_session()`` function:: ``initialize_nghttp2_session()`` function::
static void initialize_nghttp2_session(http2_session_data *session_data) { static void initialize_nghttp2_session(http2_session_data *session_data)
nghttp2_session_callbacks *callbacks; {
nghttp2_session_callbacks callbacks = {0};
nghttp2_session_callbacks_new(&callbacks); callbacks.send_callback = send_callback;
callbacks.before_frame_send_callback = before_frame_send_callback;
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); callbacks.on_frame_recv_callback = on_frame_recv_callback;
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, callbacks.on_stream_close_callback = on_stream_close_callback;
on_frame_recv_callback); callbacks.on_header_callback = on_header_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
nghttp2_session_callbacks_set_on_data_chunk_recv_callback( nghttp2_session_client_new(&session_data->session, &callbacks, session_data);
callbacks, on_data_chunk_recv_callback);
nghttp2_session_callbacks_set_on_stream_close_callback(
callbacks, on_stream_close_callback);
nghttp2_session_callbacks_set_on_header_callback(callbacks,
on_header_callback);
nghttp2_session_callbacks_set_on_begin_headers_callback(
callbacks, on_begin_headers_callback);
nghttp2_session_client_new(&session_data->session, callbacks, session_data);
nghttp2_session_callbacks_del(callbacks);
} }
Since we are creating client, we use `nghttp2_session_client_new()` to Since we are creating client, we use `nghttp2_session_client_new()` to
@@ -183,27 +176,31 @@ The `delete_http2_session_data()` destroys ``session_data`` and frees
its bufferevent, so it closes underlying connection as well. It also its bufferevent, so it closes underlying connection as well. It also
calls `nghttp2_session_del()` to delete nghttp2 session object. 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 string (:macro:`NGHTTP2_CLIENT_MAGIC`) which is 24 bytes magic byte sequence
followed by SETTINGS frame. First 24 bytes magic string is (:macro:`NGHTTP2_CLIENT_CONNECTION_HEADER`) followed by SETTINGS
automatically sent by nghttp2 library. We send SETTINGS frame in frame. The transmission of client connection header is done in
``send_client_connection_header()``:: ``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_entry iv[1] = {
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
};
int rv; int rv;
/* client 24 bytes magic string will be sent by nghttp2 library */ bufferevent_write(session_data->bev,
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, NGHTTP2_CLIENT_CONNECTION_HEADER,
ARRLEN(iv)); NGHTTP2_CLIENT_CONNECTION_HEADER_LEN);
if (rv != 0) { 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)); errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
} }
} }
Here we specify SETTINGS_MAX_CONCURRENT_STREAMS to 100, which is 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 demonstrating the use of SETTINGS frame. To queue the SETTINGS frame
for the transmission, we use `nghttp2_submit_settings()`. Note that for the transmission, we use `nghttp2_submit_settings()`. Note that
`nghttp2_submit_settings()` function only queues the frame and not `nghttp2_submit_settings()` function only queues the frame and not
@@ -214,26 +211,26 @@ used, which is described about later.
After the transmission of client connection header, we enqueue HTTP After the transmission of client connection header, we enqueue HTTP
request in ``submit_request()`` function:: request in ``submit_request()`` function::
static void submit_request(http2_session_data *session_data) { static void submit_request(http2_session_data *session_data)
int32_t stream_id; {
int rv;
http2_stream_data *stream_data = session_data->stream_data; http2_stream_data *stream_data = session_data->stream_data;
const char *uri = stream_data->uri; const char *uri = stream_data->uri;
const struct http_parser_url *u = stream_data->u; const struct http_parser_url *u = stream_data->u;
nghttp2_nv hdrs[] = { nghttp2_nv hdrs[] = {
MAKE_NV2(":method", "GET"), MAKE_NV2(":method", "GET"),
MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off], MAKE_NV(":scheme",
u->field_data[UF_SCHEMA].len), &uri[u->field_data[UF_SCHEMA].off], u->field_data[UF_SCHEMA].len),
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen), 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"); fprintf(stderr, "Request headers:\n");
print_headers(stderr, hdrs, ARRLEN(hdrs)); print_headers(stderr, hdrs, ARRLEN(hdrs));
stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs, rv = nghttp2_submit_request(session_data->session, NGHTTP2_PRI_DEFAULT,
ARRLEN(hdrs), NULL, stream_data); hdrs, ARRLEN(hdrs), NULL, stream_data);
if (stream_id < 0) { if(rv != 0) {
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id)); 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 We build HTTP request header fields in ``hdrs`` which is an array of
@@ -242,31 +239,25 @@ We build HTTP request header fields in ``hdrs`` which is an array of
we use `nghttp2_submit_request()` function. The `stream_data` is we use `nghttp2_submit_request()` function. The `stream_data` is
passed in *stream_user_data* parameter. It is used in nghttp2 passed in *stream_user_data* parameter. It is used in nghttp2
callbacks which we'll describe about later. 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 The next bufferevent callback is ``readcb()``, which is invoked when
data is available to read in the bufferevent input buffer:: 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; http2_session_data *session_data = (http2_session_data*)ptr;
int rv;
struct evbuffer *input = bufferevent_get_input(bev); struct evbuffer *input = bufferevent_get_input(bev);
size_t datalen = evbuffer_get_length(input); size_t datalen = evbuffer_get_length(input);
unsigned char *data = evbuffer_pullup(input, -1); unsigned char *data = evbuffer_pullup(input, -1);
rv = nghttp2_session_mem_recv(session_data->session, data, datalen);
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); if(rv < 0) {
if (readlen < 0) { warnx("Fatal error: %s", nghttp2_strerror(rv));
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
return; return;
} }
if (evbuffer_drain(input, readlen) != 0) { evbuffer_drain(input, rv);
warnx("Fatal error: evbuffer_drain failed"); if(session_send(session_data) != 0) {
delete_http2_session_data(session_data);
return;
}
if (session_send(session_data) != 0) {
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
return; return;
} }
@@ -279,11 +270,12 @@ invoke nghttp2 callbacks and also queue frames. Since there may be
pending frames, we call ``session_send()`` function to send those pending frames, we call ``session_send()`` function to send those
frames. The ``session_send()`` function is defined as follows:: 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; int rv;
rv = nghttp2_session_send(session_data->session); rv = nghttp2_session_send(session_data->session);
if (rv != 0) { if(rv != 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv)); warnx("Fatal error: %s", nghttp2_strerror(rv));
return -1; return -1;
} }
@@ -291,13 +283,17 @@ frames. The ``session_send()`` function is defined as follows::
} }
The `nghttp2_session_send()` function serializes the frame into wire The `nghttp2_session_send()` function serializes the frame into wire
format and call ``send_callback()`` function of type format and call :member:`nghttp2_session_callbacks.send_callback` with
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as it. We set ``send_callback()`` function to
follows:: :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, static ssize_t send_callback(nghttp2_session *session,
size_t length, int flags _U_, void *user_data) { const uint8_t *data, size_t length,
http2_session_data *session_data = (http2_session_data *)user_data; int flags, void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
struct bufferevent *bev = session_data->bev; struct bufferevent *bev = session_data->bev;
bufferevent_write(bev, data, length); bufferevent_write(bev, data, length);
return length; return length;
@@ -307,23 +303,25 @@ Since we use bufferevent to abstract network I/O, we just write the
data to the bufferevent object. Note that `nghttp2_session_send()` data to the bufferevent object. Note that `nghttp2_session_send()`
continues to write all frames queued so far. If we were writing the continues to write all frames queued so far. If we were writing the
data to the non-blocking socket directly using ``write()`` system call data to the non-blocking socket directly using ``write()`` system call
in the ``send_callback()``, we will surely get ``EAGAIN`` or in the :member:`nghttp2_session_callbacks.send_callback`, we will
``EWOULDBLOCK`` since the socket has limited send buffer. If that surely get ``EAGAIN`` or ``EWOULDBLOCK`` since the socket has limited
happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the send buffer. If that happens, we can return
nghttp2 library to stop sending further data. But writing to the :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the nghttp2 library to stop
bufferevent, we have to regulate the amount data to be buffered by sending further data. But writing to the bufferevent, we have to
ourselves to avoid possible huge memory consumption. In this example regulate the amount data to be buffered by ourselves to avoid possible
client, we do not limit anything. To see how to regulate the amount of huge memory consumption. In this example client, we do not limit
buffered data, see the ``send_callback()`` in the server tutorial. 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 The third bufferevent callback is ``writecb()``, which is invoked when
all data written in the bufferevent output buffer have been sent:: 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 && http2_session_data *session_data = (http2_session_data*)ptr;
nghttp2_session_want_write(session_data->session) == 0 && if(nghttp2_session_want_read(session_data->session) == 0 &&
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) { nghttp2_session_want_write(session_data->session) == 0 &&
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
} }
} }
@@ -346,19 +344,62 @@ We have already described about nghttp2 callback ``send_callback()``.
Let's describe remaining nghttp2 callbacks we setup in Let's describe remaining nghttp2 callbacks we setup in
``initialize_nghttp2_setup()`` function. ``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 Each request header name/value pair is emitted via
``on_header_callback`` function:: ``on_header_callback`` function::
static int on_header_callback(nghttp2_session *session _U_, static int on_header_callback(nghttp2_session *session,
const nghttp2_frame *frame, const uint8_t *name, const nghttp2_frame *frame,
size_t namelen, const uint8_t *value, const uint8_t *name, size_t namelen,
size_t valuelen, uint8_t flags _U_, const uint8_t *value, size_t valuelen,
void *user_data) { void *user_data)
http2_session_data *session_data = (http2_session_data *)user_data; {
switch (frame->hd.type) { http2_session_data *session_data = (http2_session_data*)user_data;
switch(frame->hd.type) {
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session_data->stream_data->stream_id == frame->hd.stream_id) { session_data->stream_data->stream_id == frame->hd.stream_id) {
/* Print response headers for the initiated request. */ /* Print response headers for the initiated request. */
print_header(stderr, name, namelen, value, valuelen); print_header(stderr, name, namelen, value, valuelen);
break; break;
@@ -367,18 +408,19 @@ Each request header name/value pair is emitted via
return 0; 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, After all name/value pairs are emitted for a frame,
``on_frame_recv_callback`` function is called:: ``on_frame_recv_callback`` function is called::
static int on_frame_recv_callback(nghttp2_session *session _U_, 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; {
switch (frame->hd.type) { http2_session_data *session_data = (http2_session_data*)user_data;
switch(frame->hd.type) {
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session_data->stream_data->stream_id == frame->hd.stream_id) { session_data->stream_data->stream_id == frame->hd.stream_id) {
fprintf(stderr, "All headers received\n"); fprintf(stderr, "All headers received\n");
} }
break; break;
@@ -387,43 +429,46 @@ After all name/value pairs are emitted for a frame,
} }
In this tutorial, we are just interested in the HTTP response 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 :macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). Also check
its stream ID. its stream ID.
The ``on_data_chunk_recv_callback()`` function is invoked when a chunk The ``on_data_chunk_recv_callback()`` function is invoked when a chunk
of data is received from the remote peer:: of data is received from the remote peer::
static int on_data_chunk_recv_callback(nghttp2_session *session _U_, static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
uint8_t flags _U_, int32_t stream_id, int32_t stream_id,
const uint8_t *data, size_t len, 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) { http2_session_data *session_data = (http2_session_data*)user_data;
if(session_data->stream_data->stream_id == stream_id) {
fwrite(data, len, 1, stdout); fwrite(data, len, 1, stdout);
} }
return 0; return 0;
} }
In our case, a chunk of data is response body. After checking stream 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 output in the terminal may be corrupted if the response body contains
some binary data. some binary data.
The ``on_stream_close_callback()`` function is invoked when the stream The ``on_stream_close_callback()`` function is invoked when the stream
is about to close:: 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, nghttp2_error_code error_code,
void *user_data) { void *user_data)
http2_session_data *session_data = (http2_session_data *)user_data; {
http2_session_data *session_data = (http2_session_data*)user_data;
int rv; int rv;
if (session_data->stream_data->stream_id == stream_id) { if(session_data->stream_data->stream_id == stream_id) {
fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id, fprintf(stderr, "Stream %d closed with error_code=%d\n",
error_code); stream_id, error_code);
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
if (rv != 0) { if(rv != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE; return NGHTTP2_ERR_CALLBACK_FAILURE;
} }
} }
@@ -434,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 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 resource we want (or the stream was reset by RST_STREAM from the
remote peer), we call `nghttp2_session_terminate_session()` to 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 some data associated for the stream to be closed, you may delete it
here. here.

View File

@@ -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.

View File

@@ -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 In this tutorial, we are going to write single-threaded, event-based
HTTP/2 web server, which supports HTTPS only. It can handle HTTP/2.0 web server, which supports HTTPS only. It can handle
concurrent multiple requests, but only the GET method is supported. The concurrent multiple requests, but only GET method is supported. The
complete source code, `libevent-server.c`_, is attached at the end of complete source code, `libevent-server.c`_, is attached at the end of
this page. It also resides in examples directory in the archive or this page. It also resides in examples directory in the archive or
repository. repository.
This simple server takes 3 arguments, a port number to listen to, a path to This simple server takes 3 arguments, a port number to listen to, a
your SSL/TLS private key file and a path to your certificate file. Its path to SSL/TLS private key file and certificate file. Its synopsis
synopsis is like this:: is like this::
$ libevent-server PORT /path/to/server.key /path/to/server.crt $ libevent-server PORT /path/to/server.key /path/to/server.crt
We use libevent in this tutorial to handle networking I/O. Please We use libevent in this tutorial to handle networking I/O. Please
note that nghttp2 itself does not depend on libevent. note that nghttp2 itself does not depend on libevent.
First we create a setup routine for libevent and OpenSSL in the functions First we do some setup routine for libevent and OpenSSL library in
``main()`` and ``run()``. One thing in there you should look at, is the setup function ``main()`` and ``run()``, which is not so relevant to nghttp2
of the NPN callback. The NPN callback is used for the server to advertise library use. The one thing you should look at is setup NPN callback.
which application protocols the server supports to a client. In this example The NPN callback is used for the server to advertise the application
program, when creating ``SSL_CTX`` object, we store the application protocol protocols the server supports to a client. In this example program,
name in the wire format of NPN in a statically allocated buffer. This is safe when creating ``SSL_CTX`` object, we stores the application protocol
because we only create one ``SSL_CTX`` object in the program's entire life name in the wire format of NPN in statically allocated buffer. This is
time:: safe because we only create 1 ``SSL_CTX`` object in the entire program
life time::
static unsigned char next_proto_list[256]; static unsigned char next_proto_list[256];
static size_t next_proto_list_len; static size_t next_proto_list_len;
static int next_proto_cb(SSL *s _U_, const unsigned char **data, static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
unsigned int *len, void *arg _U_) { void *arg)
{
*data = next_proto_list; *data = next_proto_list;
*len = (unsigned int)next_proto_list_len; *len = next_proto_list_len;
return SSL_TLSEXT_ERR_OK; 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()); ssl_ctx = SSL_CTX_new(SSLv23_server_method());
... ...
@@ -53,25 +55,25 @@ time::
return ssl_ctx; return ssl_ctx;
} }
The wire format of NPN is a sequence of length prefixed string. Exactly one The wire format of NPN is a sequence of length prefixed string. The
byte is used to specify the length of each protocol identifier. In this exactly one byte is used to specify the length of each protocol
tutorial, we advertise the specific HTTP/2 protocol version the current identifier. In this tutorial, we advertise the HTTP/2.0 protocol the
nghttp2 library supports. The nghttp2 library exports its identifier in nghttp2 library supports. The nghttp2 library exports its identifier
:macro:`NGHTTP2_PROTO_VERSION_ID`. The ``next_proto_cb()`` function is the in :macro:`NGHTTP2_PROTO_VERSION_ID`. The ``next_proto_cb()`` function
server-side NPN callback. In the OpenSSL implementation, we just assign the is the server-side NPN callback. In OpenSSL implementation, we just
pointer to the NPN buffers we filled in earlier. The NPN callback function is assign the pointer to the NPN buffers we filled earlier. The NPN
set to the ``SSL_CTX`` object using callback function is set to ``SSL_CTX`` object using
``SSL_CTX_set_next_protos_advertised_cb()``. ``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 { struct app_context {
SSL_CTX *ssl_ctx; SSL_CTX *ssl_ctx;
struct event_base *evbase; struct event_base *evbase;
}; };
We use the ``http2_session_data`` structure to store session-level We use ``http2_session_data`` structure to store the session-level
(which corresponds to one HTTP/2 connection) data:: (which corresponds to 1 HTTP/2.0 connection) data::
typedef struct http2_session_data { typedef struct http2_session_data {
struct http2_stream_data root; struct http2_stream_data root;
@@ -79,9 +81,11 @@ We use the ``http2_session_data`` structure to store session-level
app_context *app_ctx; app_context *app_ctx;
nghttp2_session *session; nghttp2_session *session;
char *client_addr; char *client_addr;
size_t handshake_leftlen;
} http2_session_data; } 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 { typedef struct http2_stream_data {
struct http2_stream_data *prev, *next; struct http2_stream_data *prev, *next;
@@ -90,21 +94,26 @@ We use the ``http2_stream_data`` structure to store stream-level data::
int fd; int fd;
} http2_stream_data; } http2_stream_data;
A single HTTP/2 session can have multiple streams. We manage these 1 HTTP/2.0 session can have multiple streams. We manage these
multiple streams with a doubly linked list. The first element of this multiple streams by intrusive doubly linked list to add and remove the
list is pointed to by the ``root->next`` in ``http2_session_data``. object in O(1). The first element of this list is pointed by the
Initially, ``root->next`` is ``NULL``. We use libevent's bufferevent ``root->next`` in ``http2_session_data``. Initially, ``root->next``
structure to perform network I/O. Note that the bufferevent object is is ``NULL``. The ``handshake_leftlen`` member of
kept in ``http2_session_data`` and not in ``http2_stream_data``. This ``http2_session_data`` is used to track the number of bytes remaining
is because ``http2_stream_data`` is just a logical stream multiplexed when receiving first 24 bytes magic value
over the single connection managed by bufferevent in (:macro:`NGHTTP2_CLIENT_CONNECTION_HEADER`) from the client. We use
``http2_session_data``. 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 We first create listener object to accept incoming connections.
libevent's ``struct evconnlistener`` for this purpose:: We use libevent's ``struct evconnlistener`` for this purpose::
static void start_listen(struct event_base *evbase, const char *service, static void start_listen(struct event_base *evbase, const char *service,
app_context *app_ctx) { app_context *app_ctx)
{
int rv; int rv;
struct addrinfo hints; struct addrinfo hints;
struct addrinfo *res, *rp; struct addrinfo *res, *rp;
@@ -115,176 +124,196 @@ libevent's ``struct evconnlistener`` for this purpose::
hints.ai_flags = AI_PASSIVE; hints.ai_flags = AI_PASSIVE;
#ifdef AI_ADDRCONFIG #ifdef AI_ADDRCONFIG
hints.ai_flags |= AI_ADDRCONFIG; hints.ai_flags |= AI_ADDRCONFIG;
#endif /* AI_ADDRCONFIG */ #endif // AI_ADDRCONFIG
rv = getaddrinfo(NULL, service, &hints, &res); rv = getaddrinfo(NULL, service, &hints, &res);
if (rv != 0) { if(rv != 0) {
errx(1, NULL); errx(1, NULL);
} }
for (rp = res; rp; rp = rp->ai_next) { for(rp = res; rp; rp = rp->ai_next) {
struct evconnlistener *listener; struct evconnlistener *listener;
listener = evconnlistener_new_bind( listener = evconnlistener_new_bind(evbase, acceptcb, app_ctx,
evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, LEV_OPT_CLOSE_ON_FREE |
16, rp->ai_addr, rp->ai_addrlen); LEV_OPT_REUSEABLE, -1,
if (listener) { rp->ai_addr, rp->ai_addrlen);
freeaddrinfo(res); if(listener) {
return; return;
} }
} }
errx(1, "Could not start listener"); errx(1, "Could not start listener");
} }
We specify the ``acceptcb`` callback which is called when a new connection is We specify ``acceptcb`` callback which is called when a new connection
accepted:: is accepted::
static void acceptcb(struct evconnlistener *listener _U_, int fd, static void acceptcb(struct evconnlistener *listener, int fd,
struct sockaddr *addr, int addrlen, void *arg) { struct sockaddr *addr, int addrlen, void *arg)
app_context *app_ctx = (app_context *)arg; {
app_context *app_ctx = (app_context*)arg;
http2_session_data *session_data; http2_session_data *session_data;
session_data = create_http2_session_data(app_ctx, fd, addr, addrlen); session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
bufferevent_setcb(session_data->bev, handshake_readcb, NULL, eventcb,
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data); session_data);
} }
Here we create the ``http2_session_data`` object. The bufferevent for Here we create ``http2_session_data`` object. The bufferevent for this
this connection is also initialized at this time. We specify three connection is also initialized at this time. We specify 2 callbacks
callbacks for the bufferevent: ``readcb``, ``writecb`` and for the bufferevent: ``handshake_readcb`` and ``eventcb``.
``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 (e.g., connection has been established, timeout, etc) happens on the
underlying network socket:: 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) { http2_session_data *session_data = (http2_session_data*)ptr;
if(events & BEV_EVENT_CONNECTED) {
fprintf(stderr, "%s connected\n", session_data->client_addr); 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; return;
} }
if (events & BEV_EVENT_EOF) { if(events & BEV_EVENT_EOF) {
fprintf(stderr, "%s EOF\n", session_data->client_addr); fprintf(stderr, "%s EOF\n", session_data->client_addr);
} else if (events & BEV_EVENT_ERROR) { } else if(events & BEV_EVENT_ERROR) {
fprintf(stderr, "%s network error\n", session_data->client_addr); fprintf(stderr, "%s network error\n", session_data->client_addr);
} else if (events & BEV_EVENT_TIMEOUT) { } else if(events & BEV_EVENT_TIMEOUT) {
fprintf(stderr, "%s timeout\n", session_data->client_addr); fprintf(stderr, "%s timeout\n", session_data->client_addr);
} }
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
} }
For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and For ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and ``BEV_EVENT_TIMEOUT``
``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection. event, we just simply tear down the connection. The
The ``delete_http2_session_data()`` function destroys the ``delete_http2_session_data()`` function destroys
``http2_session_data`` object and thus also its bufferevent member. ``http2_session_data`` object and thus its bufferevent member. As a
As a result, the underlying connection is closed. The result, the underlying connection is closed. The
``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is ``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is
finished successfully. Now we are ready to start the HTTP/2 finished successfully.
communication.
We initialize a nghttp2 session object which is done in The ``handshake_readcb()`` is a callback function to handle 24 bytes
``initialize_nghttp2_session()``:: magic byte string from a client, since nghttp2 library does not handle
it::
static void initialize_nghttp2_session(http2_session_data *session_data) { static void handshake_readcb(struct bufferevent *bev, void *ptr)
nghttp2_session_callbacks *callbacks; {
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_session_callbacks_new(&callbacks); if(memcmp(conhead + NGHTTP2_CLIENT_CONNECTION_HEADER_LEN
- session_data->handshake_leftlen, data, readlen) != 0) {
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); delete_http2_session_data(session_data);
return;
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, }
on_frame_recv_callback); session_data->handshake_leftlen -= readlen;
if(session_data->handshake_leftlen == 0) {
nghttp2_session_callbacks_set_on_stream_close_callback( bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, ptr);
callbacks, on_stream_close_callback); /* Process pending data in buffer since they are not notified
further */
nghttp2_session_callbacks_set_on_header_callback(callbacks, initialize_nghttp2_session(session_data);
on_header_callback); if(send_server_connection_header(session_data) != 0) {
delete_http2_session_data(session_data);
nghttp2_session_callbacks_set_on_begin_headers_callback( return;
callbacks, on_begin_headers_callback); }
if(session_recv(session_data) != 0) {
nghttp2_session_server_new(&session_data->session, callbacks, session_data); delete_http2_session_data(session_data);
return;
nghttp2_session_callbacks_del(callbacks); }
}
} }
Since we are creating a server and uses options, the nghttp2 session We check that the received byte string matches
object is created using `nghttp2_session_server_new2()` function. We :macro:`NGHTTP2_CLIENT_CONNECTION_HEADER`. When they match, the
registers five callbacks for nghttp2 session object. We'll talk about connection state is ready for starting HTTP/2.0 communication. First
these callbacks later. 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 We initialize nghttp2 session object which is done in
a server connection header in ``send_server_connection_header()``:: ``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_entry iv[1] = {
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
};
int rv; int rv;
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
ARRLEN(iv)); iv, ARRLEN(iv));
if (rv != 0) { if(rv != 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv)); warnx("Fatal error: %s", nghttp2_strerror(rv));
return -1; return -1;
} }
return 0; return 0;
} }
The server connection header is a SETTINGS frame. We specify The server connection header is SETTINGS frame. We specify
SETTINGS_MAX_CONCURRENT_STREAMS to 100 in the SETTINGS frame. To queue SETTINGS_MAX_CONCURRENT_STREAMS to 100 in SETTINGS frame. To queue
the SETTINGS frame for the transmission, we use the SETTINGS frame for the transmission, we use
`nghttp2_submit_settings()`. Note that `nghttp2_submit_settings()` `nghttp2_submit_settings()`. Note that `nghttp2_submit_settings()`
function only queues the frame and it does not actually send it. All function only queues the frame and not actually send it. All
functions in the ``nghttp2_submit_*()`` family have this property. To ``nghttp2_submit_*()`` family functions have this property. To
actually send the frame, `nghttp2_session_send()` should be used, as actually send the frame, `nghttp2_session_send()` is used, which is
described later. described about later.
Since bufferevent may buffer more than the first 24 bytes from the client, we Since bufferevent may buffer more than first 24 bytes from the client,
have to process them here since libevent won't invoke callback functions for we have to process them here since libevent won't invoke callback
this pending data. To process the received data, we call the functions for these pending data. To process received data, we call
``session_recv()`` function:: ``session_recv()`` function::
static int session_recv(http2_session_data *session_data) { static int session_recv(http2_session_data *session_data)
ssize_t readlen; {
int rv;
struct evbuffer *input = bufferevent_get_input(session_data->bev); struct evbuffer *input = bufferevent_get_input(session_data->bev);
size_t datalen = evbuffer_get_length(input); size_t datalen = evbuffer_get_length(input);
unsigned char *data = evbuffer_pullup(input, -1); unsigned char *data = evbuffer_pullup(input, -1);
rv = nghttp2_session_mem_recv(session_data->session, data, datalen);
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); if(rv < 0) {
if (readlen < 0) { warnx("Fatal error: %s", nghttp2_strerror(rv));
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
return -1; return -1;
} }
if (evbuffer_drain(input, readlen) != 0) { evbuffer_drain(input, rv);
warnx("Fatal error: evbuffer_drain failed"); if(session_send(session_data) != 0) {
return -1;
}
if (session_send(session_data) != 0) {
return -1; return -1;
} }
return 0; return 0;
} }
In this function, we feed all unprocessed but already received data to the In this function, we feed all unprocessed, received data to nghttp2
nghttp2 session object using the `nghttp2_session_mem_recv()` function. The session object using `nghttp2_session_mem_recv()` function. The
`nghttp2_session_mem_recv()` function processes the data and may invoke the `nghttp2_session_mem_recv()` processes the received data and may
nghttp2 callbacks and also queue outgoing frames. Since there may be pending invoke nghttp2 callbacks and also queue outgoing frames. Since there
outgoing frames, we call ``session_send()`` function to send off those may be pending frames, we call ``session_send()`` function to send
frames. The ``session_send()`` function is defined as follows:: 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; int rv;
rv = nghttp2_session_send(session_data->session); rv = nghttp2_session_send(session_data->session);
if (rv != 0) { if(rv != 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv)); warnx("Fatal error: %s", nghttp2_strerror(rv));
return -1; return -1;
} }
@@ -292,17 +321,21 @@ frames. The ``session_send()`` function is defined as follows::
} }
The `nghttp2_session_send()` function serializes the frame into wire The `nghttp2_session_send()` function serializes the frame into wire
format and calls ``send_callback()`` of type format and call :member:`nghttp2_session_callbacks.send_callback` with
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as it. We set ``send_callback()`` function to
follows:: :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, static ssize_t send_callback(nghttp2_session *session,
size_t length, int flags _U_, void *user_data) { const uint8_t *data, size_t length,
http2_session_data *session_data = (http2_session_data *)user_data; int flags, void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
struct bufferevent *bev = session_data->bev; struct bufferevent *bev = session_data->bev;
/* Avoid excessive buffering in server side. */ /* Avoid excessive buffering in server side. */
if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >= if(evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
OUTPUT_WOULDBLOCK_THRESHOLD) { OUTPUT_WOULDBLOCK_THRESHOLD) {
return NGHTTP2_ERR_WOULDBLOCK; return NGHTTP2_ERR_WOULDBLOCK;
} }
bufferevent_write(bev, data, length); bufferevent_write(bev, data, length);
@@ -312,24 +345,26 @@ follows::
Since we use bufferevent to abstract network I/O, we just write the Since we use bufferevent to abstract network I/O, we just write the
data to the bufferevent object. Note that `nghttp2_session_send()` data to the bufferevent object. Note that `nghttp2_session_send()`
continues to write all frames queued so far. If we were writing the continues to write all frames queued so far. If we were writing the
data to a non-blocking socket directly using ``write()`` system call data to the non-blocking socket directly using ``write()`` system call
in the ``send_callback()``, we would surely get ``EAGAIN`` or in the :member:`nghttp2_session_callbacks.send_callback`, we will
``EWOULDBLOCK`` back since the socket has limited send buffer. If that surely get ``EAGAIN`` or ``EWOULDBLOCK`` since the socket has limited
happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the send buffer. If that happens, we can return
nghttp2 library to stop sending further data. But when writing to the :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the nghttp2 library to stop
bufferevent, we have to regulate the amount data to get buffered sending further data. But writing to the bufferevent, we have to
ourselves to avoid using huge amounts of memory. To achieve this, we regulate the amount data to be buffered by ourselves to avoid possible
check the size of the output buffer and if it reaches more than or huge memory consumption. To achieve this, we check the size of output
equal to ``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop writing data buffer and if it is more than or equal to
and return :macro:`NGHTTP2_ERR_WOULDBLOCK` to tell the library to stop ``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop writing data and return
calling send_callback. :macro:`NGHTTP2_ERR_WOULDBLOCK` to tell the library to stop calling
send_callback.
The next bufferevent callback is ``readcb()``, which is invoked when The next bufferevent callback is ``readcb()``, which is invoked when
data is available to read in the bufferevent input buffer:: 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) { http2_session_data *session_data = (http2_session_data*)ptr;
if(session_recv(session_data) != 0) {
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
return; return;
} }
@@ -338,56 +373,59 @@ data is available to read in the bufferevent input buffer::
In this function, we just call ``session_recv()`` to process incoming In this function, we just call ``session_recv()`` to process incoming
data. data.
The third bufferevent callback is ``writecb()``, which is invoked when all The third bufferevent callback is ``writecb()``, which is invoked when
data in the bufferevent output buffer has been sent:: 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) { http2_session_data *session_data = (http2_session_data*)ptr;
if(evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
return; return;
} }
if (nghttp2_session_want_read(session_data->session) == 0 && if(nghttp2_session_want_read(session_data->session) == 0 &&
nghttp2_session_want_write(session_data->session) == 0) { nghttp2_session_want_write(session_data->session) == 0) {
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
return; return;
} }
if (session_send(session_data) != 0) { if(session_send(session_data) != 0) {
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
return; return;
} }
} }
First we check whether we should drop the connection or not. The nghttp2 First we check whether we should drop connection or not. The nghttp2
session object keeps track of reception and transmission of GOAWAY frames and session object keeps track of reception and transmission of GOAWAY
other error conditions as well. Using this information, the nghttp2 session frame and other error conditions as well. Using these information,
object will tell whether the connection should be dropped or not. More nghttp2 session object will tell whether the connection should be
specifically, if both `nghttp2_session_want_read()` and dropped or not. More specifically, both `nghttp2_session_want_read()`
`nghttp2_session_want_write()` return 0, we have no business left in the and `nghttp2_session_want_write()` return 0, we have no business in
connection. But since we are using bufferevent and its deferred callback the connection. But since we are using bufferevent and its deferred
option, the bufferevent output buffer may contain pending data when the callback option, the bufferevent output buffer may contain the pending
``writecb()`` is called. To handle this, we check whether the output buffer is data when the ``writecb()`` is called. To handle this situation, we
empty or not. If all these conditions are met, we drop connection. 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 Otherwise, we call ``session_send()`` to process pending output
data. Remember that in ``send_callback()``, we must not write all data to data. Remember that in ``send_callback()``, we may not write all data
bufferevent to avoid excessive buffering. We continue processing pending data to bufferevent to avoid excessive buffering. We continue process
when the output buffer becomes empty. pending data when output buffer becomes empty.
We have already described the nghttp2 callback ``send_callback()``. Let's We have already described about nghttp2 callback ``send_callback()``.
learn about the remaining nghttp2 callbacks we setup in Let's describe remaining nghttp2 callbacks we setup in
``initialize_nghttp2_setup()`` function. ``initialize_nghttp2_setup()`` function.
The ``on_begin_headers_callback()`` function is invoked when the reception of The ``on_begin_headers_callback()`` function is invoked when reception
a header block in HEADERS or PUSH_PROMISE frame is started:: of header block in HEADERS or PUSH_PROMISE frame is started::
static int on_begin_headers_callback(nghttp2_session *session, static int on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame, const nghttp2_frame *frame,
void *user_data) { void *user_data)
http2_session_data *session_data = (http2_session_data *)user_data; {
http2_session_data *session_data = (http2_session_data*)user_data;
http2_stream_data *stream_data; http2_stream_data *stream_data;
if (frame->hd.type != NGHTTP2_HEADERS || if(frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) { frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0; return 0;
} }
stream_data = create_http2_stream_data(session_data, frame->hd.stream_id); stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
@@ -396,40 +434,40 @@ a header block in HEADERS or PUSH_PROMISE frame is started::
return 0; return 0;
} }
We are only interested in the HEADERS frame in this function. Since the We only interested in HEADERS frame in this function. Since HEADERS
HEADERS frame has several roles in the HTTP/2 protocol, we check that it is a frame has several roles in HTTP/2.0 protocol, we check that it is a
request HEADERS, which opens new stream. If the frame is a request HEADERS, we request HEADERS, which opens new stream. If frame is request HEADERS,
create a ``http2_stream_data`` object to store the stream related data. We then we create ``http2_stream_data`` object to store stream related
associate the created ``http2_stream_data`` object with the stream in the data. We associate created ``http2_stream_data`` object to the stream
nghttp2 session object using `nghttp2_set_stream_user_data()` to get the in nghttp2 session object using `nghttp2_set_stream_user_data()` in
object without searching through the doubly linked list. 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 In this example server, we want to serve files relative to the current
directory in which the program was invoked. Each header name/value pair is working directory the program was invoked. Each header name/value pair
emitted via ``on_header_callback`` function, which is called after is emitted via ``on_header_callback`` function, which is called after
``on_begin_headers_callback()``:: ``on_begin_headers_callback()``::
static int on_header_callback(nghttp2_session *session, static int on_header_callback(nghttp2_session *session,
const nghttp2_frame *frame, const uint8_t *name, const nghttp2_frame *frame,
size_t namelen, const uint8_t *value, const uint8_t *name, size_t namelen,
size_t valuelen, uint8_t flags _U_, const uint8_t *value, size_t valuelen,
void *user_data _U_) { void *user_data)
{
http2_stream_data *stream_data; http2_stream_data *stream_data;
const char PATH[] = ":path"; const char PATH[] = ":path";
switch (frame->hd.type) { switch(frame->hd.type) {
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) { if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
break; break;
} }
stream_data = stream_data = nghttp2_session_get_stream_user_data(session,
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); frame->hd.stream_id);
if (!stream_data || stream_data->request_path) { if(stream_data->request_path) {
break; break;
} }
if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) { if(namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
size_t j; 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); stream_data->request_path = percent_decode(value, j);
} }
break; break;
@@ -437,28 +475,29 @@ emitted via ``on_header_callback`` function, which is called after
return 0; return 0;
} }
We search for the ``:path`` header field among the request headers and store We search ``:path`` header field in request headers and keep the
the requested path in the ``http2_stream_data`` object. In this example requested path in ``http2_stream_data`` object. In this example
program, we ignore ``:method`` header field and always treat the request as a program, we ignore ``:method`` header field and always treat the
GET request. request as GET request.
The ``on_frame_recv_callback()`` function is invoked when a frame is The ``on_frame_recv_callback()`` function is invoked when a frame is
fully received:: fully received::
static int on_frame_recv_callback(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_session_data *session_data = (http2_session_data*)user_data;
http2_stream_data *stream_data; http2_stream_data *stream_data;
switch (frame->hd.type) { switch(frame->hd.type) {
case NGHTTP2_DATA: case NGHTTP2_DATA:
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
/* Check that the client request has finished */ /* Check that the client request has finished */
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
stream_data = stream_data = nghttp2_session_get_stream_user_data(session,
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); frame->hd.stream_id);
/* For DATA and HEADERS frame, this callback may be called after /* For DATA and HEADERS frame, this callback may be called after
on_stream_close_callback. Check that stream still alive. */ on_stream_close_callback. Check that stream still alive. */
if (!stream_data) { if(!stream_data) {
return 0; return 0;
} }
return on_request_recv(session, session_data, stream_data); return on_request_recv(session, session_data, stream_data);
@@ -470,81 +509,81 @@ fully received::
return 0; return 0;
} }
First we retrieve the ``http2_stream_data`` object associated with the stream First we retrieve ``http2_stream_data`` object associated to the
in ``on_begin_headers_callback()``. It is done using stream in ``on_begin_headers_callback()``. It is done using
`nghttp2_session_get_stream_user_data()`. If the requested path cannot be `nghttp2_session_get_stream_user_data()`. If the requested path cannot
served for some reason (e.g., file is not found), we send a 404 response, be served for some reasons (e.g., file is not found), we send 404
which is done in ``error_reply()``. Otherwise, we open the requested file and response, which is done in ``error_reply()``. Otherwise, we open
send its content. We send the header field ``:status`` as a single response requested file and send its content. We send 1 header field
header. ``: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, 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; int rv;
nghttp2_data_provider data_prd; nghttp2_data_provider data_prd;
data_prd.source.fd = fd; data_prd.source.fd = fd;
data_prd.read_callback = file_read_callback; data_prd.read_callback = file_read_callback;
rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd); rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
if (rv != 0) { if(rv != 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv)); warnx("Fatal error: %s", nghttp2_strerror(rv));
return -1; return -1;
} }
return 0; 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 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 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 intended to be used as file descriptor. In this example server, we use
the file descriptor. We also set the ``file_read_callback()`` callback file descriptor. We also set ``file_read_callback()`` callback
function to read the contents of the file:: function to read content of the file::
static ssize_t file_read_callback(nghttp2_session *session _U_, static ssize_t file_read_callback
int32_t stream_id _U_, uint8_t *buf, (nghttp2_session *session, int32_t stream_id,
size_t length, uint32_t *data_flags, uint8_t *buf, size_t length, int *eof,
nghttp2_data_source *source, nghttp2_data_source *source, void *user_data)
void *user_data _U_) { {
int fd = source->fd; int fd = source->fd;
ssize_t r; ssize_t r;
while ((r = read(fd, buf, length)) == -1 && errno == EINTR) while((r = read(fd, buf, length)) == -1 && errno == EINTR);
; if(r == -1) {
if (r == -1) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
} }
if (r == 0) { if(r == 0) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF; *eof = 1;
} }
return r; return r;
} }
If an error happens while reading the file, we return If error happens while reading file, we return
:macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. This tells the :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. This tells the library
library to send RST_STREAM to the stream. When all data has been read, set to send RST_STREAM to the stream. When all data is read, set 1 to
the :macro:`NGHTTP2_DATA_FLAG_EOF` flag to ``*data_flags`` to tell the ``*eof`` to tell the nghttp2 library that we have finished reading
nghttp2 library that we have finished reading the file. file.
The `nghttp2_submit_response()` function is used to send the response to the The `nghttp2_submit_response()` is used to send response to the remote
remote peer. peer.
The ``on_stream_close_callback()`` function is invoked when the stream The ``on_stream_close_callback()`` function is invoked when the stream
is about to close:: 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,
uint32_t error_code _U_, void *user_data) { int32_t stream_id,
http2_session_data *session_data = (http2_session_data *)user_data; nghttp2_error_code error_code,
void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
http2_stream_data *stream_data; http2_stream_data *stream_data;
stream_data = nghttp2_session_get_stream_user_data(session, stream_id); stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
if (!stream_data) {
return 0;
}
remove_stream(session_data, stream_data); remove_stream(session_data, stream_data);
delete_http2_stream_data(stream_data); delete_http2_stream_data(stream_data);
return 0; return 0;
} }
We destroy the ``http2_stream_data`` object in this function since the stream We destroy ``http2_stream_data`` object in this function since the
is about to close and we no longer use that object. stream is about to close and we no longer use that object.

View File

@@ -1,6 +0,0 @@
.. include:: @top_srcdir@/doc/sources/tutorial-hpack.rst
deflate.c
---------
.. literalinclude:: @top_srcdir@/examples/deflate.c

6
examples/.gitignore vendored
View File

@@ -1,9 +1,3 @@
client client
libevent-client libevent-client
libevent-server libevent-server
deflate
tiny-nghttpd
asio-sv
asio-sv2
asio-cl
asio-cl2

View File

@@ -1,4 +1,4 @@
# nghttp2 - HTTP/2 C Library # nghttp2 - HTTP/2.0 C Library
# Copyright (c) 2012 Tatsuhiro Tsujikawa # Copyright (c) 2012 Tatsuhiro Tsujikawa
@@ -23,22 +23,22 @@
if ENABLE_EXAMPLES if ENABLE_EXAMPLES
AM_CFLAGS = $(WARNCFLAGS)
AM_CPPFLAGS = \ AM_CPPFLAGS = \
-Wall \ -Wall \
-I$(top_srcdir)/lib/includes \ -I$(top_srcdir)/lib/includes \
-I$(top_builddir)/lib/includes \ -I$(top_builddir)/lib/includes \
-I$(top_srcdir)/src/includes \
-I$(top_srcdir)/third-party \ -I$(top_srcdir)/third-party \
@LIBEVENT_OPENSSL_CFLAGS@ \ @LIBEVENT_OPENSSL_CFLAGS@ \
@OPENSSL_CFLAGS@ \ @OPENSSL_CFLAGS@ \
@DEFS@ @DEFS@
LDADD = $(top_builddir)/lib/libnghttp2.la \ AM_LDFLAGS = \
$(top_builddir)/third-party/libhttp-parser.la \
@LIBEVENT_OPENSSL_LIBS@ \ @LIBEVENT_OPENSSL_LIBS@ \
@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 client_SOURCES = client.c
@@ -46,50 +46,4 @@ libevent_client_SOURCES = libevent-client.c
libevent_server_SOURCES = libevent-server.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 endif # ENABLE_EXAMPLES

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -1,5 +1,5 @@
/* /*
* nghttp2 - HTTP/2 C Library * nghttp2 - HTTP/2.0 C Library
* *
* Copyright (c) 2013 Tatsuhiro Tsujikawa * Copyright (c) 2013 Tatsuhiro Tsujikawa
* *
@@ -26,28 +26,14 @@
* This program is written to show how to use nghttp2 API in C and * This program is written to show how to use nghttp2 API in C and
* intentionally made simple. * intentionally made simple.
*/ */
#ifdef HAVE_CONFIG_H #include <stdint.h>
#include <config.h>
#endif /* HAVE_CONFIG_H */
#include <inttypes.h>
#include <stdlib.h> #include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h> #include <unistd.h>
#endif /* HAVE_UNISTD_H */
#ifdef HAVE_FCNTL_H
#include <fcntl.h> #include <fcntl.h>
#endif /* HAVE_FCNTL_H */
#include <sys/types.h> #include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h> #include <sys/socket.h>
#endif /* HAVE_SYS_SOCKET_H */
#ifdef HAVE_NETDB_H
#include <netdb.h> #include <netdb.h>
#endif /* HAVE_NETDB_H */
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h> #include <netinet/in.h>
#endif /* HAVE_NETINET_IN_H */
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <poll.h> #include <poll.h>
#include <signal.h> #include <signal.h>
@@ -58,21 +44,20 @@
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/err.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) \ #define MAKE_NV(NAME, VALUE) \
{ \ {(uint8_t*)NAME, (uint8_t*)VALUE, \
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \ (uint16_t)(sizeof(NAME) - 1), (uint16_t)(sizeof(VALUE) - 1) }
NGHTTP2_NV_FLAG_NONE \
}
#define MAKE_NV_CS(NAME, VALUE) \ #define MAKE_NV_CS(NAME, VALUE) \
{ \ {(uint8_t*)NAME, (uint8_t*)VALUE, \
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, strlen(VALUE), \ (uint16_t)(sizeof(NAME) - 1), (uint16_t)(strlen(VALUE)) }
NGHTTP2_NV_FLAG_NONE \
}
struct Connection { struct Connection {
SSL *ssl; SSL *ssl;
@@ -87,6 +72,8 @@ struct Connection {
}; };
struct Request { struct Request {
/* The gzip stream inflater for the compressed response. */
nghttp2_gzip *inflater;
char *host; char *host;
/* In this program, path contains query component as well. */ /* In this program, path contains query component as well. */
char *path; char *path;
@@ -113,9 +100,10 @@ struct URI {
* Returns copy of string |s| with the length |len|. The returned * Returns copy of string |s| with the length |len|. The returned
* string is NULL-terminated. * string is NULL-terminated.
*/ */
static char *strcopy(const char *s, size_t len) { static char* strcopy(const char *s, size_t len)
{
char *dst; char *dst;
dst = malloc(len + 1); dst = malloc(len+1);
memcpy(dst, s, len); memcpy(dst, s, len);
dst[len] = '\0'; dst[len] = '\0';
return dst; return dst;
@@ -124,7 +112,8 @@ static char *strcopy(const char *s, size_t len) {
/* /*
* Prints error message |msg| and exit. * Prints error message |msg| and exit.
*/ */
static void die(const char *msg) { static void die(const char *msg)
{
fprintf(stderr, "FATAL: %s\n", msg); fprintf(stderr, "FATAL: %s\n", msg);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -133,7 +122,8 @@ static void die(const char *msg) {
* Prints error containing the function name |func| and message |msg| * Prints error containing the function name |func| and message |msg|
* and exit. * 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); fprintf(stderr, "FATAL: %s: %s\n", func, msg);
exit(EXIT_FAILURE); 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 * Prints error containing the function name |func| and error code
* |error_code| and exit. * |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, fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
nghttp2_strerror(error_code)); nghttp2_strerror(error_code));
exit(EXIT_FAILURE); 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 * The implementation of nghttp2_send_callback type. Here we write
* |data| with size |length| to the network and return the number of * |data| with size |length| to the network and return the number of
* bytes actually written. See the documentation of * bytes actually written. See the documentation of
* nghttp2_send_callback for the details. * nghttp2_send_callback for the details.
*/ */
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, static ssize_t send_callback(nghttp2_session *session,
size_t length, int flags _U_, void *user_data) { const uint8_t *data, size_t length, int flags,
void *user_data)
{
struct Connection *connection; struct Connection *connection;
int rv; ssize_t rv;
connection = (struct Connection *)user_data; connection = (struct Connection*)user_data;
connection->want_io = IO_NONE; connection->want_io = IO_NONE;
ERR_clear_error(); ERR_clear_error();
rv = SSL_write(connection->ssl, data, (int)length); rv = SSL_write(connection->ssl, data, length);
if (rv <= 0) { if(rv < 0) {
int err = SSL_get_error(connection->ssl, rv); int err = SSL_get_error(connection->ssl, rv);
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
connection->want_io = connection->want_io = (err == SSL_ERROR_WANT_READ ?
(err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE); WANT_READ : WANT_WRITE);
rv = NGHTTP2_ERR_WOULDBLOCK; rv = NGHTTP2_ERR_WOULDBLOCK;
} else { } else {
rv = NGHTTP2_ERR_CALLBACK_FAILURE; rv = NGHTTP2_ERR_CALLBACK_FAILURE;
@@ -181,39 +204,64 @@ static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
* |length| bytes. Returns the number of bytes stored in |buf|. See * |length| bytes. Returns the number of bytes stored in |buf|. See
* the documentation of nghttp2_recv_callback for the details. * the documentation of nghttp2_recv_callback for the details.
*/ */
static ssize_t recv_callback(nghttp2_session *session _U_, uint8_t *buf, static ssize_t recv_callback(nghttp2_session *session,
size_t length, int flags _U_, void *user_data) { uint8_t *buf, size_t length, int flags,
void *user_data)
{
struct Connection *connection; struct Connection *connection;
int rv; ssize_t rv;
connection = (struct Connection *)user_data; connection = (struct Connection*)user_data;
connection->want_io = IO_NONE; connection->want_io = IO_NONE;
ERR_clear_error(); ERR_clear_error();
rv = SSL_read(connection->ssl, buf, (int)length); rv = SSL_read(connection->ssl, buf, length);
if (rv < 0) { if(rv < 0) {
int err = SSL_get_error(connection->ssl, rv); int err = SSL_get_error(connection->ssl, rv);
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
connection->want_io = connection->want_io = (err == SSL_ERROR_WANT_READ ?
(err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE); WANT_READ : WANT_WRITE);
rv = NGHTTP2_ERR_WOULDBLOCK; rv = NGHTTP2_ERR_WOULDBLOCK;
} else { } else {
rv = NGHTTP2_ERR_CALLBACK_FAILURE; rv = NGHTTP2_ERR_CALLBACK_FAILURE;
} }
} else if (rv == 0) { } else if(rv == 0) {
rv = NGHTTP2_ERR_EOF; rv = NGHTTP2_ERR_EOF;
} }
return rv; return rv;
} }
/*
* 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)
{
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, static int on_frame_send_callback(nghttp2_session *session,
const nghttp2_frame *frame, const nghttp2_frame *frame, void *user_data)
void *user_data _U_) { {
size_t i; size_t i;
switch (frame->hd.type) { switch(frame->hd.type) {
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) { if(nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) {
const nghttp2_nv *nva = frame->headers.nva; const nghttp2_nv *nva = frame->headers.nva;
printf("[INFO] C ----------------------------> S (HEADERS)\n"); printf("[INFO] C ----------------------------> S (HEADERS)\n");
for (i = 0; i < frame->headers.nvlen; ++i) { for(i = 0; i < frame->headers.nvlen; ++i) {
fwrite(nva[i].name, nva[i].namelen, 1, stdout); fwrite(nva[i].name, nva[i].namelen, 1, stdout);
printf(": "); printf(": ");
fwrite(nva[i].value, nva[i].valuelen, 1, stdout); fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
@@ -232,18 +280,19 @@ static int on_frame_send_callback(nghttp2_session *session,
} }
static int on_frame_recv_callback(nghttp2_session *session, static int on_frame_recv_callback(nghttp2_session *session,
const nghttp2_frame *frame, const nghttp2_frame *frame, void *user_data)
void *user_data _U_) { {
size_t i; size_t i;
switch (frame->hd.type) { switch(frame->hd.type) {
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) { if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
const nghttp2_nv *nva = frame->headers.nva; const nghttp2_nv *nva = frame->headers.nva;
struct Request *req; struct Request *req;
req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
if (req) { if(req) {
check_gzip(req, frame->headers.nva, frame->headers.nvlen);
printf("[INFO] C <---------------------------- S (HEADERS)\n"); printf("[INFO] C <---------------------------- S (HEADERS)\n");
for (i = 0; i < frame->headers.nvlen; ++i) { for(i = 0; i < frame->headers.nvlen; ++i) {
fwrite(nva[i].name, nva[i].namelen, 1, stdout); fwrite(nva[i].name, nva[i].namelen, 1, stdout);
printf(": "); printf(": ");
fwrite(nva[i].value, nva[i].valuelen, 1, stdout); fwrite(nva[i].value, nva[i].valuelen, 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, * fetch 1 resource in this program, after reception of the response,
* we submit GOAWAY and close the session. * we submit GOAWAY and close the session.
*/ */
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, static int on_stream_close_callback(nghttp2_session *session,
uint32_t error_code _U_, int32_t stream_id,
void *user_data _U_) { nghttp2_error_code error_code,
void *user_data)
{
struct Request *req; struct Request *req;
req = nghttp2_session_get_stream_user_data(session, stream_id); req = nghttp2_session_get_stream_user_data(session, stream_id);
if (req) { if(req) {
int rv; 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) { if(rv != 0) {
diec("nghttp2_session_terminate_session", rv); diec("nghttp2_submit_goaway", rv);
} }
} }
return 0; 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 * The implementation of nghttp2_on_data_chunk_recv_callback type. We
* use this function to print the received response body. * use this function to print the received response body.
*/ */
static int on_data_chunk_recv_callback(nghttp2_session *session, static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
uint8_t flags _U_, int32_t stream_id, int32_t stream_id,
const uint8_t *data, size_t len, const uint8_t *data, size_t len,
void *user_data _U_) { void *user_data)
{
struct Request *req; struct Request *req;
req = nghttp2_session_get_stream_user_data(session, stream_id); req = nghttp2_session_get_stream_user_data(session, stream_id);
if (req) { if(req) {
printf("[INFO] C <---------------------------- S (DATA chunk)\n" printf("[INFO] C <---------------------------- S (DATA chunk)\n"
"%lu bytes\n", "%lu bytes\n", (unsigned long int)len);
(unsigned long int)len); if(req->inflater) {
fwrite(data, 1, len, stdout); 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"); printf("\n");
} }
return 0; 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 * always required. Since we use nghttp2_session_recv(), the
* recv_callback is also required. * recv_callback is also required.
*/ */
static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) { static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks)
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); {
memset(callbacks, 0, sizeof(nghttp2_session_callbacks));
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback); callbacks->send_callback = send_callback;
callbacks->recv_callback = recv_callback;
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, callbacks->before_frame_send_callback = before_frame_send_callback;
on_frame_send_callback); callbacks->on_frame_send_callback = on_frame_send_callback;
callbacks->on_frame_recv_callback = on_frame_recv_callback;
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, callbacks->on_stream_close_callback = on_stream_close_callback;
on_frame_recv_callback); callbacks->on_data_chunk_recv_callback = on_data_chunk_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);
} }
/* /*
* Callback function for TLS NPN. Since this program only supports * 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. * library supports, we terminate program.
*/ */
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out, static int select_next_proto_cb(SSL* ssl,
unsigned char *outlen, const unsigned char *in, unsigned char **out, unsigned char *outlen,
unsigned int inlen, void *arg _U_) { const unsigned char *in, unsigned int inlen,
void *arg)
{
int rv; int rv;
/* nghttp2_select_next_protocol() selects HTTP/2 protocol the /* nghttp2_select_next_protocol() selects HTTP/2.0 protocol the
nghttp2 library supports. */ nghttp2 library supports. */
rv = nghttp2_select_next_protocol(out, outlen, in, inlen); rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
if (rv <= 0) { 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; return SSL_TLSEXT_ERR_OK;
} }
@@ -351,23 +416,25 @@ static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
/* /*
* Setup SSL/TLS context. * 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 */ /* 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_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
/* Set NPN callback */ /* Set NPN callback */
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); 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; int rv;
if (SSL_set_fd(ssl, fd) == 0) { if(SSL_set_fd(ssl, fd) == 0) {
dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL)); dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
} }
ERR_clear_error(); ERR_clear_error();
rv = SSL_connect(ssl); rv = SSL_connect(ssl);
if (rv <= 0) { if(rv <= 0) {
dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL)); dief("SSL_connect", 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 * Connects to the host |host| and port |port|. This function returns
* the file descriptor of the client socket. * 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; struct addrinfo hints;
int fd = -1; int fd = -1;
int rv; int rv;
@@ -387,18 +455,17 @@ static int connect_to(const char *host, uint16_t port) {
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
rv = getaddrinfo(host, service, &hints, &res); rv = getaddrinfo(host, service, &hints, &res);
if (rv != 0) { if(rv != 0) {
dief("getaddrinfo", gai_strerror(rv)); dief("getaddrinfo", gai_strerror(rv));
} }
for (rp = res; rp; rp = rp->ai_next) { for(rp = res; rp; rp = rp->ai_next) {
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd == -1) { if(fd == -1) {
continue; continue;
} }
while ((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 && while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
errno == EINTR) errno == EINTR);
; if(rv == 0) {
if (rv == 0) {
break; break;
} }
close(fd); close(fd);
@@ -408,25 +475,25 @@ static int connect_to(const char *host, uint16_t port) {
return fd; return fd;
} }
static void make_non_block(int fd) { static void make_non_block(int fd)
{
int flags, rv; 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) {
if (flags == -1) {
dief("fcntl", strerror(errno)); 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) {
if (rv == -1) {
dief("fcntl", strerror(errno)); dief("fcntl", strerror(errno));
} }
} }
static void set_tcp_nodelay(int fd) { static void set_tcp_nodelay(int fd)
{
int val = 1; int val = 1;
int rv; int rv;
rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
if (rv == -1) { if(rv == -1) {
dief("setsockopt", strerror(errno)); dief("setsockopt", strerror(errno));
} }
} }
@@ -434,14 +501,15 @@ static void set_tcp_nodelay(int fd) {
/* /*
* Update |pollfd| based on the state of |connection|. * 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; pollfd->events = 0;
if (nghttp2_session_want_read(connection->session) || if(nghttp2_session_want_read(connection->session) ||
connection->want_io == WANT_READ) { connection->want_io == WANT_READ) {
pollfd->events |= POLLIN; pollfd->events |= POLLIN;
} }
if (nghttp2_session_want_write(connection->session) || if(nghttp2_session_want_write(connection->session) ||
connection->want_io == WANT_WRITE) { connection->want_io == WANT_WRITE) {
pollfd->events |= POLLOUT; pollfd->events |= POLLOUT;
} }
} }
@@ -451,61 +519,66 @@ static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) {
* function does not send packets; just append the request to the * function does not send packets; just append the request to the
* internal queue in |connection->session|. * internal queue in |connection->session|.
*/ */
static void submit_request(struct Connection *connection, struct Request *req) { static void submit_request(struct Connection *connection, struct Request *req)
int32_t stream_id; {
/* Make sure that the last item is NULL */ int pri = 0;
const nghttp2_nv nva[] = {MAKE_NV(":method", "GET"), int rv;
MAKE_NV_CS(":path", req->path), const nghttp2_nv nva[] = {
MAKE_NV(":scheme", "https"), /* Make sure that the last item is NULL */
MAKE_NV_CS(":authority", req->hostport), MAKE_NV(":method", "GET"),
MAKE_NV("accept", "*/*"), MAKE_NV_CS(":path", req->path),
MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)}; MAKE_NV(":scheme", "https"),
MAKE_NV_CS(":authority", req->hostport),
stream_id = nghttp2_submit_request(connection->session, NULL, nva, MAKE_NV("accept", "*/*"),
sizeof(nva) / sizeof(nva[0]), NULL, req); MAKE_NV("user-agent", "nghttp2/"NGHTTP2_VERSION)
};
if (stream_id < 0) { rv = nghttp2_submit_request(connection->session, pri,
diec("nghttp2_submit_request", stream_id); 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. * Performs the network I/O.
*/ */
static void exec_io(struct Connection *connection) { static void exec_io(struct Connection *connection)
{
int rv; int rv;
rv = nghttp2_session_recv(connection->session); rv = nghttp2_session_recv(connection->session);
if (rv != 0) { if(rv != 0) {
diec("nghttp2_session_recv", rv); diec("nghttp2_session_recv", rv);
} }
rv = nghttp2_session_send(connection->session); rv = nghttp2_session_send(connection->session);
if (rv != 0) { if(rv != 0) {
diec("nghttp2_session_send", rv); diec("nghttp2_session_send", rv);
} }
} }
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->host = strcopy(uri->host, uri->hostlen);
req->port = uri->port; req->port = uri->port;
req->path = strcopy(uri->path, uri->pathlen); req->path = strcopy(uri->path, uri->pathlen);
req->hostport = strcopy(uri->hostport, uri->hostportlen); req->hostport = strcopy(uri->hostport, uri->hostportlen);
req->stream_id = -1; 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->host);
free(req->path); free(req->path);
free(req->hostport); free(req->hostport);
nghttp2_gzip_inflate_del(req->inflater);
} }
/* /*
* Fetches the resource denoted by |uri|. * Fetches the resource denoted by |uri|.
*/ */
static void fetch_uri(const struct URI *uri) { static void fetch_uri(const struct URI *uri)
nghttp2_session_callbacks *callbacks; {
nghttp2_session_callbacks callbacks;
int fd; int fd;
SSL_CTX *ssl_ctx; SSL_CTX *ssl_ctx;
SSL *ssl; SSL *ssl;
@@ -517,18 +590,20 @@ static void fetch_uri(const struct URI *uri) {
request_init(&req, uri); request_init(&req, uri);
setup_nghttp2_callbacks(&callbacks);
/* Establish connection and setup SSL */ /* Establish connection and setup SSL */
fd = connect_to(req.host, req.port); fd = connect_to(req.host, req.port);
if (fd == -1) { if(fd == -1) {
die("Could not open file descriptor"); die("Could not open file descriptor");
} }
ssl_ctx = SSL_CTX_new(SSLv23_client_method()); ssl_ctx = SSL_CTX_new(SSLv23_client_method());
if (ssl_ctx == NULL) { if(ssl_ctx == NULL) {
dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL)); dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL));
} }
init_ssl_ctx(ssl_ctx); init_ssl_ctx(ssl_ctx);
ssl = SSL_new(ssl_ctx); ssl = SSL_new(ssl_ctx);
if (ssl == NULL) { if(ssl == NULL) {
dief("SSL_new", ERR_error_string(ERR_get_error(), NULL)); dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
} }
/* To simplify the program, we perform SSL/TLS handshake in blocking /* To simplify the program, we perform SSL/TLS handshake in blocking
@@ -538,30 +613,21 @@ static void fetch_uri(const struct URI *uri) {
connection.ssl = ssl; connection.ssl = ssl;
connection.want_io = IO_NONE; connection.want_io = IO_NONE;
/* Send connection header in blocking I/O mode */
SSL_write(ssl, NGHTTP2_CLIENT_CONNECTION_HEADER,
NGHTTP2_CLIENT_CONNECTION_HEADER_LEN);
/* Here make file descriptor non-block */ /* Here make file descriptor non-block */
make_non_block(fd); make_non_block(fd);
set_tcp_nodelay(fd); set_tcp_nodelay(fd);
printf("[INFO] SSL/TLS handshake completed\n"); printf("[INFO] SSL/TLS handshake completed\n");
rv = nghttp2_session_client_new(&connection.session, &callbacks,
rv = nghttp2_session_callbacks_new(&callbacks); &connection);
if(rv != 0) {
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);
if (rv != 0) {
diec("nghttp2_session_client_new", rv); 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 the HTTP request to the outbound queue. */
submit_request(&connection, &req); submit_request(&connection, &req);
@@ -569,16 +635,16 @@ static void fetch_uri(const struct URI *uri) {
ctl_poll(pollfds, &connection); ctl_poll(pollfds, &connection);
/* Event loop */ /* Event loop */
while (nghttp2_session_want_read(connection.session) || while(nghttp2_session_want_read(connection.session) ||
nghttp2_session_want_write(connection.session)) { nghttp2_session_want_write(connection.session)) {
int nfds = poll(pollfds, npollfds, -1); int nfds = poll(pollfds, npollfds, -1);
if (nfds == -1) { if(nfds == -1) {
dief("poll", strerror(errno)); dief("poll", strerror(errno));
} }
if (pollfds[0].revents & (POLLIN | POLLOUT)) { if(pollfds[0].revents & (POLLIN | POLLOUT)) {
exec_io(&connection); exec_io(&connection);
} }
if ((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) { if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
die("Connection error"); die("Connection error");
} }
ctl_poll(pollfds, &connection); ctl_poll(pollfds, &connection);
@@ -594,94 +660,96 @@ static void fetch_uri(const struct URI *uri) {
request_free(&req); 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 */ /* We only interested in https */
size_t len, i, offset; size_t len, i, offset;
int ipv6addr = 0; int ipv6addr = 0;
memset(res, 0, sizeof(struct URI)); memset(res, 0, sizeof(struct URI));
len = strlen(uri); len = strlen(uri);
if (len < 9 || memcmp("https://", uri, 8) != 0) { if(len < 9 || memcmp("https://", uri, 8) != 0) {
return -1; return -1;
} }
offset = 8; offset = 8;
res->host = res->hostport = &uri[offset]; res->host = res->hostport = &uri[offset];
res->hostlen = 0; res->hostlen = 0;
if (uri[offset] == '[') { if(uri[offset] == '[') {
/* IPv6 literal address */ /* IPv6 literal address */
++offset; ++offset;
++res->host; ++res->host;
ipv6addr = 1; ipv6addr = 1;
for (i = offset; i < len; ++i) { for(i = offset; i < len; ++i) {
if (uri[i] == ']') { if(uri[i] == ']') {
res->hostlen = i - offset; res->hostlen = i-offset;
offset = i + 1; offset = i+1;
break; break;
} }
} }
} else { } else {
const char delims[] = ":/?#"; const char delims[] = ":/?#";
for (i = offset; i < len; ++i) { for(i = offset; i < len; ++i) {
if (strchr(delims, uri[i]) != NULL) { if(strchr(delims, uri[i]) != NULL) {
break; break;
} }
} }
res->hostlen = i - offset; res->hostlen = i-offset;
offset = i; offset = i;
} }
if (res->hostlen == 0) { if(res->hostlen == 0) {
return -1; return -1;
} }
/* Assuming https */ /* Assuming https */
res->port = 443; res->port = 443;
if (offset < len) { if(offset < len) {
if (uri[offset] == ':') { if(uri[offset] == ':') {
/* port */ /* port */
const char delims[] = "/?#"; const char delims[] = "/?#";
int port = 0; int port = 0;
++offset; ++offset;
for (i = offset; i < len; ++i) { for(i = offset; i < len; ++i) {
if (strchr(delims, uri[i]) != NULL) { if(strchr(delims, uri[i]) != NULL) {
break; break;
} }
if ('0' <= uri[i] && uri[i] <= '9') { if('0' <= uri[i] && uri[i] <= '9') {
port *= 10; port *= 10;
port += uri[i] - '0'; port += uri[i]-'0';
if (port > 65535) { if(port > 65535) {
return -1; return -1;
} }
} else { } else {
return -1; return -1;
} }
} }
if (port == 0) { if(port == 0) {
return -1; return -1;
} }
offset = i; offset = i;
res->port = port; res->port = port;
} }
} }
res->hostportlen = uri + offset + ipv6addr - res->host; res->hostportlen = uri+offset+ipv6addr-res->host;
for (i = offset; i < len; ++i) { for(i = offset; i < len; ++i) {
if (uri[i] == '#') { if(uri[i] == '#') {
break; break;
} }
} }
if (i - offset == 0) { if(i-offset == 0) {
res->path = "/"; res->path = "/";
res->pathlen = 1; res->pathlen = 1;
} else { } else {
res->path = &uri[offset]; res->path = &uri[offset];
res->pathlen = i - offset; res->pathlen = i-offset;
} }
return 0; return 0;
} }
int main(int argc, char **argv) { int main(int argc, char **argv)
{
struct URI uri; struct URI uri;
struct sigaction act; struct sigaction act;
int rv; int rv;
if (argc < 2) { if(argc < 2) {
die("Specify a https URI"); die("Specify a https URI");
} }
@@ -691,11 +759,9 @@ int main(int argc, char **argv) {
SSL_load_error_strings(); SSL_load_error_strings();
SSL_library_init(); SSL_library_init();
OpenSSL_add_all_algorithms();
OPENSSL_config(NULL);
rv = parse_uri(&uri, argv[1]); rv = parse_uri(&uri, argv[1]);
if (rv != 0) { if(rv != 0) {
die("parse_uri failed"); die("parse_uri failed");
} }
fetch_uri(&uri); fetch_uri(&uri);

View File

@@ -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;
}

View File

@@ -1,5 +1,5 @@
/* /*
* nghttp2 - HTTP/2 C Library * nghttp2 - HTTP/2.0 C Library
* *
* Copyright (c) 2013 Tatsuhiro Tsujikawa * Copyright (c) 2013 Tatsuhiro Tsujikawa
* *
@@ -22,27 +22,16 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 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> #include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h> #include <unistd.h>
#endif /* HAVE_UNISTD_H */
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h> #include <sys/socket.h>
#endif /* HAVE_SYS_SOCKET_H */
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h> #include <netinet/in.h>
#endif /* HAVE_NETINET_IN_H */
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <err.h> #include <err.h>
#include <signal.h> #include <signal.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/conf.h>
#include <event.h> #include <event.h>
#include <event2/event.h> #include <event2/event.h>
@@ -53,14 +42,14 @@
#include "http-parser/http_parser.h" #include "http-parser/http_parser.h"
#define ARRLEN(x) (sizeof(x) / sizeof(x[0])) #define ARRLEN(x) (sizeof(x)/sizeof(x[0]))
typedef struct { typedef struct {
/* The NULL-terminated URI string to retrieve. */ /* The NULL-terminated URI string to retreive. */
const char *uri; const char *uri;
/* Parsed result of the |uri| */ /* Parsed result of the |uri| */
struct http_parser_url *u; 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; char *authority;
/* The path portion of the |uri|, including query, not /* The path portion of the |uri|, including query, not
NULL-terminated */ NULL-terminated */
@@ -80,8 +69,9 @@ typedef struct {
http2_stream_data *stream_data; http2_stream_data *stream_data;
} http2_session_data; } http2_session_data;
static http2_stream_data *create_http2_stream_data(const char *uri, 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) */ /* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */
size_t extra = 7; size_t extra = 7;
http2_stream_data *stream_data = malloc(sizeof(http2_stream_data)); http2_stream_data *stream_data = malloc(sizeof(http2_stream_data));
@@ -92,29 +82,29 @@ static http2_stream_data *create_http2_stream_data(const char *uri,
stream_data->authoritylen = u->field_data[UF_HOST].len; stream_data->authoritylen = u->field_data[UF_HOST].len;
stream_data->authority = malloc(stream_data->authoritylen + extra); stream_data->authority = malloc(stream_data->authoritylen + extra);
memcpy(stream_data->authority, &uri[u->field_data[UF_HOST].off], memcpy(stream_data->authority,
u->field_data[UF_HOST].len); &uri[u->field_data[UF_HOST].off], u->field_data[UF_HOST].len);
if (u->field_set & (1 << UF_PORT)) { if(u->field_set & (1 << UF_PORT)) {
stream_data->authoritylen += stream_data->authoritylen +=
snprintf(stream_data->authority + u->field_data[UF_HOST].len, extra, snprintf(stream_data->authority + u->field_data[UF_HOST].len, extra,
":%u", u->port); ":%u", u->port);
} }
stream_data->pathlen = 0; stream_data->pathlen = 0;
if (u->field_set & (1 << UF_PATH)) { if(u->field_set & (1 << UF_PATH)) {
stream_data->pathlen = u->field_data[UF_PATH].len; stream_data->pathlen = u->field_data[UF_PATH].len;
} }
if (u->field_set & (1 << UF_QUERY)) { if(u->field_set & (1 << UF_QUERY)) {
/* +1 for '?' character */ /* +1 for '?' character */
stream_data->pathlen += u->field_data[UF_QUERY].len + 1; stream_data->pathlen += u->field_data[UF_QUERY].len + 1;
} }
if (stream_data->pathlen > 0) { if(stream_data->pathlen > 0) {
stream_data->path = malloc(stream_data->pathlen); stream_data->path = malloc(stream_data->pathlen);
if (u->field_set & (1 << UF_PATH)) { if(u->field_set & (1 << UF_PATH)) {
memcpy(stream_data->path, &uri[u->field_data[UF_PATH].off], memcpy(stream_data->path,
u->field_data[UF_PATH].len); &uri[u->field_data[UF_PATH].off], u->field_data[UF_PATH].len);
} }
if (u->field_set & (1 << UF_QUERY)) { if(u->field_set & (1 << UF_QUERY)) {
memcpy(stream_data->path + u->field_data[UF_PATH].len + 1, memcpy(stream_data->path + u->field_data[UF_PATH].len + 1,
&uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len); &uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len);
} }
@@ -124,15 +114,16 @@ static http2_stream_data *create_http2_stream_data(const char *uri,
return stream_data; 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->path);
free(stream_data->authority); free(stream_data->authority);
free(stream_data); free(stream_data);
} }
/* Initializes |session_data| */ /* Initializes |session_data| */
static http2_session_data * static http2_session_data *create_http2_session_data(struct event_base *evbase)
create_http2_session_data(struct event_base *evbase) { {
http2_session_data *session_data = malloc(sizeof(http2_session_data)); http2_session_data *session_data = malloc(sizeof(http2_session_data));
memset(session_data, 0, sizeof(http2_session_data)); memset(session_data, 0, sizeof(http2_session_data));
@@ -140,10 +131,11 @@ create_http2_session_data(struct event_base *evbase) {
return session_data; 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); SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
if (ssl) { if(ssl) {
SSL_shutdown(ssl); SSL_shutdown(ssl);
} }
bufferevent_free(session_data->bev); bufferevent_free(session_data->bev);
@@ -152,15 +144,17 @@ static void delete_http2_session_data(http2_session_data *session_data) {
session_data->dnsbase = NULL; session_data->dnsbase = NULL;
nghttp2_session_del(session_data->session); nghttp2_session_del(session_data->session);
session_data->session = NULL; session_data->session = NULL;
if (session_data->stream_data) { if(session_data->stream_data) {
delete_http2_stream_data(session_data->stream_data); delete_http2_stream_data(session_data->stream_data);
session_data->stream_data = NULL; session_data->stream_data = NULL;
} }
free(session_data); free(session_data);
} }
static void print_header(FILE *f, const uint8_t *name, size_t namelen, static void print_header(FILE *f,
const uint8_t *value, size_t valuelen) { const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen)
{
fwrite(name, namelen, 1, f); fwrite(name, namelen, 1, f);
fprintf(f, ": "); fprintf(f, ": ");
fwrite(value, valuelen, 1, 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 /* Print HTTP headers to |f|. Please note that this function does not
take into account that header name and value are sequence of take into account that header name and value are sequence of
octets, therefore they may contain non-printable characters. */ 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; size_t i;
for (i = 0; i < nvlen; ++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"); fprintf(f, "\n");
} }
@@ -181,26 +178,51 @@ static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen) {
/* nghttp2_send_callback. Here we transmit the |data|, |length| bytes, /* nghttp2_send_callback. Here we transmit the |data|, |length| bytes,
to the network. Because we are using libevent bufferevent, we just to the network. Because we are using libevent bufferevent, we just
write those bytes into bufferevent buffer. */ write those bytes into bufferevent buffer. */
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, static ssize_t send_callback(nghttp2_session *session,
size_t length, int flags _U_, void *user_data) { const uint8_t *data, size_t length,
http2_session_data *session_data = (http2_session_data *)user_data; int flags, void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
struct bufferevent *bev = session_data->bev; struct bufferevent *bev = session_data->bev;
bufferevent_write(bev, data, length); bufferevent_write(bev, data, length);
return 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 /* nghttp2_on_header_callback: Called when nghttp2 library emits
single header name/value pair. */ single header name/value pair. */
static int on_header_callback(nghttp2_session *session _U_, static int on_header_callback(nghttp2_session *session,
const nghttp2_frame *frame, const uint8_t *name, const nghttp2_frame *frame,
size_t namelen, const uint8_t *value, const uint8_t *name, size_t namelen,
size_t valuelen, uint8_t flags _U_, const uint8_t *value, size_t valuelen,
void *user_data) { void *user_data)
http2_session_data *session_data = (http2_session_data *)user_data; {
switch (frame->hd.type) { http2_session_data *session_data = (http2_session_data*)user_data;
switch(frame->hd.type) {
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session_data->stream_data->stream_id == frame->hd.stream_id) { session_data->stream_data->stream_id == frame->hd.stream_id) {
/* Print response headers for the initiated request. */ /* Print response headers for the initiated request. */
print_header(stderr, name, namelen, value, valuelen); print_header(stderr, name, namelen, value, valuelen);
break; break;
@@ -211,14 +233,15 @@ static int on_header_callback(nghttp2_session *session _U_,
/* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets /* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets
started to receive header block. */ 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, const nghttp2_frame *frame,
void *user_data) { void *user_data)
http2_session_data *session_data = (http2_session_data *)user_data; {
switch (frame->hd.type) { http2_session_data *session_data = (http2_session_data*)user_data;
switch(frame->hd.type) {
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session_data->stream_data->stream_id == frame->hd.stream_id) { session_data->stream_data->stream_id == frame->hd.stream_id) {
fprintf(stderr, "Response headers for stream ID=%d:\n", fprintf(stderr, "Response headers for stream ID=%d:\n",
frame->hd.stream_id); frame->hd.stream_id);
} }
@@ -229,13 +252,14 @@ static int on_begin_headers_callback(nghttp2_session *session _U_,
/* nghttp2_on_frame_recv_callback: Called when nghttp2 library /* nghttp2_on_frame_recv_callback: Called when nghttp2 library
received a complete frame from the remote peer. */ received a complete frame from the remote peer. */
static int on_frame_recv_callback(nghttp2_session *session _U_, 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; {
switch (frame->hd.type) { http2_session_data *session_data = (http2_session_data*)user_data;
switch(frame->hd.type) {
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE && if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session_data->stream_data->stream_id == frame->hd.stream_id) { session_data->stream_data->stream_id == frame->hd.stream_id) {
fprintf(stderr, "All headers received\n"); fprintf(stderr, "All headers received\n");
} }
break; break;
@@ -248,12 +272,13 @@ static int on_frame_recv_callback(nghttp2_session *session _U_,
is meant to the stream we initiated, print the received data in is meant to the stream we initiated, print the received data in
stdout, so that the user can redirect its output to the file stdout, so that the user can redirect its output to the file
easily. */ easily. */
static int on_data_chunk_recv_callback(nghttp2_session *session _U_, static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
uint8_t flags _U_, int32_t stream_id, int32_t stream_id,
const uint8_t *data, size_t len, 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) { http2_session_data *session_data = (http2_session_data*)user_data;
if(session_data->stream_data->stream_id == stream_id) {
fwrite(data, len, 1, stdout); fwrite(data, len, 1, stdout);
} }
return 0; return 0;
@@ -263,16 +288,19 @@ static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
closed. This example program only deals with 1 HTTP request (1 closed. This example program only deals with 1 HTTP request (1
stream), if it is closed, we send GOAWAY and tear down the stream), if it is closed, we send GOAWAY and tear down the
session */ session */
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, static int on_stream_close_callback(nghttp2_session *session,
uint32_t error_code, void *user_data) { int32_t stream_id,
http2_session_data *session_data = (http2_session_data *)user_data; nghttp2_error_code error_code,
void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
int rv; int rv;
if (session_data->stream_data->stream_id == stream_id) { if(session_data->stream_data->stream_id == stream_id) {
fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id, fprintf(stderr, "Stream %d closed with error_code=%d\n",
error_code); stream_id, error_code);
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
if (rv != 0) { if(rv != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE; return NGHTTP2_ERR_CALLBACK_FAILURE;
} }
} }
@@ -280,126 +308,115 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
} }
/* NPN TLS extension client callback. We check that server advertised /* 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. */ the program. */
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out, static int select_next_proto_cb(SSL* ssl,
unsigned char *outlen, const unsigned char *in, unsigned char **out, unsigned char *outlen,
unsigned int inlen, void *arg _U_) { const unsigned char *in, unsigned int inlen,
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) { void *arg)
{
if(nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID); errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
} }
return SSL_TLSEXT_ERR_OK; return SSL_TLSEXT_ERR_OK;
} }
/* Create SSL_CTX. */ /* 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;
ssl_ctx = SSL_CTX_new(SSLv23_client_method()); ssl_ctx = SSL_CTX_new(SSLv23_client_method());
if (!ssl_ctx) { if(!ssl_ctx) {
errx(1, "Could not create SSL/TLS context: %s", errx(1, "Could not create SSL/TLS context: %s",
ERR_error_string(ERR_get_error(), NULL)); ERR_error_string(ERR_get_error(), NULL));
} }
SSL_CTX_set_options(ssl_ctx, SSL_CTX_set_options(ssl_ctx,
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
return ssl_ctx; return ssl_ctx;
} }
/* Create SSL object */ /* Create SSL object */
static SSL *create_ssl(SSL_CTX *ssl_ctx) { static SSL* create_ssl(SSL_CTX *ssl_ctx)
{
SSL *ssl; SSL *ssl;
ssl = SSL_new(ssl_ctx); ssl = SSL_new(ssl_ctx);
if (!ssl) { if(!ssl) {
errx(1, "Could not create SSL/TLS session object: %s", errx(1, "Could not create SSL/TLS session object: %s",
ERR_error_string(ERR_get_error(), NULL)); ERR_error_string(ERR_get_error(), NULL));
} }
return ssl; return ssl;
} }
static void initialize_nghttp2_session(http2_session_data *session_data) { static void initialize_nghttp2_session(http2_session_data *session_data)
nghttp2_session_callbacks *callbacks; {
nghttp2_session_callbacks callbacks = {0};
nghttp2_session_callbacks_new(&callbacks); callbacks.send_callback = send_callback;
callbacks.before_frame_send_callback = before_frame_send_callback;
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); callbacks.on_frame_recv_callback = on_frame_recv_callback;
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, callbacks.on_stream_close_callback = on_stream_close_callback;
on_frame_recv_callback); callbacks.on_header_callback = on_header_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
nghttp2_session_callbacks_set_on_data_chunk_recv_callback( nghttp2_session_client_new(&session_data->session, &callbacks, session_data);
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);
} }
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_entry iv[1] = {
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
};
int rv; int rv;
/* client 24 bytes magic string will be sent by nghttp2 library */ bufferevent_write(session_data->bev,
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, NGHTTP2_CLIENT_CONNECTION_HEADER,
ARRLEN(iv)); NGHTTP2_CLIENT_CONNECTION_HEADER_LEN);
if (rv != 0) { 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)); errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
} }
} }
#define MAKE_NV(NAME, VALUE, VALUELEN) \ #define MAKE_NV(NAME, VALUE, VALUELEN) \
{ \ { (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, VALUELEN }
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, VALUELEN, \
NGHTTP2_NV_FLAG_NONE \
}
#define MAKE_NV2(NAME, VALUE) \ #define MAKE_NV2(NAME, VALUE) \
{ \ { (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1 }
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
NGHTTP2_NV_FLAG_NONE \
}
/* Send HTTP request to the remote peer */ /* Send HTTP request to the remote peer */
static void submit_request(http2_session_data *session_data) { static void submit_request(http2_session_data *session_data)
int32_t stream_id; {
int rv;
http2_stream_data *stream_data = session_data->stream_data; http2_stream_data *stream_data = session_data->stream_data;
const char *uri = stream_data->uri; const char *uri = stream_data->uri;
const struct http_parser_url *u = stream_data->u; const struct http_parser_url *u = stream_data->u;
nghttp2_nv hdrs[] = { nghttp2_nv hdrs[] = {
MAKE_NV2(":method", "GET"), MAKE_NV2(":method", "GET"),
MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off], MAKE_NV(":scheme",
u->field_data[UF_SCHEMA].len), &uri[u->field_data[UF_SCHEMA].off], u->field_data[UF_SCHEMA].len),
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen), 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"); fprintf(stderr, "Request headers:\n");
print_headers(stderr, hdrs, ARRLEN(hdrs)); print_headers(stderr, hdrs, ARRLEN(hdrs));
stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs, rv = nghttp2_submit_request(session_data->session, NGHTTP2_PRI_DEFAULT,
ARRLEN(hdrs), NULL, stream_data); hdrs, ARRLEN(hdrs), NULL, stream_data);
if (stream_id < 0) { if(rv != 0) {
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id)); 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 /* Serialize the frame and send (or buffer) the data to
bufferevent. */ bufferevent. */
static int session_send(http2_session_data *session_data) { static int session_send(http2_session_data *session_data)
{
int rv; int rv;
rv = nghttp2_session_send(session_data->session); rv = nghttp2_session_send(session_data->session);
if (rv != 0) { if(rv != 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv)); warnx("Fatal error: %s", nghttp2_strerror(rv));
return -1; return -1;
} }
@@ -410,25 +427,21 @@ static int session_send(http2_session_data *session_data) {
of bufferevent and feed them to nghttp2 library. This may invoke of bufferevent and feed them to nghttp2 library. This may invoke
nghttp2 callbacks. It may also queues the frame in nghttp2 session nghttp2 callbacks. It may also queues the frame in nghttp2 session
context. To send them, we call session_send() in the end. */ 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; http2_session_data *session_data = (http2_session_data*)ptr;
int rv;
struct evbuffer *input = bufferevent_get_input(bev); struct evbuffer *input = bufferevent_get_input(bev);
size_t datalen = evbuffer_get_length(input); size_t datalen = evbuffer_get_length(input);
unsigned char *data = evbuffer_pullup(input, -1); unsigned char *data = evbuffer_pullup(input, -1);
rv = nghttp2_session_mem_recv(session_data->session, data, datalen);
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); if(rv < 0) {
if (readlen < 0) { warnx("Fatal error: %s", nghttp2_strerror(rv));
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
return; return;
} }
if (evbuffer_drain(input, readlen) != 0) { evbuffer_drain(input, rv);
warnx("Fatal error: evbuffer_drain failed"); if(session_send(session_data) != 0) {
delete_http2_session_data(session_data);
return;
}
if (session_send(session_data) != 0) {
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
return; return;
} }
@@ -438,11 +451,12 @@ static void readcb(struct bufferevent *bev, void *ptr) {
receiving GOAWAY, we check the some conditions on the nghttp2 receiving GOAWAY, we check the some conditions on the nghttp2
library and output buffer of bufferevent. If it indicates we have library and output buffer of bufferevent. If it indicates we have
no business to this session, tear down the connection. */ 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 && http2_session_data *session_data = (http2_session_data*)ptr;
nghttp2_session_want_write(session_data->session) == 0 && if(nghttp2_session_want_read(session_data->session) == 0 &&
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) { nghttp2_session_want_write(session_data->session) == 0 &&
evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
} }
} }
@@ -452,9 +466,10 @@ static void writecb(struct bufferevent *bev _U_, void *ptr) {
peer verification. After SSL/TLS handshake is over, initialize peer verification. After SSL/TLS handshake is over, initialize
nghttp2 library session, and send client connection header. Then nghttp2 library session, and send client connection header. Then
send HTTP request. */ 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) { http2_session_data *session_data = (http2_session_data*)ptr;
if(events & BEV_EVENT_CONNECTED) {
int fd = bufferevent_getfd(bev); int fd = bufferevent_getfd(bev);
int val = 1; int val = 1;
fprintf(stderr, "Connected\n"); fprintf(stderr, "Connected\n");
@@ -462,38 +477,41 @@ static void eventcb(struct bufferevent *bev, short events, void *ptr) {
initialize_nghttp2_session(session_data); initialize_nghttp2_session(session_data);
send_client_connection_header(session_data); send_client_connection_header(session_data);
submit_request(session_data); submit_request(session_data);
if (session_send(session_data) != 0) { if(session_send(session_data) != 0) {
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
} }
return; return;
} }
if (events & BEV_EVENT_EOF) { if(events & BEV_EVENT_EOF) {
warnx("Disconnected from the remote host"); warnx("Disconnected from the remote host");
} else if (events & BEV_EVENT_ERROR) { } else if(events & BEV_EVENT_ERROR) {
warnx("Network error"); warnx("Network error");
} else if (events & BEV_EVENT_TIMEOUT) { } else if(events & BEV_EVENT_TIMEOUT) {
warnx("Timeout"); warnx("Timeout");
} }
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
} }
/* Start connecting to the remote peer |host:port| */ /* 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, const char *host, uint16_t port,
http2_session_data *session_data) { http2_session_data *session_data)
{
int rv; int rv;
struct bufferevent *bev; struct bufferevent *bev;
SSL *ssl; SSL *ssl;
ssl = create_ssl(ssl_ctx); ssl = create_ssl(ssl_ctx);
bev = bufferevent_openssl_socket_new( bev = bufferevent_openssl_socket_new(evbase, -1, ssl,
evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING, BUFFEREVENT_SSL_CONNECTING,
BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE); BEV_OPT_DEFER_CALLBACKS |
BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data); bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase, rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
AF_UNSPEC, host, port); AF_UNSPEC, host, port);
if (rv != 0) { if(rv != 0) {
errx(1, "Could not connect to the remote host %s", host); errx(1, "Could not connect to the remote host %s", host);
} }
session_data->bev = bev; session_data->bev = bev;
@@ -501,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 /* Get resource denoted by the |uri|. The debug and error messages are
printed in stderr, while the response body is printed in stdout. */ 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; struct http_parser_url u;
char *host; char *host;
uint16_t port; uint16_t port;
@@ -512,11 +531,11 @@ static void run(const char *uri) {
/* Parse the |uri| and stores its components in |u| */ /* Parse the |uri| and stores its components in |u| */
rv = http_parser_parse_url(uri, strlen(uri), 0, &u); rv = http_parser_parse_url(uri, strlen(uri), 0, &u);
if (rv != 0) { if(rv != 0) {
errx(1, "Could not parse URI %s", uri); errx(1, "Could not parse URI %s", uri);
} }
host = strndup(&uri[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len); host = strndup(&uri[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len);
if (!(u.field_set & (1 << UF_PORT))) { if(!(u.field_set & (1 << UF_PORT))) {
port = 443; port = 443;
} else { } else {
port = u.port; port = u.port;
@@ -539,10 +558,11 @@ static void run(const char *uri) {
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
} }
int main(int argc, char **argv) { int main(int argc, char **argv)
{
struct sigaction act; struct sigaction act;
if (argc < 2) { if(argc < 2) {
fprintf(stderr, "Usage: libevent-client HTTPS_URI\n"); fprintf(stderr, "Usage: libevent-client HTTPS_URI\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -553,8 +573,6 @@ int main(int argc, char **argv) {
SSL_load_error_strings(); SSL_load_error_strings();
SSL_library_init(); SSL_library_init();
OpenSSL_add_all_algorithms();
OPENSSL_config(NULL);
run(argv[1]); run(argv[1]);
return 0; return 0;

View File

@@ -1,5 +1,5 @@
/* /*
* nghttp2 - HTTP/2 C Library * nghttp2 - HTTP/2.0 C Library
* *
* Copyright (c) 2013 Tatsuhiro Tsujikawa * Copyright (c) 2013 Tatsuhiro Tsujikawa
* *
@@ -22,35 +22,20 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 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> #include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h> #include <sys/socket.h>
#endif /* HAVE_SYS_SOCKET_H */
#ifdef HAVE_NETDB_H
#include <netdb.h> #include <netdb.h>
#endif /* HAVE_NETDB_H */
#include <signal.h> #include <signal.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h> #include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <sys/stat.h> #include <sys/stat.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h> #include <fcntl.h>
#endif /* HAVE_FCNTL_H */
#include <ctype.h> #include <ctype.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h> #include <netinet/in.h>
#endif /* HAVE_NETINET_IN_H */
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <err.h> #include <err.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/conf.h>
#include <event.h> #include <event.h>
#include <event2/event.h> #include <event2/event.h>
@@ -61,13 +46,10 @@
#define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16) #define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16)
#define ARRLEN(x) (sizeof(x) / sizeof(x[0])) #define ARRLEN(x) (sizeof(x)/sizeof(x[0]))
#define MAKE_NV(NAME, VALUE) \ #define MAKE_NV(NAME, VALUE) \
{ \ { (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1 }
(uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
NGHTTP2_NV_FLAG_NONE \
}
struct app_context; struct app_context;
typedef struct app_context app_context; typedef struct app_context app_context;
@@ -85,6 +67,7 @@ typedef struct http2_session_data {
app_context *app_ctx; app_context *app_ctx;
nghttp2_session *session; nghttp2_session *session;
char *client_addr; char *client_addr;
size_t handshake_leftlen;
} http2_session_data; } http2_session_data;
struct app_context { struct app_context {
@@ -95,40 +78,32 @@ struct app_context {
static unsigned char next_proto_list[256]; static unsigned char next_proto_list[256];
static size_t next_proto_list_len; static size_t next_proto_list_len;
static int next_proto_cb(SSL *s _U_, const unsigned char **data, static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
unsigned int *len, void *arg _U_) { void *arg)
{
*data = next_proto_list; *data = next_proto_list;
*len = (unsigned int)next_proto_list_len; *len = next_proto_list_len;
return SSL_TLSEXT_ERR_OK; return SSL_TLSEXT_ERR_OK;
} }
/* Create SSL_CTX. */ /* 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; SSL_CTX *ssl_ctx;
EC_KEY *ecdh;
ssl_ctx = SSL_CTX_new(SSLv23_server_method()); ssl_ctx = SSL_CTX_new(SSLv23_server_method());
if (!ssl_ctx) { if(!ssl_ctx) {
errx(1, "Could not create SSL/TLS context: %s", errx(1, "Could not create SSL/TLS context: %s",
ERR_error_string(ERR_get_error(), NULL)); ERR_error_string(ERR_get_error(), NULL));
} }
SSL_CTX_set_options(ssl_ctx, SSL_CTX_set_options(ssl_ctx,
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); if(SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file,
if (!ecdh) { SSL_FILETYPE_PEM) != 1) {
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) {
errx(1, "Could not read private key file %s", key_file); errx(1, "Could not read private key file %s", key_file);
} }
if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) { if(SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
errx(1, "Could not read certificate file %s", cert_file); errx(1, "Could not read certificate file %s", cert_file);
} }
@@ -142,10 +117,11 @@ static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
} }
/* Create SSL object */ /* Create SSL object */
static SSL *create_ssl(SSL_CTX *ssl_ctx) { static SSL* create_ssl(SSL_CTX *ssl_ctx)
{
SSL *ssl; SSL *ssl;
ssl = SSL_new(ssl_ctx); ssl = SSL_new(ssl_ctx);
if (!ssl) { if(!ssl) {
errx(1, "Could not create SSL/TLS session object: %s", errx(1, "Could not create SSL/TLS session object: %s",
ERR_error_string(ERR_get_error(), NULL)); ERR_error_string(ERR_get_error(), NULL));
} }
@@ -153,25 +129,28 @@ static SSL *create_ssl(SSL_CTX *ssl_ctx) {
} }
static void add_stream(http2_session_data *session_data, 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; stream_data->next = session_data->root.next;
session_data->root.next = stream_data; session_data->root.next = stream_data;
stream_data->prev = &session_data->root; stream_data->prev = &session_data->root;
if (stream_data->next) { if(stream_data->next) {
stream_data->next->prev = stream_data; stream_data->next->prev = stream_data;
} }
} }
static void remove_stream(http2_session_data *session_data _U_, static void remove_stream(http2_session_data *session_data,
http2_stream_data *stream_data) { http2_stream_data *stream_data)
{
stream_data->prev->next = stream_data->next; stream_data->prev->next = stream_data->next;
if (stream_data->next) { if(stream_data->next) {
stream_data->next->prev = stream_data->prev; stream_data->next->prev = stream_data->prev;
} }
} }
static http2_stream_data * static http2_stream_data* create_http2_stream_data
create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) { (http2_session_data *session_data, int32_t stream_id)
{
http2_stream_data *stream_data; http2_stream_data *stream_data;
stream_data = malloc(sizeof(http2_stream_data)); stream_data = malloc(sizeof(http2_stream_data));
memset(stream_data, 0, sizeof(http2_stream_data)); memset(stream_data, 0, sizeof(http2_stream_data));
@@ -182,18 +161,20 @@ create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) {
return stream_data; 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) { {
if(stream_data->fd != -1) {
close(stream_data->fd); close(stream_data->fd);
} }
free(stream_data->request_path); free(stream_data->request_path);
free(stream_data); free(stream_data);
} }
static http2_session_data *create_http2_session_data(app_context *app_ctx, static http2_session_data* create_http2_session_data(app_context *app_ctx,
int fd, int fd,
struct sockaddr *addr, struct sockaddr *addr,
int addrlen) { int addrlen)
{
int rv; int rv;
http2_session_data *session_data; http2_session_data *session_data;
SSL *ssl; SSL *ssl;
@@ -205,11 +186,13 @@ static http2_session_data *create_http2_session_data(app_context *app_ctx,
memset(session_data, 0, sizeof(http2_session_data)); memset(session_data, 0, sizeof(http2_session_data));
session_data->app_ctx = app_ctx; session_data->app_ctx = app_ctx;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
session_data->bev = bufferevent_openssl_socket_new( session_data->bev = bufferevent_openssl_socket_new
app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING, (app_ctx->evbase, fd, ssl,
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); 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); rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
if (rv != 0) { if(rv != 0) {
session_data->client_addr = strdup("(unknown)"); session_data->client_addr = strdup("(unknown)");
} else { } else {
session_data->client_addr = strdup(host); session_data->client_addr = strdup(host);
@@ -218,16 +201,17 @@ static http2_session_data *create_http2_session_data(app_context *app_ctx,
return session_data; 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; http2_stream_data *stream_data;
SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev); SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
fprintf(stderr, "%s disconnected\n", session_data->client_addr); fprintf(stderr, "%s disconnected\n", session_data->client_addr);
if (ssl) { if(ssl) {
SSL_shutdown(ssl); SSL_shutdown(ssl);
} }
bufferevent_free(session_data->bev); bufferevent_free(session_data->bev);
nghttp2_session_del(session_data->session); nghttp2_session_del(session_data->session);
for (stream_data = session_data->root.next; stream_data;) { for(stream_data = session_data->root.next; stream_data;) {
http2_stream_data *next = stream_data->next; http2_stream_data *next = stream_data->next;
delete_http2_stream_data(stream_data); delete_http2_stream_data(stream_data);
stream_data = next; stream_data = next;
@@ -238,10 +222,11 @@ static void delete_http2_session_data(http2_session_data *session_data) {
/* Serialize the frame and send (or buffer) the data to /* Serialize the frame and send (or buffer) the data to
bufferevent. */ bufferevent. */
static int session_send(http2_session_data *session_data) { static int session_send(http2_session_data *session_data)
{
int rv; int rv;
rv = nghttp2_session_send(session_data->session); rv = nghttp2_session_send(session_data->session);
if (rv != 0) { if(rv != 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv)); warnx("Fatal error: %s", nghttp2_strerror(rv));
return -1; return -1;
} }
@@ -252,34 +237,33 @@ static int session_send(http2_session_data *session_data) {
function. Invocation of nghttp2_session_mem_recv() may make function. Invocation of nghttp2_session_mem_recv() may make
additional pending frames, so call session_send() at the end of the additional pending frames, so call session_send() at the end of the
function. */ function. */
static int session_recv(http2_session_data *session_data) { static int session_recv(http2_session_data *session_data)
ssize_t readlen; {
int rv;
struct evbuffer *input = bufferevent_get_input(session_data->bev); struct evbuffer *input = bufferevent_get_input(session_data->bev);
size_t datalen = evbuffer_get_length(input); size_t datalen = evbuffer_get_length(input);
unsigned char *data = evbuffer_pullup(input, -1); unsigned char *data = evbuffer_pullup(input, -1);
rv = nghttp2_session_mem_recv(session_data->session, data, datalen);
readlen = nghttp2_session_mem_recv(session_data->session, data, datalen); if(rv < 0) {
if (readlen < 0) { warnx("Fatal error: %s", nghttp2_strerror(rv));
warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
return -1; return -1;
} }
if (evbuffer_drain(input, readlen) != 0) { evbuffer_drain(input, rv);
warnx("Fatal error: evbuffer_drain failed"); if(session_send(session_data) != 0) {
return -1;
}
if (session_send(session_data) != 0) {
return -1; return -1;
} }
return 0; return 0;
} }
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data, static ssize_t send_callback(nghttp2_session *session,
size_t length, int flags _U_, void *user_data) { const uint8_t *data, size_t length,
http2_session_data *session_data = (http2_session_data *)user_data; int flags, void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
struct bufferevent *bev = session_data->bev; struct bufferevent *bev = session_data->bev;
/* Avoid excessive buffering in server side. */ /* Avoid excessive buffering in server side. */
if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >= if(evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
OUTPUT_WOULDBLOCK_THRESHOLD) { OUTPUT_WOULDBLOCK_THRESHOLD) {
return NGHTTP2_ERR_WOULDBLOCK; return NGHTTP2_ERR_WOULDBLOCK;
} }
bufferevent_write(bev, data, length); bufferevent_write(bev, data, length);
@@ -287,24 +271,26 @@ static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
} }
/* Returns nonzero if the string |s| ends with the substring |sub| */ /* 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 slen = strlen(s);
size_t sublen = strlen(sub); size_t sublen = strlen(sub);
if (slen < sublen) { if(slen < sublen) {
return 0; return 0;
} }
return memcmp(s + slen - sublen, sub, sublen) == 0; return memcmp(s + slen - sublen, sub, sublen) == 0;
} }
/* Returns int value of hex string character |c| */ /* 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') { {
if('0' <= c && c <= '9') {
return c - '0'; return c - '0';
} }
if ('A' <= c && c <= 'F') { if('A' <= c && c <= 'F') {
return c - 'A' + 10; return c - 'A' + 10;
} }
if ('a' <= c && c <= 'f') { if('a' <= c && c <= 'f') {
return c - 'a' + 10; return c - 'a' + 10;
} }
return 0; return 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 and returns the decoded byte string in allocated buffer. The return
value is NULL terminated. The caller must free the returned value is NULL terminated. The caller must free the returned
string. */ 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; char *res;
res = malloc(valuelen + 1); res = malloc(valuelen + 1);
if (valuelen > 3) { if(valuelen > 3) {
size_t i, j; size_t i, j;
for (i = 0, j = 0; i < valuelen - 2;) { for(i = 0, j = 0; i < valuelen - 2;) {
if (value[i] != '%' || !isxdigit(value[i + 1]) || if(value[i] != '%' ||
!isxdigit(value[i + 2])) { !isxdigit(value[i + 1]) || !isxdigit(value[i + 2])) {
res[j++] = value[i++]; res[j++] = value[i++];
continue; continue;
} }
@@ -338,33 +325,33 @@ static char *percent_decode(const uint8_t *value, size_t valuelen) {
return res; return res;
} }
static ssize_t file_read_callback(nghttp2_session *session _U_, static ssize_t file_read_callback
int32_t stream_id _U_, uint8_t *buf, (nghttp2_session *session, int32_t stream_id,
size_t length, uint32_t *data_flags, uint8_t *buf, size_t length, int *eof,
nghttp2_data_source *source, nghttp2_data_source *source, void *user_data)
void *user_data _U_) { {
int fd = source->fd; int fd = source->fd;
ssize_t r; ssize_t r;
while ((r = read(fd, buf, length)) == -1 && errno == EINTR) while((r = read(fd, buf, length)) == -1 && errno == EINTR);
; if(r == -1) {
if (r == -1) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
} }
if (r == 0) { if(r == 0) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF; *eof = 1;
} }
return r; return r;
} }
static int send_response(nghttp2_session *session, int32_t stream_id, 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; int rv;
nghttp2_data_provider data_prd; nghttp2_data_provider data_prd;
data_prd.source.fd = fd; data_prd.source.fd = fd;
data_prd.read_callback = file_read_callback; data_prd.read_callback = file_read_callback;
rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd); rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
if (rv != 0) { if(rv != 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv)); warnx("Fatal error: %s", nghttp2_strerror(rv));
return -1; return -1;
} }
@@ -372,40 +359,34 @@ static int send_response(nghttp2_session *session, int32_t stream_id,
} }
const char ERROR_HTML[] = "<html><head><title>404</title></head>" const char ERROR_HTML[] = "<html><head><title>404</title></head>"
"<body><h1>404 Not Found</h1></body></html>"; "<body><h1>404 Not Found</h1></body></html>";
static int error_reply(nghttp2_session *session, static int error_reply(nghttp2_session *session,
http2_stream_data *stream_data) { http2_stream_data *stream_data)
{
int rv; int rv;
ssize_t writelen;
int pipefd[2]; int pipefd[2];
nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")}; nghttp2_nv hdrs[] = {
MAKE_NV(":status", "404")
};
rv = pipe(pipefd); rv = pipe(pipefd);
if (rv != 0) { if(rv != 0) {
warn("Could not create pipe"); warn("Could not create pipe");
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
stream_data->stream_id, stream_data->stream_id,
NGHTTP2_INTERNAL_ERROR); NGHTTP2_INTERNAL_ERROR);
if (rv != 0) { if(rv != 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv)); warnx("Fatal error: %s", nghttp2_strerror(rv));
return -1; return -1;
} }
return 0; return 0;
} }
write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
close(pipefd[1]); close(pipefd[1]);
if (writelen != sizeof(ERROR_HTML) - 1) {
close(pipefd[0]);
return -1;
}
stream_data->fd = pipefd[0]; stream_data->fd = pipefd[0];
if(send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), pipefd[0]) != 0) {
pipefd[0]) != 0) {
close(pipefd[0]); close(pipefd[0]);
return -1; return -1;
} }
@@ -415,26 +396,26 @@ static int error_reply(nghttp2_session *session,
/* nghttp2_on_header_callback: Called when nghttp2 library emits /* nghttp2_on_header_callback: Called when nghttp2 library emits
single header name/value pair. */ single header name/value pair. */
static int on_header_callback(nghttp2_session *session, static int on_header_callback(nghttp2_session *session,
const nghttp2_frame *frame, const uint8_t *name, const nghttp2_frame *frame,
size_t namelen, const uint8_t *value, const uint8_t *name, size_t namelen,
size_t valuelen, uint8_t flags _U_, const uint8_t *value, size_t valuelen,
void *user_data _U_) { void *user_data)
{
http2_stream_data *stream_data; http2_stream_data *stream_data;
const char PATH[] = ":path"; const char PATH[] = ":path";
switch (frame->hd.type) { switch(frame->hd.type) {
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) { if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
break; break;
} }
stream_data = stream_data = nghttp2_session_get_stream_user_data(session,
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); frame->hd.stream_id);
if (!stream_data || stream_data->request_path) { if(stream_data->request_path) {
break; break;
} }
if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) { if(namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
size_t j; 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); stream_data->request_path = percent_decode(value, j);
} }
break; break;
@@ -444,12 +425,13 @@ static int on_header_callback(nghttp2_session *session,
static int on_begin_headers_callback(nghttp2_session *session, static int on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame, const nghttp2_frame *frame,
void *user_data) { void *user_data)
http2_session_data *session_data = (http2_session_data *)user_data; {
http2_session_data *session_data = (http2_session_data*)user_data;
http2_stream_data *stream_data; http2_stream_data *stream_data;
if (frame->hd.type != NGHTTP2_HEADERS || if(frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) { frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0; return 0;
} }
stream_data = create_http2_stream_data(session_data, frame->hd.stream_id); stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
@@ -460,47 +442,52 @@ static int on_begin_headers_callback(nghttp2_session *session,
/* Minimum check for directory traversal. Returns nonzero if it is /* Minimum check for directory traversal. Returns nonzero if it is
safe. */ safe. */
static int check_path(const char *path) { static int check_path(const char *path)
{
/* We don't like '\' in url. */ /* We don't like '\' in url. */
return path[0] && path[0] == '/' && strchr(path, '\\') == NULL && return path[0] && path[0] == '/' &&
strstr(path, "/../") == NULL && strstr(path, "/./") == NULL && strchr(path, '\\') == NULL &&
!ends_with(path, "/..") && !ends_with(path, "/."); strstr(path, "/../") == NULL &&
strstr(path, "/./") == NULL &&
!ends_with(path, "/..") && !ends_with(path, "/.");
} }
static int on_request_recv(nghttp2_session *session, static int on_request_recv(nghttp2_session *session,
http2_session_data *session_data, http2_session_data *session_data,
http2_stream_data *stream_data) { http2_stream_data *stream_data)
{
int fd; int fd;
nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")}; nghttp2_nv hdrs[] = {
MAKE_NV(":status", "200")
};
char *rel_path; char *rel_path;
if (!stream_data->request_path) { if(!stream_data->request_path) {
if (error_reply(session, stream_data) != 0) { if(error_reply(session, stream_data) != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE; return NGHTTP2_ERR_CALLBACK_FAILURE;
} }
return 0; return 0;
} }
fprintf(stderr, "%s GET %s\n", session_data->client_addr, fprintf(stderr, "%s GET %s\n", session_data->client_addr,
stream_data->request_path); stream_data->request_path);
if (!check_path(stream_data->request_path)) { if(!check_path(stream_data->request_path)) {
if (error_reply(session, stream_data) != 0) { if(error_reply(session, stream_data) != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE; return NGHTTP2_ERR_CALLBACK_FAILURE;
} }
return 0; 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); fd = open(rel_path, O_RDONLY);
if (fd == -1) { if(fd == -1) {
if (error_reply(session, stream_data) != 0) { if(error_reply(session, stream_data) != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE; return NGHTTP2_ERR_CALLBACK_FAILURE;
} }
return 0; return 0;
} }
stream_data->fd = fd; stream_data->fd = fd;
if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) != if(send_response(session, stream_data->stream_id, hdrs,
0) { ARRLEN(hdrs), fd) != 0) {
close(fd); close(fd);
return NGHTTP2_ERR_CALLBACK_FAILURE; return NGHTTP2_ERR_CALLBACK_FAILURE;
} }
@@ -508,19 +495,20 @@ static int on_request_recv(nghttp2_session *session,
} }
static int on_frame_recv_callback(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_session_data *session_data = (http2_session_data*)user_data;
http2_stream_data *stream_data; http2_stream_data *stream_data;
switch (frame->hd.type) { switch(frame->hd.type) {
case NGHTTP2_DATA: case NGHTTP2_DATA:
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
/* Check that the client request has finished */ /* Check that the client request has finished */
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
stream_data = stream_data = nghttp2_session_get_stream_user_data(session,
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); frame->hd.stream_id);
/* For DATA and HEADERS frame, this callback may be called after /* For DATA and HEADERS frame, this callback may be called after
on_stream_close_callback. Check that stream still alive. */ on_stream_close_callback. Check that stream still alive. */
if (!stream_data) { if(!stream_data) {
return 0; return 0;
} }
return on_request_recv(session, session_data, stream_data); return on_request_recv(session, session_data, stream_data);
@@ -532,54 +520,44 @@ static int on_frame_recv_callback(nghttp2_session *session,
return 0; return 0;
} }
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, static int on_stream_close_callback(nghttp2_session *session,
uint32_t error_code _U_, void *user_data) { int32_t stream_id,
http2_session_data *session_data = (http2_session_data *)user_data; nghttp2_error_code error_code,
void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
http2_stream_data *stream_data; http2_stream_data *stream_data;
stream_data = nghttp2_session_get_stream_user_data(session, stream_id); stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
if (!stream_data) {
return 0;
}
remove_stream(session_data, stream_data); remove_stream(session_data, stream_data);
delete_http2_stream_data(stream_data); delete_http2_stream_data(stream_data);
return 0; return 0;
} }
static void initialize_nghttp2_session(http2_session_data *session_data) { static void initialize_nghttp2_session(http2_session_data *session_data)
nghttp2_session_callbacks *callbacks; {
nghttp2_session_callbacks callbacks = {0};
nghttp2_session_callbacks_new(&callbacks); callbacks.send_callback = send_callback;
callbacks.on_frame_recv_callback = on_frame_recv_callback;
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); callbacks.on_stream_close_callback = on_stream_close_callback;
callbacks.on_header_callback = on_header_callback;
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, callbacks.on_begin_headers_callback = on_begin_headers_callback;
on_frame_recv_callback); nghttp2_session_server_new(&session_data->session, &callbacks, session_data);
nghttp2_session_callbacks_set_on_stream_close_callback(
callbacks, on_stream_close_callback);
nghttp2_session_callbacks_set_on_header_callback(callbacks,
on_header_callback);
nghttp2_session_callbacks_set_on_begin_headers_callback(
callbacks, on_begin_headers_callback);
nghttp2_session_server_new(&session_data->session, callbacks, session_data);
nghttp2_session_callbacks_del(callbacks);
} }
/* 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 */ 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_entry iv[1] = {
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
};
int rv; int rv;
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv, rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
ARRLEN(iv)); iv, ARRLEN(iv));
if (rv != 0) { if(rv != 0) {
warnx("Fatal error: %s", nghttp2_strerror(rv)); warnx("Fatal error: %s", nghttp2_strerror(rv));
return -1; return -1;
} }
@@ -588,9 +566,10 @@ static int send_server_connection_header(http2_session_data *session_data) {
/* readcb for bufferevent after client connection header was /* readcb for bufferevent after client connection header was
checked. */ 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) { http2_session_data *session_data = (http2_session_data*)ptr;
if(session_recv(session_data) != 0) {
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
return; return;
} }
@@ -604,60 +583,88 @@ static void readcb(struct bufferevent *bev _U_, void *ptr) {
process pending data in the output buffer. This is necessary process pending data in the output buffer. This is necessary
because we have a threshold on the buffer size to avoid too much because we have a threshold on the buffer size to avoid too much
buffering. See send_callback(). */ 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) { http2_session_data *session_data = (http2_session_data*)ptr;
if(evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
return; return;
} }
if (nghttp2_session_want_read(session_data->session) == 0 && if(nghttp2_session_want_read(session_data->session) == 0 &&
nghttp2_session_want_write(session_data->session) == 0) { nghttp2_session_want_write(session_data->session) == 0) {
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
return; return;
} }
if (session_send(session_data) != 0) { if(session_send(session_data) != 0) {
delete_http2_session_data(session_data); delete_http2_session_data(session_data);
return; return;
} }
} }
/* eventcb for bufferevent */ /* 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) { http2_session_data *session_data = (http2_session_data*)ptr;
if(events & BEV_EVENT_CONNECTED) {
fprintf(stderr, "%s connected\n", session_data->client_addr); 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; return;
} }
if (events & BEV_EVENT_EOF) { if(events & BEV_EVENT_EOF) {
fprintf(stderr, "%s EOF\n", session_data->client_addr); fprintf(stderr, "%s EOF\n", session_data->client_addr);
} else if (events & BEV_EVENT_ERROR) { } else if(events & BEV_EVENT_ERROR) {
fprintf(stderr, "%s network error\n", session_data->client_addr); fprintf(stderr, "%s network error\n", session_data->client_addr);
} else if (events & BEV_EVENT_TIMEOUT) { } else if(events & BEV_EVENT_TIMEOUT) {
fprintf(stderr, "%s timeout\n", session_data->client_addr); fprintf(stderr, "%s timeout\n", session_data->client_addr);
} }
delete_http2_session_data(session_data); 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 */ /* callback for evconnlistener */
static void acceptcb(struct evconnlistener *listener _U_, int fd, static void acceptcb(struct evconnlistener *listener, int fd,
struct sockaddr *addr, int addrlen, void *arg) { struct sockaddr *addr, int addrlen, void *arg)
app_context *app_ctx = (app_context *)arg; {
app_context *app_ctx = (app_context*)arg;
http2_session_data *session_data; http2_session_data *session_data;
session_data = create_http2_session_data(app_ctx, fd, addr, addrlen); session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
bufferevent_setcb(session_data->bev, handshake_readcb, NULL, eventcb,
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data); session_data);
} }
static void start_listen(struct event_base *evbase, const char *service, static void start_listen(struct event_base *evbase, const char *service,
app_context *app_ctx) { app_context *app_ctx)
{
int rv; int rv;
struct addrinfo hints; struct addrinfo hints;
struct addrinfo *res, *rp; struct addrinfo *res, *rp;
@@ -668,20 +675,19 @@ static void start_listen(struct event_base *evbase, const char *service,
hints.ai_flags = AI_PASSIVE; hints.ai_flags = AI_PASSIVE;
#ifdef AI_ADDRCONFIG #ifdef AI_ADDRCONFIG
hints.ai_flags |= AI_ADDRCONFIG; hints.ai_flags |= AI_ADDRCONFIG;
#endif /* AI_ADDRCONFIG */ #endif // AI_ADDRCONFIG
rv = getaddrinfo(NULL, service, &hints, &res); rv = getaddrinfo(NULL, service, &hints, &res);
if (rv != 0) { if(rv != 0) {
errx(1, NULL); errx(1, NULL);
} }
for (rp = res; rp; rp = rp->ai_next) { for(rp = res; rp; rp = rp->ai_next) {
struct evconnlistener *listener; struct evconnlistener *listener;
listener = evconnlistener_new_bind( listener = evconnlistener_new_bind(evbase, acceptcb, app_ctx,
evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, LEV_OPT_CLOSE_ON_FREE |
16, rp->ai_addr, rp->ai_addrlen); LEV_OPT_REUSEABLE, 16,
if (listener) { rp->ai_addr, rp->ai_addrlen);
freeaddrinfo(res); if(listener) {
return; return;
} }
} }
@@ -689,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, 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)); memset(app_ctx, 0, sizeof(app_context));
app_ctx->ssl_ctx = ssl_ctx; app_ctx->ssl_ctx = ssl_ctx;
app_ctx->evbase = evbase; app_ctx->evbase = evbase;
} }
static void run(const char *service, const char *key_file, static void run(const char *service,
const char *cert_file) { const char *key_file, const char *cert_file)
{
SSL_CTX *ssl_ctx; SSL_CTX *ssl_ctx;
app_context app_ctx; app_context app_ctx;
struct event_base *evbase; struct event_base *evbase;
@@ -712,10 +720,11 @@ static void run(const char *service, const char *key_file,
SSL_CTX_free(ssl_ctx); SSL_CTX_free(ssl_ctx);
} }
int main(int argc, char **argv) { int main(int argc, char **argv)
{
struct sigaction act; struct sigaction act;
if (argc < 4) { if(argc < 4) {
fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n"); fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -726,8 +735,6 @@ int main(int argc, char **argv) {
SSL_load_error_strings(); SSL_load_error_strings();
SSL_library_init(); SSL_library_init();
OpenSSL_add_all_algorithms();
OPENSSL_config(NULL);
run(argv[1], argv[2], argv[3]); run(argv[1], argv[2], argv[3]);
return 0; return 0;

File diff suppressed because it is too large Load Diff

View File

@@ -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')

View File

@@ -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()

View File

@@ -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
View File

0
genvchartbl.py Executable file → Normal file
View File

View File

@@ -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()

View File

@@ -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())

View File

@@ -1,2 +0,0 @@
# generated files
config.go

View File

@@ -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 github.com/tatsuhiro-t/spdy
it:
sh setenv go test -v

View File

@@ -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