Add C++ library libnghttp2_asio on top of libnghttp2

The libnghttp2_asio library is C++ library built on top of libnghttp2.
Currently, it has server API and easily create HTTP/2 server using
node.js like API calls.  See the example server source code in
examples/asio-sv.cc.  The library depends on Boost::ASIO library.
This commit is contained in:
Tatsuhiro Tsujikawa 2014-09-21 18:28:06 +09:00
parent 446f8f13aa
commit 5d0bf4cc84
20 changed files with 3032 additions and 0 deletions

View File

@ -76,6 +76,11 @@ AC_ARG_ENABLE([hpack-tools],
[Build HPACK tools [default=check]])],
[request_hpack_tools=$enableval], [request_hpack_tools=check])
AC_ARG_ENABLE([asio-lib],
[AS_HELP_STRING([--enable-asio-lib],
[Build C++ libnghttp2_asio library [default=no]])],
[request_asio_lib=$enableval], [request_asio_lib=no])
AC_ARG_ENABLE([examples],
[AS_HELP_STRING([--enable-examples],
[Build examples [default=check]])],
@ -329,6 +334,23 @@ fi
AM_CONDITIONAL([HAVE_SPDYLAY], [ test "x${have_spdylay}" = "xyes" ])
# Check Boost Asio library
have_asio_lib=no
AX_BOOST_BASE([1.55.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
# The nghttp, nghttpd and nghttpx under src depend on zlib, OpenSSL
# and libevent_openssl
enable_app=no
@ -361,6 +383,16 @@ fi
AM_CONDITIONAL([ENABLE_HPACK_TOOLS], [ test "x${enable_hpack_tools}" = "xyes" ])
# C++ library libnghttp2_asio
enable_asio_lib=no
if test "x${request_asio_lib}" != "xno" &&
test "x${have_asio_lib}" = "xyes"; then
enable_asio_lib=yes
fi
AM_CONDITIONAL([ENABLE_ASIO_LIB], [ test "x${enable_asio_lib}" = "xyes" ])
# The example programs depend on OpenSSL and libevent_openssl
enable_examples=no
if test "x${request_examples}" != "xno" &&
@ -505,6 +537,8 @@ AC_CONFIG_FILES([
tests/testdata/Makefile
third-party/Makefile
src/Makefile
src/includes/Makefile
src/libnghttp2_asio.pc
examples/Makefile
python/Makefile
python/setup.py
@ -553,8 +587,14 @@ AC_MSG_NOTICE([summary of build options:
Spdylay: ${have_spdylay}
Jansson: ${have_jansson}
Jemalloc: ${have_jemalloc}
Boost CPPFLAGS: ${BOOST_CPPFLAGS}
Boost LDFLAGS: ${BOOST_LDFLAGS}
Boost::ASIO: ${BOOST_ASIO_LIB}
Boost::System: ${BOOST_SYSTEM_LIB}
Boost::Thread: ${BOOST_THREAD_LIB}
Applications: ${enable_app}
HPACK tools: ${enable_hpack_tools}
Libnghttp2_asio:${enable_asio_lib}
Examples: ${enable_examples}
Python bindings:${enable_python_bindings}
Failmalloc: ${request_failmalloc}

View File

@ -27,6 +27,7 @@ AM_CPPFLAGS = \
-Wall \
-I$(top_srcdir)/lib/includes \
-I$(top_builddir)/lib/includes \
-I$(top_srcdir)/src/includes \
-I$(top_srcdir)/third-party \
@LIBEVENT_OPENSSL_CFLAGS@ \
@OPENSSL_CFLAGS@ \
@ -48,4 +49,18 @@ libevent_server_SOURCES = libevent-server.c
deflate_SOURCES = deflate.c
if ENABLE_ASIO_LIB
noinst_PROGRAMS += asio-sv
asio_sv_SOURCES = asio-sv.cc
asio_sv_CPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS}
asio_sv_LDFLAGS = \
@JEMALLOC_LIBS@ \
${BOOST_LDFLAGS} \
${BOOST_SYSTEM_LIB}
asio_sv_LDADD = $(top_builddir)/src/libnghttp2_asio.la
endif # ENABLE_ASIO_LIB
endif # ENABLE_EXAMPLES

78
examples/asio-sv.cc Normal file
View File

@ -0,0 +1,78 @@
/*
* 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.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 < 3) {
std::cerr << "Usage: asio-sv <port> <threads> <private-key-file> "
<< "<cert-file>\n";
return 1;
}
uint16_t port = std::stoi(argv[1]);
std::size_t num_threads = std::stoi(argv[2]);
http2 server;
server.num_threads(num_threads);
if(argc >= 5) {
server.tls(argv[3], argv[4]);
}
server.listen
("*", port,
[](std::shared_ptr<request> req, std::shared_ptr<response> res)
{
res->write_head(200, { header{ "foo", "bar" } });
res->end("hello, world");
});
} catch (std::exception& e) {
std::cerr << "exception: " << e.what() << "\n";
}
return 0;
}

110
m4/ax_boost_asio.m4 Normal file
View File

@ -0,0 +1,110 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_boost_asio.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_BOOST_ASIO
#
# DESCRIPTION
#
# Test for Asio library from the Boost C++ libraries. The macro requires a
# preceding call to AX_BOOST_BASE. Further documentation is available at
# <http://randspringer.de/boost/index.html>.
#
# This macro calls:
#
# AC_SUBST(BOOST_ASIO_LIB)
#
# And sets:
#
# HAVE_BOOST_ASIO
#
# LICENSE
#
# Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
# Copyright (c) 2008 Pete Greenwell <pete@mu.org>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 16
AC_DEFUN([AX_BOOST_ASIO],
[
AC_ARG_WITH([boost-asio],
AS_HELP_STRING([--with-boost-asio@<:@=special-lib@:>@],
[use the ASIO library from boost - it is possible to specify a certain library for the linker
e.g. --with-boost-asio=boost_system-gcc41-mt-1_34 ]),
[
if test "$withval" = "no"; then
want_boost="no"
elif test "$withval" = "yes"; then
want_boost="yes"
ax_boost_user_asio_lib=""
else
want_boost="yes"
ax_boost_user_asio_lib="$withval"
fi
],
[want_boost="yes"]
)
if test "x$want_boost" = "xyes"; then
AC_REQUIRE([AC_PROG_CC])
CPPFLAGS_SAVED="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
export CPPFLAGS
LDFLAGS_SAVED="$LDFLAGS"
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
export LDFLAGS
AC_CACHE_CHECK(whether the Boost::ASIO library is available,
ax_cv_boost_asio,
[AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ @%:@include <boost/asio.hpp>
]],
[[
boost::asio::io_service io;
boost::system::error_code timer_result;
boost::asio::deadline_timer t(io);
t.cancel();
io.run_one();
return 0;
]])],
ax_cv_boost_asio=yes, ax_cv_boost_asio=no)
AC_LANG_POP([C++])
])
if test "x$ax_cv_boost_asio" = "xyes"; then
AC_DEFINE(HAVE_BOOST_ASIO,,[define if the Boost::ASIO library is available])
BN=boost_system
BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
if test "x$ax_boost_user_asio_lib" = "x"; then
for ax_lib in `ls $BOOSTLIBDIR/libboost_system*.so* $BOOSTLIBDIR/libboost_system*.dylib* $BOOSTLIBDIR/libboost_system*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_system.*\)\.so.*$;\1;' -e 's;^lib\(boost_system.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_system.*\)\.a.*$;\1;' ` ; do
AC_CHECK_LIB($ax_lib, main, [BOOST_ASIO_LIB="-l$ax_lib" AC_SUBST(BOOST_ASIO_LIB) link_thread="yes" break],
[link_thread="no"])
done
else
for ax_lib in $ax_boost_user_asio_lib $BN-$ax_boost_user_asio_lib; do
AC_CHECK_LIB($ax_lib, main,
[BOOST_ASIO_LIB="-l$ax_lib" AC_SUBST(BOOST_ASIO_LIB) link_asio="yes" break],
[link_asio="no"])
done
fi
if test "x$ax_lib" = "x"; then
AC_MSG_ERROR(Could not find a version of the library!)
fi
if test "x$link_asio" = "xno"; then
AC_MSG_ERROR(Could not link against $ax_lib !)
fi
fi
CPPFLAGS="$CPPFLAGS_SAVED"
LDFLAGS="$LDFLAGS_SAVED"
fi
])

275
m4/ax_boost_base.m4 Normal file
View File

@ -0,0 +1,275 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_boost_base.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
#
# DESCRIPTION
#
# Test for the Boost C++ libraries of a particular version (or newer)
#
# If no path to the installed boost library is given the macro searchs
# under /usr, /usr/local, /opt and /opt/local and evaluates the
# $BOOST_ROOT environment variable. Further documentation is available at
# <http://randspringer.de/boost/index.html>.
#
# This macro calls:
#
# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS)
#
# And sets:
#
# HAVE_BOOST
#
# LICENSE
#
# Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
# Copyright (c) 2009 Peter Adolphs
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 25
AC_DEFUN([AX_BOOST_BASE],
[
AC_ARG_WITH([boost],
[AS_HELP_STRING([--with-boost@<:@=ARG@:>@],
[use Boost library from a standard location (ARG=yes),
from the specified location (ARG=<path>),
or disable it (ARG=no)
@<:@ARG=yes@:>@ ])],
[
if test "$withval" = "no"; then
want_boost="no"
elif test "$withval" = "yes"; then
want_boost="yes"
ac_boost_path=""
else
want_boost="yes"
ac_boost_path="$withval"
fi
],
[want_boost="yes"])
AC_ARG_WITH([boost-libdir],
AS_HELP_STRING([--with-boost-libdir=LIB_DIR],
[Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]),
[
if test -d "$withval"
then
ac_boost_lib_path="$withval"
else
AC_MSG_ERROR(--with-boost-libdir expected directory name)
fi
],
[ac_boost_lib_path=""]
)
if test "x$want_boost" = "xyes"; then
boost_lib_version_req=ifelse([$1], ,1.20.0,$1)
boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'`
boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'`
boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'`
boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'`
if test "x$boost_lib_version_req_sub_minor" = "x" ; then
boost_lib_version_req_sub_minor="0"
fi
WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor`
AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req)
succeeded=no
dnl On 64-bit systems check for system libraries in both lib64 and lib.
dnl The former is specified by FHS, but e.g. Debian does not adhere to
dnl this (as it rises problems for generic multi-arch support).
dnl The last entry in the list is chosen by default when no libraries
dnl are found, e.g. when only header-only libraries are installed!
libsubdirs="lib"
ax_arch=`uname -m`
case $ax_arch in
x86_64)
libsubdirs="lib64 libx32 lib lib64"
;;
ppc64|s390x|sparc64|aarch64|ppc64le)
libsubdirs="lib64 lib lib64 ppc64le"
;;
esac
dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give
dnl them priority over the other paths since, if libs are found there, they
dnl are almost assuredly the ones desired.
AC_REQUIRE([AC_CANONICAL_HOST])
libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs"
case ${host_cpu} in
i?86)
libsubdirs="lib/i386-${host_os} $libsubdirs"
;;
esac
dnl first we check the system location for boost libraries
dnl this location ist chosen if boost libraries are installed with the --layout=system option
dnl or if you install boost with RPM
if test "$ac_boost_path" != ""; then
BOOST_CPPFLAGS="-I$ac_boost_path/include"
for ac_boost_path_tmp in $libsubdirs; do
if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then
BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp"
break
fi
done
elif test "$cross_compiling" != yes; then
for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do
if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then
for libsubdir in $libsubdirs ; do
if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
done
BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir"
BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include"
break;
fi
done
fi
dnl overwrite ld flags if we have required special directory with
dnl --with-boost-libdir parameter
if test "$ac_boost_lib_path" != ""; then
BOOST_LDFLAGS="-L$ac_boost_lib_path"
fi
CPPFLAGS_SAVED="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
export CPPFLAGS
LDFLAGS_SAVED="$LDFLAGS"
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
export LDFLAGS
AC_REQUIRE([AC_PROG_CXX])
AC_LANG_PUSH(C++)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@%:@include <boost/version.hpp>
]], [[
#if BOOST_VERSION >= $WANT_BOOST_VERSION
// Everything is okay
#else
# error Boost version is too old
#endif
]])],[
AC_MSG_RESULT(yes)
succeeded=yes
found_system=yes
],[
])
AC_LANG_POP([C++])
dnl if we found no boost with system layout we search for boost libraries
dnl built and installed without the --layout=system option or for a staged(not installed) version
if test "x$succeeded" != "xyes"; then
_version=0
if test "$ac_boost_path" != ""; then
if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do
_version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
V_CHECK=`expr $_version_tmp \> $_version`
if test "$V_CHECK" = "1" ; then
_version=$_version_tmp
fi
VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE"
done
fi
else
if test "$cross_compiling" != yes; then
for ac_boost_path in /usr /usr/local /opt /opt/local ; do
if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do
_version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
V_CHECK=`expr $_version_tmp \> $_version`
if test "$V_CHECK" = "1" ; then
_version=$_version_tmp
best_path=$ac_boost_path
fi
done
fi
done
VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE"
if test "$ac_boost_lib_path" = ""; then
for libsubdir in $libsubdirs ; do
if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
done
BOOST_LDFLAGS="-L$best_path/$libsubdir"
fi
fi
if test "x$BOOST_ROOT" != "x"; then
for libsubdir in $libsubdirs ; do
if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
done
if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then
version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'`
stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'`
stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'`
V_CHECK=`expr $stage_version_shorten \>\= $_version`
if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then
AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT)
BOOST_CPPFLAGS="-I$BOOST_ROOT"
BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir"
fi
fi
fi
fi
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
export CPPFLAGS
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
export LDFLAGS
AC_LANG_PUSH(C++)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@%:@include <boost/version.hpp>
]], [[
#if BOOST_VERSION >= $WANT_BOOST_VERSION
// Everything is okay
#else
# error Boost version is too old
#endif
]])],[
AC_MSG_RESULT(yes)
succeeded=yes
found_system=yes
],[
])
AC_LANG_POP([C++])
fi
if test "$succeeded" != "yes" ; then
if test "$_version" = "0" ; then
AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation.]])
else
AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).])
fi
# execute ACTION-IF-NOT-FOUND (if present):
ifelse([$3], , :, [$3])
else
AC_SUBST(BOOST_CPPFLAGS)
AC_SUBST(BOOST_LDFLAGS)
AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available])
# execute ACTION-IF-FOUND (if present):
ifelse([$2], , :, [$2])
fi
CPPFLAGS="$CPPFLAGS_SAVED"
LDFLAGS="$LDFLAGS_SAVED"
fi
])

120
m4/ax_boost_system.m4 Normal file
View File

@ -0,0 +1,120 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_boost_system.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_BOOST_SYSTEM
#
# DESCRIPTION
#
# Test for System library from the Boost C++ libraries. The macro requires
# a preceding call to AX_BOOST_BASE. Further documentation is available at
# <http://randspringer.de/boost/index.html>.
#
# This macro calls:
#
# AC_SUBST(BOOST_SYSTEM_LIB)
#
# And sets:
#
# HAVE_BOOST_SYSTEM
#
# LICENSE
#
# Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
# Copyright (c) 2008 Michael Tindal
# Copyright (c) 2008 Daniel Casimiro <dan.casimiro@gmail.com>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 17
AC_DEFUN([AX_BOOST_SYSTEM],
[
AC_ARG_WITH([boost-system],
AS_HELP_STRING([--with-boost-system@<:@=special-lib@:>@],
[use the System library from boost - it is possible to specify a certain library for the linker
e.g. --with-boost-system=boost_system-gcc-mt ]),
[
if test "$withval" = "no"; then
want_boost="no"
elif test "$withval" = "yes"; then
want_boost="yes"
ax_boost_user_system_lib=""
else
want_boost="yes"
ax_boost_user_system_lib="$withval"
fi
],
[want_boost="yes"]
)
if test "x$want_boost" = "xyes"; then
AC_REQUIRE([AC_PROG_CC])
AC_REQUIRE([AC_CANONICAL_BUILD])
CPPFLAGS_SAVED="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
export CPPFLAGS
LDFLAGS_SAVED="$LDFLAGS"
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
export LDFLAGS
AC_CACHE_CHECK(whether the Boost::System library is available,
ax_cv_boost_system,
[AC_LANG_PUSH([C++])
CXXFLAGS_SAVE=$CXXFLAGS
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/system/error_code.hpp>]],
[[boost::system::system_category]])],
ax_cv_boost_system=yes, ax_cv_boost_system=no)
CXXFLAGS=$CXXFLAGS_SAVE
AC_LANG_POP([C++])
])
if test "x$ax_cv_boost_system" = "xyes"; then
AC_SUBST(BOOST_CPPFLAGS)
AC_DEFINE(HAVE_BOOST_SYSTEM,,[define if the Boost::System library is available])
BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
LDFLAGS_SAVE=$LDFLAGS
if test "x$ax_boost_user_system_lib" = "x"; then
for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do
ax_lib=${libextension}
AC_CHECK_LIB($ax_lib, exit,
[BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
[link_system="no"])
done
if test "x$link_system" != "xyes"; then
for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do
ax_lib=${libextension}
AC_CHECK_LIB($ax_lib, exit,
[BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
[link_system="no"])
done
fi
else
for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do
AC_CHECK_LIB($ax_lib, exit,
[BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
[link_system="no"])
done
fi
if test "x$ax_lib" = "x"; then
AC_MSG_ERROR(Could not find a version of the library!)
fi
if test "x$link_system" = "xno"; then
AC_MSG_ERROR(Could not link against $ax_lib !)
fi
fi
CPPFLAGS="$CPPFLAGS_SAVED"
LDFLAGS="$LDFLAGS_SAVED"
fi
])

149
m4/ax_boost_thread.m4 Normal file
View File

@ -0,0 +1,149 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_boost_thread.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_BOOST_THREAD
#
# DESCRIPTION
#
# Test for Thread library from the Boost C++ libraries. The macro requires
# a preceding call to AX_BOOST_BASE. Further documentation is available at
# <http://randspringer.de/boost/index.html>.
#
# This macro calls:
#
# AC_SUBST(BOOST_THREAD_LIB)
#
# And sets:
#
# HAVE_BOOST_THREAD
#
# LICENSE
#
# Copyright (c) 2009 Thomas Porschberg <thomas@randspringer.de>
# Copyright (c) 2009 Michael Tindal
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 27
AC_DEFUN([AX_BOOST_THREAD],
[
AC_ARG_WITH([boost-thread],
AS_HELP_STRING([--with-boost-thread@<:@=special-lib@:>@],
[use the Thread library from boost - it is possible to specify a certain library for the linker
e.g. --with-boost-thread=boost_thread-gcc-mt ]),
[
if test "$withval" = "no"; then
want_boost="no"
elif test "$withval" = "yes"; then
want_boost="yes"
ax_boost_user_thread_lib=""
else
want_boost="yes"
ax_boost_user_thread_lib="$withval"
fi
],
[want_boost="yes"]
)
if test "x$want_boost" = "xyes"; then
AC_REQUIRE([AC_PROG_CC])
AC_REQUIRE([AC_CANONICAL_BUILD])
CPPFLAGS_SAVED="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
export CPPFLAGS
LDFLAGS_SAVED="$LDFLAGS"
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
export LDFLAGS
AC_CACHE_CHECK(whether the Boost::Thread library is available,
ax_cv_boost_thread,
[AC_LANG_PUSH([C++])
CXXFLAGS_SAVE=$CXXFLAGS
if test "x$host_os" = "xsolaris" ; then
CXXFLAGS="-pthreads $CXXFLAGS"
elif test "x$host_os" = "xmingw32" ; then
CXXFLAGS="-mthreads $CXXFLAGS"
else
CXXFLAGS="-pthread $CXXFLAGS"
fi
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/thread/thread.hpp>]],
[[boost::thread_group thrds;
return 0;]])],
ax_cv_boost_thread=yes, ax_cv_boost_thread=no)
CXXFLAGS=$CXXFLAGS_SAVE
AC_LANG_POP([C++])
])
if test "x$ax_cv_boost_thread" = "xyes"; then
if test "x$host_os" = "xsolaris" ; then
BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS"
elif test "x$host_os" = "xmingw32" ; then
BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS"
else
BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS"
fi
AC_SUBST(BOOST_CPPFLAGS)
AC_DEFINE(HAVE_BOOST_THREAD,,[define if the Boost::Thread library is available])
BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
LDFLAGS_SAVE=$LDFLAGS
case "x$host_os" in
*bsd* )
LDFLAGS="-pthread $LDFLAGS"
break;
;;
esac
if test "x$ax_boost_user_thread_lib" = "x"; then
for libextension in `ls -r $BOOSTLIBDIR/libboost_thread* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'`; do
ax_lib=${libextension}
AC_CHECK_LIB($ax_lib, exit,
[BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break],
[link_thread="no"])
done
if test "x$link_thread" != "xyes"; then
for libextension in `ls -r $BOOSTLIBDIR/boost_thread* 2>/dev/null | sed 's,.*/,,' | sed 's,\..*,,'`; do
ax_lib=${libextension}
AC_CHECK_LIB($ax_lib, exit,
[BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break],
[link_thread="no"])
done
fi
else
for ax_lib in $ax_boost_user_thread_lib boost_thread-$ax_boost_user_thread_lib; do
AC_CHECK_LIB($ax_lib, exit,
[BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break],
[link_thread="no"])
done
fi
if test "x$ax_lib" = "x"; then
AC_MSG_ERROR(Could not find a version of the library!)
fi
if test "x$link_thread" = "xno"; then
AC_MSG_ERROR(Could not link against $ax_lib !)
else
case "x$host_os" in
*bsd* )
BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS"
break;
;;
esac
fi
fi
CPPFLAGS="$CPPFLAGS_SAVED"
LDFLAGS="$LDFLAGS_SAVED"
fi
])

View File

@ -20,6 +20,7 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
SUBDIRS = includes
bin_PROGRAMS =
check_PROGRAMS =
@ -30,6 +31,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/lib/includes \
-I$(top_builddir)/lib/includes \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/src/includes \
-I$(top_srcdir)/third-party \
@LIBSPDYLAY_CFLAGS@ \
@XML_CPPFLAGS@ \
@ -163,3 +165,33 @@ inflatehd_SOURCES = inflatehd.cc $(HPACK_TOOLS_COMMON_SRCS)
deflatehd_SOURCES = deflatehd.cc $(HPACK_TOOLS_COMMON_SRCS)
endif # ENABLE_HPACK_TOOLS
if ENABLE_ASIO_LIB
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libnghttp2_asio.pc
DISTCLEANFILES = $(pkgconfig_DATA)
lib_LTLIBRARIES = libnghttp2_asio.la
libnghttp2_asio_la_SOURCES = \
asio_connection.h \
asio_server.cc asio_server.h \
asio_io_service_pool.cc asio_io_service_pool.h \
asio_http2_handler.cc asio_http2_handler.h \
asio_http2_impl.cc asio_http2_impl.h \
util.cc util.h http2.cc http2.h \
ssl.cc ssl.h
libnghttp2_asio_la_CPPFLAGS= ${BOOST_CPPFLAGS} ${AM_CPPFLAGS}
libnghttp2_asio_la_LDFLAGS = \
${BOOST_LDFLAGS} \
${BOOST_ASIO_LIB} \
${BOOST_THREAD_LIB} \
${BOOST_SYSTEM_LIB} \
@OPENSSL_LIBS@ \
-no-undefined \
-version-info 0:0:0
libnghttp2_asio_la_LIBADD = $(top_builddir)/lib/libnghttp2.la
endif # ENABLE_ASIO_LIB

187
src/asio_connection.h Normal file
View File

@ -0,0 +1,187 @@
/*
* 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:
//
// connection.hpp
// ~~~~~~~~~~~~~~
//
// 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)
//
#ifndef HTTP_SERVER2_CONNECTION_HPP
#define HTTP_SERVER2_CONNECTION_HPP
#include "nghttp2_config.h"
#include <memory>
#include <boost/asio.hpp>
#include <boost/noncopyable.hpp>
#include <boost/array.hpp>
#include <nghttp2/asio_http2.h>
#include "asio_http2_handler.h"
#include "util.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
/// Represents a single connection from a client.
template<typename socket_type>
class connection
: public std::enable_shared_from_this<connection<socket_type>>,
private boost::noncopyable
{
public:
/// Construct a connection with the given io_service.
template<typename... SocketArgs>
explicit connection(request_cb cb, SocketArgs&&... args)
: socket_(std::forward<SocketArgs>(args)...),
request_cb_(std::move(cb)),
writing_(false)
{}
/// Start the first asynchronous operation for the connection.
void start()
{
handler_ = std::make_shared<http2_handler>
(socket_.get_io_service(),
[this]()
{
do_write();
},
request_cb_);
if(handler_->start() != 0) {
return;
}
do_read();
}
socket_type& socket()
{
return socket_;
}
void do_read()
{
auto self = this->shared_from_this();
socket_.async_read_some
(boost::asio::buffer(buffer_),
[this, self](const boost::system::error_code& e,
std::size_t bytes_transferred)
{
if (!e) {
if(handler_->on_read(buffer_, bytes_transferred) != 0) {
return;
}
do_write();
if(!writing_ && handler_->should_stop()) {
return;
}
do_read();
}
// If an error occurs then no new asynchronous operations are
// started. This means that all shared_ptr references to the
// connection object will disappear and the object will be
// destroyed automatically after this handler returns. The
// connection class's destructor closes the socket.
});
}
void do_write()
{
auto self = this->shared_from_this();
if(writing_) {
return;
}
int rv;
std::size_t nwrite;
rv = handler_->on_write(outbuf_, nwrite);
if(rv != 0) {
return;
}
if(nwrite == 0) {
return;
}
writing_ = true;
boost::asio::async_write
(socket_, boost::asio::buffer(outbuf_, nwrite),
[this, self](const boost::system::error_code& e,
std::size_t)
{
if(!e) {
writing_ = false;
do_write();
}
});
// No new asynchronous operations are started. This means that all
// shared_ptr references to the connection object will disappear and
// the object will be destroyed automatically after this handler
// returns. The connection class's destructor closes the socket.
}
private:
socket_type socket_;
request_cb request_cb_;
std::shared_ptr<http2_handler> handler_;
/// Buffer for incoming data.
boost::array<uint8_t, 8192> buffer_;
boost::array<uint8_t, 16394> outbuf_;
bool writing_;
};
} // namespace server
} // namespace asio_http2
} // namespace nghttp2
#endif // HTTP_SERVER2_CONNECTION_HPP

845
src/asio_http2_handler.cc Normal file
View File

@ -0,0 +1,845 @@
/*
* 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.
*/
#include "asio_http2_handler.h"
#include <iostream>
#include "http2.h"
#include "util.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
extern std::shared_ptr<std::string> cached_date;
request::request()
: impl_(util::make_unique<request_impl>())
{}
const std::vector<header>& request::headers() const
{
return impl_->headers();
}
const std::string& request::method() const
{
return impl_->method();
}
const std::string& request::scheme() const
{
return impl_->scheme();
}
const std::string& request::authority() const
{
return impl_->authority();
}
const std::string& request::host() const
{
return impl_->host();
}
const std::string& request::path() const
{
return impl_->path();
}
bool request::push(std::string method, std::string path,
std::vector<header> headers)
{
return impl_->push(std::move(method), std::move(path), std::move(headers));
}
bool request::pushed() const
{
return impl_->pushed();
}
void request::on_data(data_cb cb)
{
return impl_->on_data(std::move(cb));
}
void request::on_end(void_cb cb)
{
return impl_->on_end(std::move(cb));
}
request_impl& request::impl()
{
return *impl_;
}
response::response()
: impl_(util::make_unique<response_impl>())
{}
void response::write_head(unsigned int status_code,
std::vector<header> headers)
{
impl_->write_head(status_code, std::move(headers));
}
void response::end(std::string data)
{
impl_->end(std::move(data));
}
void response::end(read_cb cb)
{
impl_->end(std::move(cb));
}
unsigned int response::status_code() const
{
return impl_->status_code();
}
bool response::started() const
{
return impl_->started();
}
response_impl& response::impl()
{
return *impl_;
}
request_impl::request_impl()
: pushed_(false)
{}
const std::vector<header>& request_impl::headers() const
{
return headers_;
}
const std::string& request_impl::method() const
{
return method_;
}
const std::string& request_impl::scheme() const
{
return scheme_;
}
const std::string& request_impl::authority() const
{
return authority_;
}
const std::string& request_impl::host() const
{
return host_;
}
const std::string& request_impl::path() const
{
return path_;
}
void request_impl::set_header(std::vector<header> headers)
{
headers_ = std::move(headers);
}
void request_impl::add_header(std::string name, std::string value)
{
headers_.push_back(header{std::move(name), std::move(value)});
}
void request_impl::method(std::string arg)
{
method_ = std::move(arg);
}
void request_impl::scheme(std::string arg)
{
scheme_ = std::move(arg);
}
void request_impl::authority(std::string arg)
{
authority_ = std::move(arg);
}
void request_impl::host(std::string arg)
{
host_ = std::move(arg);
}
void request_impl::path(std::string arg)
{
path_ = std::move(arg);
}
bool request_impl::push(std::string method, std::string path,
std::vector<header> headers)
{
if(handler_.expired() || stream_.expired()) {
return false;
}
auto handler = handler_.lock();
auto stream = stream_.lock();
auto rv = handler->push_promise
(*stream, std::move(method), std::move(path), std::move(headers));
return rv == 0;
}
bool request_impl::pushed() const
{
return pushed_;
}
void request_impl::pushed(bool f)
{
pushed_ = f;
}
void request_impl::on_data(data_cb cb)
{
on_data_cb_ = std::move(cb);
}
void request_impl::on_end(void_cb cb)
{
on_end_cb_ = std::move(cb);
}
void request_impl::handler(std::weak_ptr<http2_handler> h)
{
handler_ = std::move(h);
}
void request_impl::stream(std::weak_ptr<http2_stream> s)
{
stream_ = std::move(s);
}
void request_impl::call_on_data(const uint8_t *data, std::size_t len)
{
if(on_data_cb_) {
on_data_cb_(data, len);
}
}
void request_impl::call_on_end()
{
if(on_end_cb_) {
on_end_cb_();
}
}
response_impl::response_impl()
: status_code_(200),
started_(false)
{}
unsigned int response_impl::status_code() const
{
return status_code_;
}
void response_impl::write_head(unsigned int status_code,
std::vector<header> headers)
{
status_code_ = status_code;
headers_ = std::move(headers);
}
void response_impl::end(std::string data)
{
if(started_) {
return;
}
auto strio = std::make_shared<std::pair<std::string, size_t>>
(std::move(data), data.size());
auto read_cb = [strio](uint8_t *buf, size_t len)
{
auto nread = std::min(len, strio->second);
memcpy(buf, strio->first.c_str(), nread);
strio->second -= nread;
if(strio->second == 0) {
return std::make_pair(nread, true);
}
return std::make_pair(nread, false);
};
end(std::move(read_cb));
}
void response_impl::end(read_cb cb)
{
if(started_ || handler_.expired() || stream_.expired()) {
return;
}
read_cb_ = std::move(cb);
started_ = true;
auto handler = handler_.lock();
auto stream = stream_.lock();
if(handler->start_response(*stream) != 0) {
handler->stream_error(stream->get_stream_id(), NGHTTP2_INTERNAL_ERROR);
return;
}
if(!handler->inside_callback()) {
handler->initiate_write();
}
}
bool response_impl::started() const
{
return started_;
}
const std::vector<header>& response_impl::headers() const
{
return headers_;
}
void response_impl::handler(std::weak_ptr<http2_handler> h)
{
handler_ = std::move(h);
}
void response_impl::stream(std::weak_ptr<http2_stream> s)
{
stream_ = std::move(s);
}
std::pair<ssize_t, bool> response_impl::call_read
(uint8_t *data, std::size_t len)
{
if(read_cb_) {
return read_cb_(data, len);
}
return std::make_pair(0, true);
}
http2_stream::http2_stream(int32_t stream_id)
: request_(std::make_shared<request>()),
response_(std::make_shared<response>()),
stream_id_(stream_id)
{}
int32_t http2_stream::get_stream_id() const
{
return stream_id_;
}
const std::shared_ptr<request>& http2_stream::get_request()
{
return request_;
}
const std::shared_ptr<response>& http2_stream::get_response()
{
return response_;
}
namespace {
int stream_error(nghttp2_session *session, int32_t stream_id,
uint32_t error_code)
{
return nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
error_code);
}
} // namespace
namespace {
int on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame,
void *user_data)
{
auto handler = static_cast<http2_handler*>(user_data);
if(frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0;
}
handler->create_stream(frame->hd.stream_id);
return 0;
}
} // namespace
namespace {
int on_header_callback(nghttp2_session *session,
const nghttp2_frame *frame,
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
uint8_t flags,
void *user_data)
{
auto handler = static_cast<http2_handler*>(user_data);
auto stream_id = frame->hd.stream_id;
if(frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0;
}
auto stream = handler->find_stream(stream_id);
if(!stream) {
return 0;
}
if(!nghttp2_check_header_name(name, namelen) ||
!nghttp2_check_header_value(value, valuelen)) {
stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
auto& req = stream->get_request()->impl();
if(name[0] == ':' && !req.headers().empty()) {
stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if(util::streq(":method", name, namelen)) {
if(!req.method().empty()) {
stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
req.method(std::string(value, value + valuelen));
} else if(util::streq(":scheme", name, namelen)) {
if(!req.scheme().empty()) {
stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
req.scheme(std::string(value, value + valuelen));
} else if(util::streq(":authority", name, namelen)) {
if(!req.authority().empty()) {
stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
req.authority(std::string(value, value + valuelen));
} else if(util::streq(":path", name, namelen)) {
if(!req.path().empty()) {
stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
req.path(std::string(value, value + valuelen));
} else {
if(name[0] == ':') {
stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if(util::streq("host", name, namelen)) {
req.host(std::string(value, value + valuelen));
}
req.add_header(std::string(name, name + namelen),
std::string(value, value + valuelen));
}
return 0;
}
} // namespace
namespace {
int on_frame_recv_callback
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
{
auto handler = static_cast<http2_handler*>(user_data);
auto stream = handler->find_stream(frame->hd.stream_id);
switch(frame->hd.type) {
case NGHTTP2_DATA:
if(!stream) {
break;
}
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
stream->get_request()->impl().call_on_end();
}
break;
case NGHTTP2_HEADERS: {
if(!stream || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
break;
}
auto& req = stream->get_request()->impl();
if(req.method().empty() || req.scheme().empty() || req.path().empty() ||
(req.authority().empty() && req.host().empty())) {
stream_error(session, frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
return 0;
}
if(req.host().empty()) {
req.host(req.authority());
}
handler->call_on_request(*stream);
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
stream->get_request()->impl().call_on_end();
}
break;
}
}
return 0;
}
} // namespace
namespace {
int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const uint8_t *data, size_t len,
void *user_data)
{
auto handler = static_cast<http2_handler*>(user_data);
auto stream = handler->find_stream(stream_id);
if(!stream) {
return 0;
}
stream->get_request()->impl().call_on_data(data, len);
return 0;
}
} // namespace
namespace {
int on_stream_close_callback
(nghttp2_session *session, int32_t stream_id, uint32_t error_code,
void *user_data)
{
auto handler = static_cast<http2_handler*>(user_data);
handler->close_stream(stream_id);
return 0;
}
} // namespace
namespace {
int on_frame_send_callback
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
{
auto handler = static_cast<http2_handler*>(user_data);
if(frame->hd.type != NGHTTP2_PUSH_PROMISE) {
return 0;
}
auto stream = handler->find_stream(frame->push_promise.promised_stream_id);
if(!stream) {
return 0;
}
handler->call_on_request(*stream);
return 0;
}
} // namespace
namespace {
int on_frame_not_send_callback
(nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code,
void *user_data)
{
if(frame->hd.type != NGHTTP2_HEADERS) {
return 0;
}
// Issue RST_STREAM so that stream does not hang around.
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
return 0;
}
} // namespace
http2_handler::http2_handler
(boost::asio::io_service& io_service, connection_write writefun, request_cb cb)
: writefun_(writefun),
request_cb_(std::move(cb)),
io_service_(io_service),
session_(nullptr),
buf_(nullptr),
buflen_(0),
inside_callback_(false)
{}
http2_handler::~http2_handler()
{
nghttp2_session_del(session_);
}
int http2_handler::start()
{
int rv;
nghttp2_session_callbacks *callbacks;
rv = nghttp2_session_callbacks_new(&callbacks);
if(rv != 0) {
return -1;
}
auto cb_del = util::defer(callbacks, nghttp2_session_callbacks_del);
nghttp2_session_callbacks_set_on_begin_headers_callback
(callbacks, on_begin_headers_callback);
nghttp2_session_callbacks_set_on_header_callback
(callbacks, on_header_callback);
nghttp2_session_callbacks_set_on_frame_recv_callback
(callbacks, on_frame_recv_callback);
nghttp2_session_callbacks_set_on_data_chunk_recv_callback
(callbacks, on_data_chunk_recv_callback);
nghttp2_session_callbacks_set_on_stream_close_callback
(callbacks, on_stream_close_callback);
nghttp2_session_callbacks_set_on_frame_send_callback
(callbacks, on_frame_send_callback);
nghttp2_session_callbacks_set_on_frame_not_send_callback
(callbacks, on_frame_not_send_callback);
nghttp2_option *option;
rv = nghttp2_option_new(&option);
if(rv != 0) {
return -1;
}
auto opt_del = util::defer(option, nghttp2_option_del);
nghttp2_option_set_recv_client_preface(option, 1);
rv = nghttp2_session_server_new2(&session_, callbacks, this, option);
if(rv != 0) {
return -1;
}
nghttp2_settings_entry ent { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 };
nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1);
return 0;
}
std::shared_ptr<http2_stream> http2_handler::create_stream(int32_t stream_id)
{
auto stream = std::make_shared<http2_stream>(stream_id);
streams_.emplace(stream_id, stream);
auto self = shared_from_this();
auto& req = stream->get_request()->impl();
auto& res = stream->get_response()->impl();
req.handler(self);
req.stream(stream);
res.handler(self);
res.stream(stream);
return stream;
}
void http2_handler::close_stream(int32_t stream_id)
{
streams_.erase(stream_id);
}
std::shared_ptr<http2_stream> http2_handler::find_stream(int32_t stream_id)
{
auto i = streams_.find(stream_id);
if(i == std::end(streams_)) {
return nullptr;
}
return (*i).second;
}
void http2_handler::call_on_request(http2_stream& stream)
{
request_cb_(stream.get_request(), stream.get_response());
}
bool http2_handler::should_stop() const
{
return
!nghttp2_session_want_read(session_) &&
!nghttp2_session_want_write(session_);
}
int http2_handler::start_response(http2_stream& stream)
{
int rv;
auto& res = stream.get_response()->impl();
auto& headers = res.headers();
auto nva = std::vector<nghttp2_nv>();
nva.reserve(2 + headers.size());
auto status = std::to_string(res.status_code());
auto date = cached_date;
nva.push_back(nghttp2::http2::make_nv_ls(":status", status));
nva.push_back(nghttp2::http2::make_nv_ls("date", *date));
for(auto& hd : headers) {
nva.push_back(nghttp2::http2::make_nv(hd.name, hd.value));
}
nghttp2_data_provider prd;
prd.source.ptr = &stream;
prd.read_callback =
[](nghttp2_session *session, int32_t stream_id,
uint8_t *buf, size_t length, uint32_t *data_flags,
nghttp2_data_source *source, void *user_data) -> ssize_t
{
auto& stream = *static_cast<http2_stream*>(source->ptr);
auto rv = stream.get_response()->impl().call_read(buf, length);
if(rv.first < 0) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if(rv.second) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
} else if(rv.first == 0) {
return NGHTTP2_ERR_DEFERRED;
}
return rv.first;
};
rv = nghttp2_submit_response(session_, stream.get_stream_id(),
nva.data(), nva.size(), &prd);
if(rv != 0) {
return -1;
}
return 0;
}
void http2_handler::enter_callback()
{
assert(!inside_callback_);
inside_callback_ = true;
}
void http2_handler::leave_callback()
{
assert(inside_callback_);
inside_callback_ = false;
}
bool http2_handler::inside_callback() const
{
return inside_callback_;
}
void http2_handler::stream_error(int32_t stream_id, uint32_t error_code)
{
::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code);
}
void http2_handler::initiate_write()
{
writefun_();
}
int http2_handler::push_promise(http2_stream& stream, std::string method,
std::string path,
std::vector<header> headers)
{
int rv;
auto& req = stream.get_request()->impl();
auto nva = std::vector<nghttp2_nv>();
nva.reserve(5 + headers.size());
nva.push_back(nghttp2::http2::make_nv_ls(":method", method));
nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.scheme()));
if(!req.authority().empty()) {
nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.authority()));
}
nva.push_back(nghttp2::http2::make_nv_ls(":path", path));
if(!req.host().empty()) {
nva.push_back(nghttp2::http2::make_nv_ls("host", req.host()));
}
for(auto& hd : headers) {
nva.push_back(nghttp2::http2::make_nv(hd.name, hd.value));
}
rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE,
stream.get_stream_id(),
nva.data(), nva.size(), nullptr);
if(rv < 0) {
return -1;
}
auto promised_stream = create_stream(rv);
auto& promised_req = promised_stream->get_request()->impl();
promised_req.pushed(true);
promised_req.method(std::move(method));
promised_req.scheme(req.scheme());
promised_req.authority(req.authority());
promised_req.path(std::move(path));
promised_req.host(req.host());
promised_req.set_header(std::move(headers));
if(!req.host().empty()) {
promised_req.add_header("host", req.host());
}
return 0;
}
callback_guard::callback_guard(http2_handler& h)
: handler(h)
{
handler.enter_callback();
}
callback_guard::~callback_guard()
{
handler.leave_callback();
}
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

241
src/asio_http2_handler.h Normal file
View File

@ -0,0 +1,241 @@
/*
* 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.
*/
#ifndef HTTP2_HANDLER_H
#define HTTP2_HANDLER_H
#include "nghttp2_config.h"
#include <map>
#include <vector>
#include <functional>
#include <string>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <nghttp2/nghttp2.h>
#include <nghttp2/asio_http2.h>
namespace nghttp2 {
namespace asio_http2 {
namespace server {
class http2_handler;
class http2_stream;
class request_impl {
public:
request_impl();
const std::vector<header>& headers() const;
const std::string& method() const;
const std::string& scheme() const;
const std::string& authority() const;
const std::string& host() const;
const std::string& path() const;
bool push(std::string method, std::string path,
std::vector<header> headers = {});
bool pushed() const;
void on_data(data_cb cb);
void on_end(void_cb cb);
void set_header(std::vector<header> headers);
void add_header(std::string name, std::string value);
void method(std::string method);
void scheme(std::string scheme);
void authority(std::string authority);
void host(std::string host);
void path(std::string path);
void pushed(bool f);
void handler(std::weak_ptr<http2_handler> h);
void stream(std::weak_ptr<http2_stream> s);
void call_on_data(const uint8_t *data, std::size_t len);
void call_on_end();
private:
std::vector<header> headers_;
std::string method_;
std::string scheme_;
std::string authority_;
std::string host_;
std::string path_;
data_cb on_data_cb_;
void_cb on_end_cb_;
std::weak_ptr<http2_handler> handler_;
std::weak_ptr<http2_stream> stream_;
bool pushed_;
};
class response_impl {
public:
response_impl();
void write_head(unsigned int status_code, std::vector<header> headers = {});
void end(std::string data = "");
void end(read_cb cb);
unsigned int status_code() const;
const std::vector<header>& headers() const;
bool started() const;
void handler(std::weak_ptr<http2_handler> h);
void stream(std::weak_ptr<http2_stream> s);
read_cb::result_type call_read(uint8_t *data, std::size_t len);
private:
std::vector<header> headers_;
read_cb read_cb_;
std::weak_ptr<http2_handler> handler_;
std::weak_ptr<http2_stream> stream_;
unsigned int status_code_;
bool started_;
};
class http2_stream {
public:
http2_stream(int32_t stream_id);
int32_t get_stream_id() const;
const std::shared_ptr<request>& get_request();
const std::shared_ptr<response>& get_response();
private:
std::shared_ptr<request> request_;
std::shared_ptr<response> response_;
int32_t stream_id_;
};
struct callback_guard {
callback_guard(http2_handler& h);
~callback_guard();
http2_handler& handler;
};
typedef std::function<void(void)> connection_write;
class http2_handler : public std::enable_shared_from_this<http2_handler> {
public:
http2_handler(boost::asio::io_service& io_service,
connection_write writefun,
request_cb cb);
~http2_handler();
int start();
std::shared_ptr<http2_stream> create_stream(int32_t stream_id);
void close_stream(int32_t stream_id);
std::shared_ptr<http2_stream> find_stream(int32_t stream_id);
void call_on_request(http2_stream& stream);
bool should_stop() const;
int start_response(http2_stream& stream);
void stream_error(int32_t stream_id, uint32_t error_code);
void initiate_write();
void enter_callback();
void leave_callback();
bool inside_callback() const;
int push_promise(http2_stream& stream, std::string method,
std::string path,
std::vector<header> headers);
template<size_t N>
int on_read(const boost::array<uint8_t, N>& buffer, std::size_t len)
{
callback_guard cg(*this);
int rv;
rv = nghttp2_session_mem_recv(session_, buffer.data(), len);
if(rv < 0) {
return -1;
}
return 0;
}
template<size_t N>
int on_write(boost::array<uint8_t, N>& buffer, std::size_t& len)
{
callback_guard cg(*this);
len = 0;
if(buf_) {
std::copy(buf_, buf_ + buflen_, std::begin(buffer));
len += buflen_;
buf_ = nullptr;
buflen_ = 0;
}
for(;;) {
const uint8_t *data;
auto nread = nghttp2_session_mem_send(session_, &data);
if(nread < 0) {
return -1;
}
if(nread == 0) {
break;
}
if(len + nread > buffer.size()) {
buf_ = data;
buflen_ = nread;
break;
}
std::copy(data, data + nread, std::begin(buffer) + len);
len += nread;
}
return 0;
}
private:
std::map<int32_t, std::shared_ptr<http2_stream>> streams_;
connection_write writefun_;
request_cb request_cb_;
boost::asio::io_service& io_service_;
nghttp2_session *session_;
const uint8_t *buf_;
std::size_t buflen_;
bool inside_callback_;
};
} // namespace server
} // namespace asio_http2
} // namespace nghttp
#endif // HTTP2_HANDLER_H

181
src/asio_http2_impl.cc Normal file
View File

@ -0,0 +1,181 @@
/*
* 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.
*/
#include "asio_http2_impl.h"
#include <boost/asio/ssl.hpp>
#include <openssl/ssl.h>
#include <nghttp2/nghttp2.h>
#include "asio_server.h"
#include "util.h"
#include "ssl.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
http2::http2()
: impl_(util::make_unique<http2_impl>())
{}
http2::~http2()
{}
void http2::listen(const std::string& address, uint16_t port, request_cb cb)
{
impl_->listen(address, port, std::move(cb));
}
void http2::num_threads(size_t num_threads)
{
impl_->num_threads(num_threads);
}
void http2::tls(std::string private_key_file,
std::string certificate_file)
{
impl_->tls(std::move(private_key_file), std::move(certificate_file));
}
http2_impl::http2_impl()
: num_threads_(1)
{}
namespace {
std::array<unsigned char, NGHTTP2_PROTO_VERSION_ID_LEN + 1>&
get_alpn_token()
{
static std::array<unsigned char, NGHTTP2_PROTO_VERSION_ID_LEN + 1> token;
token[0] = NGHTTP2_PROTO_VERSION_ID_LEN;
std::copy(NGHTTP2_PROTO_VERSION_ID,
NGHTTP2_PROTO_VERSION_ID + NGHTTP2_PROTO_VERSION_ID_LEN,
std::begin(token) + 1);
return token;
}
} // namespace
void http2_impl::listen(const std::string& address, uint16_t port,
request_cb cb)
{
std::unique_ptr<boost::asio::ssl::context> ssl_ctx;
if(!private_key_file_.empty() && !certificate_file_.empty()) {
ssl_ctx = util::make_unique<boost::asio::ssl::context>
(boost::asio::ssl::context::sslv23);
ssl_ctx->use_private_key_file(private_key_file_,
boost::asio::ssl::context::pem);
ssl_ctx->use_certificate_chain_file(certificate_file_);
auto ctx = ssl_ctx->native_handle();
SSL_CTX_set_options(ctx,
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
SSL_OP_SINGLE_ECDH_USE |
SSL_OP_NO_TICKET |
SSL_OP_CIPHER_SERVER_PREFERENCE);
SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
SSL_CTX_set_cipher_list(ctx, ssl::DEFAULT_CIPHER_LIST);
auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if(ecdh) {
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
EC_KEY_free(ecdh);
}
SSL_CTX_set_next_protos_advertised_cb
(ctx,
[](SSL *s, const unsigned char **data, unsigned int *len, void *arg) {
auto& token = get_alpn_token();
*data = token.data();
*len = token.size();
return SSL_TLSEXT_ERR_OK;
}, nullptr);
}
server(address, port, num_threads_, std::move(cb), std::move(ssl_ctx)).run();
}
void http2_impl::num_threads(size_t num_threads)
{
num_threads_ = num_threads;
}
void http2_impl::tls(std::string private_key_file,
std::string certificate_file)
{
private_key_file_ = std::move(private_key_file);
certificate_file_ = std::move(certificate_file);
}
template<typename T, typename F>
std::shared_ptr<util::Defer<T, F>> defer_shared(T&& t, F f)
{
return std::make_shared<util::Defer<T, F>>(std::forward<T>(t),
std::forward<F>(f));
}
read_cb file_reader(const std::string& path)
{
auto fd = open(path.c_str(), O_RDONLY);
if(fd == -1) {
return read_cb();
}
auto d = defer_shared(static_cast<int>(fd), close);
return [fd, d](uint8_t *buf, size_t len) -> read_cb::result_type
{
int rv;
while((rv = read(fd, buf, len)) == -1 && errno == EINTR);
if(rv == -1) {
return std::make_pair(-1, false);
}
if(rv == 0) {
return std::make_pair(rv, true);
}
return std::make_pair(rv, false);
};
}
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

60
src/asio_http2_impl.h Normal file
View File

@ -0,0 +1,60 @@
/*
* 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.
*/
#ifndef ASIO_HTTP2_IMPL_H
#define ASIO_HTTP2_IMPL_H
#include "nghttp2_config.h"
#include <nghttp2/asio_http2.h>
namespace nghttp2 {
namespace asio_http2 {
namespace server {
class server;
class http2_impl {
public:
http2_impl();
void listen(const std::string& address, uint16_t port,
request_cb cb);
void num_threads(size_t num_threads);
void tls(std::string private_key_file, std::string certificate_file);
private:
std::string private_key_file_;
std::string certificate_file_;
std::unique_ptr<server> server_;
std::size_t num_threads_;
};
} // namespace server
} // namespace asio_http2
} // namespace nghttp2
#endif // ASIO_HTTP2_IMPL_H

109
src/asio_io_service_pool.cc Normal file
View File

@ -0,0 +1,109 @@
/*
* 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:
//
// io_service_pool.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 "asio_server.h"
#include <stdexcept>
#include <future>
#include <boost/thread/thread.hpp>
#include <boost/bind.hpp>
namespace nghttp2 {
namespace asio_http2 {
namespace server {
io_service_pool::io_service_pool(std::size_t pool_size)
: next_io_service_(0)
{
if (pool_size == 0) {
throw std::runtime_error("io_service_pool size is 0");
}
// Give all the io_services work to do so that their run() functions will not
// exit until they are explicitly stopped.
for (std::size_t i = 0; i < pool_size; ++i)
{
auto io_service = std::make_shared<boost::asio::io_service>();
auto work = std::make_shared<boost::asio::io_service::work>(*io_service);
io_services_.push_back(io_service);
work_.push_back(work);
}
}
void io_service_pool::run()
{
// Create a pool of threads to run all of the io_services.
auto futs = std::vector<std::future<std::size_t>>();
for (std::size_t i = 0; i < io_services_.size(); ++i) {
futs.push_back
(std::async(std::launch::async,
(size_t(boost::asio::io_service::*)(void))
&boost::asio::io_service::run,
io_services_[i]));
}
// Wait for all threads in the pool to exit.
for (auto& fut : futs) {
fut.get();
}
}
void io_service_pool::stop()
{
// Explicitly stop all io_services.
for (auto& iosv : io_services_) {
iosv->stop();
}
}
boost::asio::io_service& io_service_pool::get_io_service()
{
// Use a round-robin scheme to choose the next io_service to use.
auto& io_service = *io_services_[next_io_service_];
++next_io_service_;
if (next_io_service_ == io_services_.size()) {
next_io_service_ = 0;
}
return io_service;
}
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

View File

@ -0,0 +1,92 @@
/*
* 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:
//
// io_service_pool.hpp
// ~~~~~~~~~~~~~~~~~~~
//
// 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)
//
#ifndef HTTP_SERVER2_IO_SERVICE_POOL_HPP
#define HTTP_SERVER2_IO_SERVICE_POOL_HPP
#include "nghttp2_config.h"
#include <vector>
#include <memory>
#include <boost/asio.hpp>
#include <boost/noncopyable.hpp>
#include <nghttp2/asio_http2.h>
namespace nghttp2 {
namespace asio_http2 {
namespace server {
/// A pool of io_service objects.
class io_service_pool
: private boost::noncopyable
{
public:
/// Construct the io_service pool.
explicit io_service_pool(std::size_t pool_size);
/// Run all io_service objects in the pool.
void run();
/// Stop all io_service objects in the pool.
void stop();
/// Get an io_service to use.
boost::asio::io_service& get_io_service();
private:
typedef std::shared_ptr<boost::asio::io_service> io_service_ptr;
typedef std::shared_ptr<boost::asio::io_service::work> work_ptr;
/// The pool of io_services.
std::vector<io_service_ptr> io_services_;
/// The work that keeps the io_services running.
std::vector<work_ptr> work_;
/// The next io_service to use for a connection.
std::size_t next_io_service_;
};
} // namespace server
} // namespace asio_http2
} // namespace nghttp2
#endif // HTTP_SERVER2_IO_SERVICE_POOL_HPP

162
src/asio_server.cc Normal file
View File

@ -0,0 +1,162 @@
/*
* 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:
//
// server.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 "asio_server.h"
#include <boost/date_time/posix_time/posix_time.hpp>
namespace nghttp2 {
namespace asio_http2 {
namespace server {
server::server(const std::string& address, uint16_t port,
std::size_t io_service_pool_size,
request_cb cb,
std::unique_ptr<boost::asio::ssl::context> ssl_ctx)
: io_service_pool_(io_service_pool_size),
signals_(io_service_pool_.get_io_service()),
tick_timer_(io_service_pool_.get_io_service(),
boost::posix_time::seconds(1)),
acceptor_(io_service_pool_.get_io_service()),
ssl_ctx_(std::move(ssl_ctx)),
request_cb_(std::move(cb))
{
// Register to handle the signals that indicate when the server should exit.
// It is safe to register for the same signal multiple times in a program,
// provided all registration for the specified signal is made through Asio.
signals_.add(SIGINT);
signals_.add(SIGTERM);
#if defined(SIGQUIT)
signals_.add(SIGQUIT);
#endif // defined(SIGQUIT)
signals_.async_wait([this](const boost::system::error_code& error,
int signal_number)
{
io_service_pool_.stop();
});
// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
boost::asio::ip::tcp::resolver resolver(acceptor_.get_io_service());
boost::asio::ip::tcp::resolver::query query(address, std::to_string(port));
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
acceptor_.open(endpoint.protocol());
acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
acceptor_.bind(endpoint);
acceptor_.listen();
start_accept();
start_timer();
}
void server::run()
{
io_service_pool_.run();
}
std::shared_ptr<std::string> cached_date;
namespace {
void update_date()
{
cached_date = std::make_shared<std::string>(util::http_date(time(nullptr)));
}
} // namespace
void server::start_timer()
{
update_date();
tick_timer_.async_wait
([this](const boost::system::error_code& e)
{
tick_timer_.expires_at(tick_timer_.expires_at() +
boost::posix_time::seconds(1));
start_timer();
});
}
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
void server::start_accept()
{
if(ssl_ctx_) {
auto new_connection =
std::make_shared<connection<ssl_socket>>
(request_cb_, io_service_pool_.get_io_service(), *ssl_ctx_);
acceptor_.async_accept
(new_connection->socket().lowest_layer(),
[this, new_connection](const boost::system::error_code& e)
{
if(!e) {
new_connection->socket().lowest_layer().set_option
(boost::asio::ip::tcp::no_delay(true));
new_connection->socket().async_handshake
(boost::asio::ssl::stream_base::server,
[new_connection](const boost::system::error_code& e)
{
if(!e) {
new_connection->start();
}
});
}
start_accept();
});
} else {
auto new_connection =
std::make_shared<connection<boost::asio::ip::tcp::socket>>
(request_cb_, io_service_pool_.get_io_service());
acceptor_.async_accept
(new_connection->socket(),
[this, new_connection](const boost::system::error_code& e)
{
if (!e) {
new_connection->socket().set_option
(boost::asio::ip::tcp::no_delay(true));
new_connection->start();
}
start_accept();
});
}
}
} // namespace server
} // namespace asio_http2
} // namespace nghttp2

103
src/asio_server.h Normal file
View File

@ -0,0 +1,103 @@
/*
* 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:
//
// server.hpp
// ~~~~~~~~~~
//
// 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)
//
#ifndef HTTP_SERVER2_SERVER_HPP
#define HTTP_SERVER2_SERVER_HPP
#include "nghttp2_config.h"
#include <string>
#include <vector>
#include <memory>
#include <boost/noncopyable.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <nghttp2/asio_http2.h>
#include "asio_connection.h"
#include "asio_io_service_pool.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
/// The top-level class of the HTTP server.
class server
: private boost::noncopyable
{
public:
/// Construct the server to listen on the specified TCP address and port, and
/// serve up files from the given directory.
explicit server(const std::string& address, uint16_t port,
std::size_t io_service_pool_size,
request_cb cb,
std::unique_ptr<boost::asio::ssl::context> ssl_ctx);
/// Run the server's io_service loop.
void run();
private:
/// Initiate an asynchronous accept operation.
void start_accept();
void start_timer();
/// The pool of io_service objects used to perform asynchronous operations.
io_service_pool io_service_pool_;
/// The signal_set is used to register for process termination notifications.
boost::asio::signal_set signals_;
boost::asio::deadline_timer tick_timer_;
/// Acceptor used to listen for incoming connections.
boost::asio::ip::tcp::acceptor acceptor_;
std::unique_ptr<boost::asio::ssl::context> ssl_ctx_;
request_cb request_cb_;
};
} // namespace server
} // namespace asio_http2
} // namespace nghttp2
#endif // HTTP_SERVER2_SERVER_HPP

23
src/includes/Makefile.am Normal file
View File

@ -0,0 +1,23 @@
# 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.
nobase_include_HEADERS = nghttp2/asio_http2.h

View File

@ -0,0 +1,177 @@
/*
* 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.
*/
#ifndef ASIO_HTTP2_H
#define ASIO_HTTP2_H
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include <functional>
namespace nghttp2 {
namespace asio_http2 {
struct header {
std::string name;
std::string value;
};
namespace server {
class request_impl;
class response_impl;
typedef std::function<void(const uint8_t*, std::size_t)> data_cb;
typedef std::function<void(void)> void_cb;
// Callback function to generate response body. The implementation of
// this callback must fill at most |len| bytes data to |buf|. The
// return value is pair of written bytes and bool value indicating
// that this is the end of the body. If the end of the body was
// reached, return true. If there is error and application wants to
// terminate stream, return std::make_pair(-1, false). Currently,
// returning std::make_pair(0, false) is reserved for future use.
typedef std::function<std::pair<ssize_t, bool>
(uint8_t *buf, std::size_t len)> read_cb;
class request {
public:
// Application must not call this directly.
request();
// Returns request headers. The pusedo headers, which start with
// colon (;), are exluced from this list.
const std::vector<header>& headers() const;
// Returns method (e.g., GET).
const std::string& method() const;
// Returns scheme (e.g., https).
const std::string& scheme() const;
// Returns authority (e.g., example.org). This could be empty
// string. In this case, check host().
const std::string& authority() const;
// Returns host (e.g., example.org). If host header field is not
// present, this value is copied from authority().
const std::string& host() const;
// Returns path (e.g., /index.html).
const std::string& path() const;
// Sets callback when chunk of request body is received.
void on_data(data_cb cb);
// Sets callback when request was completed.
void on_end(void_cb cb);
// Pushes resource denoted by |path| using |method|. The additional
// headers can be given in |headers|. request_cb will be called for
// pushed resource later on. This function returns true if it
// succeeds, or false.
bool push(std::string method, std::string path,
std::vector<header> headers = {});
// Returns true if this is pushed request.
bool pushed() const;
// Application must not call this directly.
request_impl& impl();
private:
std::unique_ptr<request_impl> impl_;
};
class response {
public:
// Application must not call this directly.
response();
// Write response header using |status_code| (e.g., 200) and
// additional headers in |headers|.
void write_head(unsigned int status_code, std::vector<header> headers = {});
// Sends |data| as request body. No further call of end() is
// allowed.
void end(std::string data = "");
// Sets callback |cb| as a generator of the response body. No
// further call of end() is allowed.
void end(read_cb cb);
// Resumes deferred response. Not implemented yet.
void resume();
// Returns status code.
unsigned int status_code() const;
// Returns true if response has been started.
bool started() const;
// Application must not call this directly.
response_impl& impl();
private:
std::unique_ptr<response_impl> impl_;
};
typedef std::function<void(std::shared_ptr<request>,
std::shared_ptr<response>)> request_cb;
class http2_impl;
class http2 {
public:
http2();
~http2();
// Starts listening connection on given address and port. The
// incoming requests are handled by given callback |cb|.
void listen(const std::string& address, uint16_t port,
request_cb cb);
// Sets number of native threads.
void num_threads(size_t num_threads);
// Sets TLS private key file and certificate file. Both files must
// be in PEM format.
void tls(std::string private_key_file, std::string certificate_file);
private:
std::unique_ptr<http2_impl> impl_;
};
// Convenient function to create function to read file denoted by
// |path|. This can be passed to response::end().
read_cb file_reader(const std::string& path);
} // namespace server
} // namespace asio_http2
} // namespace nghttp2
#endif // ASIO_HTTP2_H

33
src/libnghttp2_asio.pc.in Normal file
View File

@ -0,0 +1,33 @@
# 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.
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libnghttp2_asio
Description: HTTP/2 C++ library
URL: https://github.com/tatsuhiro-t/nghttp2
Version: @VERSION@
Libs: -L${libdir} -lnghttp2_asio
Cflags: -I${includedir}