summaryrefslogtreecommitdiffstats
path: root/ext/yahttp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:34:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:34:30 +0000
commit4fc2f55f761d71aae1f145d5aa94ba929cc39676 (patch)
tree5c1e1db3b46dd4edbe11f612d93cb94b96891ce3 /ext/yahttp
parentInitial commit. (diff)
downloaddnsdist-4fc2f55f761d71aae1f145d5aa94ba929cc39676.tar.xz
dnsdist-4fc2f55f761d71aae1f145d5aa94ba929cc39676.zip
Adding upstream version 1.7.3.upstream/1.7.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ext/yahttp')
-rw-r--r--ext/yahttp/LICENSE21
-rw-r--r--ext/yahttp/Makefile.am3
-rw-r--r--ext/yahttp/Makefile.in740
-rw-r--r--ext/yahttp/README.md69
-rw-r--r--ext/yahttp/yahttp/Makefile.am13
-rw-r--r--ext/yahttp/yahttp/Makefile.in748
-rw-r--r--ext/yahttp/yahttp/cookie.hpp144
-rw-r--r--ext/yahttp/yahttp/exception.hpp24
-rw-r--r--ext/yahttp/yahttp/reqresp.cpp331
-rw-r--r--ext/yahttp/yahttp/reqresp.hpp351
-rw-r--r--ext/yahttp/yahttp/router.cpp160
-rw-r--r--ext/yahttp/yahttp/router.hpp72
-rw-r--r--ext/yahttp/yahttp/url.hpp200
-rw-r--r--ext/yahttp/yahttp/utility.hpp462
-rw-r--r--ext/yahttp/yahttp/yahttp-config.h1
-rw-r--r--ext/yahttp/yahttp/yahttp.hpp37
16 files changed, 3376 insertions, 0 deletions
diff --git a/ext/yahttp/LICENSE b/ext/yahttp/LICENSE
new file mode 100644
index 0000000..08f6f64
--- /dev/null
+++ b/ext/yahttp/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Aki Tuomi
+
+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.
diff --git a/ext/yahttp/Makefile.am b/ext/yahttp/Makefile.am
new file mode 100644
index 0000000..aaaeee0
--- /dev/null
+++ b/ext/yahttp/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = yahttp
+
+EXTRA_DIST = LICENSE README.md
diff --git a/ext/yahttp/Makefile.in b/ext/yahttp/Makefile.in
new file mode 100644
index 0000000..04def69
--- /dev/null
+++ b/ext/yahttp/Makefile.in
@@ -0,0 +1,740 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = ext/yahttp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ac_pthread_set_name.m4 \
+ $(top_srcdir)/m4/ax_arg_default_enable_disable.m4 \
+ $(top_srcdir)/m4/ax_check_sign.m4 \
+ $(top_srcdir)/m4/ax_compile_check_sizeof.m4 \
+ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4/ax_cxx_compile_stdcxx_17.m4 \
+ $(top_srcdir)/m4/ax_python_module.m4 $(top_srcdir)/m4/boost.m4 \
+ $(top_srcdir)/m4/dnsdist_enable_dnscrypt.m4 \
+ $(top_srcdir)/m4/dnsdist_enable_doh.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/pdns_check_cdb.m4 \
+ $(top_srcdir)/m4/pdns_check_clock_gettime.m4 \
+ $(top_srcdir)/m4/pdns_check_dnstap.m4 \
+ $(top_srcdir)/m4/pdns_check_libcrypto.m4 \
+ $(top_srcdir)/m4/pdns_check_libedit.m4 \
+ $(top_srcdir)/m4/pdns_check_libh2o_evloop.m4 \
+ $(top_srcdir)/m4/pdns_check_lmdb.m4 \
+ $(top_srcdir)/m4/pdns_check_lua_hpp.m4 \
+ $(top_srcdir)/m4/pdns_check_network_libs.m4 \
+ $(top_srcdir)/m4/pdns_check_os.m4 \
+ $(top_srcdir)/m4/pdns_check_pthread_np.m4 \
+ $(top_srcdir)/m4/pdns_check_python_venv.m4 \
+ $(top_srcdir)/m4/pdns_check_ragel.m4 \
+ $(top_srcdir)/m4/pdns_check_secure_memset.m4 \
+ $(top_srcdir)/m4/pdns_check_time_t.m4 \
+ $(top_srcdir)/m4/pdns_d_fortify_source.m4 \
+ $(top_srcdir)/m4/pdns_enable_sanitizers.m4 \
+ $(top_srcdir)/m4/pdns_enable_tls.m4 \
+ $(top_srcdir)/m4/pdns_enable_unit_tests.m4 \
+ $(top_srcdir)/m4/pdns_param_ssp_buffer_size.m4 \
+ $(top_srcdir)/m4/pdns_pie.m4 $(top_srcdir)/m4/pdns_relro.m4 \
+ $(top_srcdir)/m4/pdns_stack_protector.m4 \
+ $(top_srcdir)/m4/pdns_with_ebpf.m4 \
+ $(top_srcdir)/m4/pdns_with_gnutls.m4 \
+ $(top_srcdir)/m4/pdns_with_libcap.m4 \
+ $(top_srcdir)/m4/pdns_with_libsodium.m4 \
+ $(top_srcdir)/m4/pdns_with_libssl.m4 \
+ $(top_srcdir)/m4/pdns_with_lua.m4 \
+ $(top_srcdir)/m4/pdns_with_net_snmp.m4 \
+ $(top_srcdir)/m4/pdns_with_nghttp2.m4 \
+ $(top_srcdir)/m4/pdns_with_re2.m4 \
+ $(top_srcdir)/m4/pdns_with_service_user.m4 \
+ $(top_srcdir)/m4/systemd.m4 $(top_srcdir)/m4/warnings.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CPPFLAGS = @AM_CPPFLAGS@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_CPPFLAGS = @BOOST_CPPFLAGS@
+BOOST_LDPATH = @BOOST_LDPATH@
+BOOST_ROOT = @BOOST_ROOT@
+BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS = @BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS@
+BOOST_UNIT_TEST_FRAMEWORK_LDPATH = @BOOST_UNIT_TEST_FRAMEWORK_LDPATH@
+BOOST_UNIT_TEST_FRAMEWORK_LIBS = @BOOST_UNIT_TEST_FRAMEWORK_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CDB_CFLAGS = @CDB_CFLAGS@
+CDB_LIBS = @CDB_LIBS@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+DYNLINKFLAGS = @DYNLINKFLAGS@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FSTRM_CFLAGS = @FSTRM_CFLAGS@
+FSTRM_LIBS = @FSTRM_LIBS@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+HAVE_CXX17 = @HAVE_CXX17@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+IPCRYPT_CFLAGS = @IPCRYPT_CFLAGS@
+IPCRYPT_LIBS = @IPCRYPT_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCAP_CFLAGS = @LIBCAP_CFLAGS@
+LIBCAP_LIBS = @LIBCAP_LIBS@
+LIBCRYPTO_INCLUDES = @LIBCRYPTO_INCLUDES@
+LIBCRYPTO_LDFLAGS = @LIBCRYPTO_LDFLAGS@
+LIBCRYPTO_LIBS = @LIBCRYPTO_LIBS@
+LIBEDIT_CFLAGS = @LIBEDIT_CFLAGS@
+LIBEDIT_LIBS = @LIBEDIT_LIBS@
+LIBH2OEVLOOP_CFLAGS = @LIBH2OEVLOOP_CFLAGS@
+LIBH2OEVLOOP_LIBS = @LIBH2OEVLOOP_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSODIUM_CFLAGS = @LIBSODIUM_CFLAGS@
+LIBSODIUM_LIBS = @LIBSODIUM_LIBS@
+LIBSSL_CFLAGS = @LIBSSL_CFLAGS@
+LIBSSL_LIBS = @LIBSSL_LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LMDB_CFLAGS = @LMDB_CFLAGS@
+LMDB_LIBS = @LMDB_LIBS@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LUA_CFLAGS = @LUA_CFLAGS@
+LUA_LIBS = @LUA_LIBS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NET_SNMP_CFLAGS = @NET_SNMP_CFLAGS@
+NET_SNMP_LIBS = @NET_SNMP_LIBS@
+NGHTTP2_CFLAGS = @NGHTTP2_CFLAGS@
+NGHTTP2_LIBS = @NGHTTP2_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGEVERSION = @PACKAGEVERSION@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROGRAM_LDFLAGS = @PROGRAM_LDFLAGS@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RAGEL = @RAGEL@
+RANLIB = @RANLIB@
+RE2_CFLAGS = @RE2_CFLAGS@
+RE2_LIBS = @RE2_LIBS@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RT_LIBS = @RT_LIBS@
+SANITIZER_FLAGS = @SANITIZER_FLAGS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEMCTL = @SYSTEMCTL@
+SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@
+SYSTEMD_DIR = @SYSTEMD_DIR@
+SYSTEMD_LIBS = @SYSTEMD_LIBS@
+SYSTEMD_MODULES_LOAD = @SYSTEMD_MODULES_LOAD@
+THREADFLAGS = @THREADFLAGS@
+VERSION = @VERSION@
+WARN_CFLAGS = @WARN_CFLAGS@
+YAHTTP_CFLAGS = @YAHTTP_CFLAGS@
+YAHTTP_LIBS = @YAHTTP_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+service_group = @service_group@
+service_user = @service_user@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+systemd = @systemd@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = yahttp
+EXTRA_DIST = LICENSE README.md
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign ext/yahttp/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign ext/yahttp/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+ check-am clean clean-generic clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+ ps ps-am tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/ext/yahttp/README.md b/ext/yahttp/README.md
new file mode 100644
index 0000000..86794b5
--- /dev/null
+++ b/ext/yahttp/README.md
@@ -0,0 +1,69 @@
+Yet Another HTTP Library
+========================
+
+YaHTTP aims to be a pure http request/response parser that has no IO ties. It is intended to be used on small-footprint applications and other utilities that want to use HTTP over something else than network IO.
+
+[![Build Status](https://travis-ci.org/cmouse/yahttp.svg?branch=master)](https://travis-ci.org/cmouse/yahttp)
+[![Coverity Scan Build Status](https://scan.coverity.com/projects/2161/badge.svg)](https://scan.coverity.com/projects/2161)
+[![Coverage Status](https://coveralls.io/repos/github/cmouse/yahttp/badge.svg)](https://coveralls.io/github/cmouse/yahttp)
+
+WARNINGS
+--------
+If you are upgrading from version before May 02, 2014 - *PLEASE BE SURE TO CHECK THAT EVERYTHING WORKS AS EXPECTED*. There has been complete overhaul of the library and many things have changed.
+
+NOTES
+-----
+Do not use resp = req, or resp(req) to build the response object, despite it being supported. This will cause request headers to get duplicated. Also, you *must* set response#version to request#version if you intend to support older than HTTP/1.1 clients. Set response#status to at least 200, it won't be done for you. No Server or Product token is sent either, you can add those if you want.
+
+If you do not want to send chunked responses, set content-length header. Setting this header will always disable chunked responses. This will also happen if you downgrade your responses to version 10 or 9.
+
+Integration guide
+-----------------
+
+Here are some instructions on how to integrate YaHTTP into your project.
+
+With automake and libtool
+-------------------------
+
+If you don't need router or any of the C++11 features, you can just create empty yahttp-config.h, or symlink it to your project's config.h for the yahttp.hpp to include. Then just put this stuff into it's own folder and create Makefile.am with following contents (you can change the compilation flags):
+
+```
+noinst_LTLIBRARIES=libyahttp.la
+libyahttp_la_CXXFLAGS=$(RELRO_CFLAGS) $(PIE_CFLAGS) -D__STRICT_ANSI__
+libyahttp_la_SOURCES=cookie.hpp exception.hpp reqresp.cpp reqresp.hpp router.cpp router.hpp url.hpp utility.hpp yahttp.hpp
+```
+
+You can define RELRO and PIE to match your project.
+
+To compile your project use -Lpath/to/yahttp -lyahttp
+
+If you need router, additionally check for boost or C++11 and replace yahttp-config.h to config.h in yahttp.hpp or add relevant options to your compiler CXXFLAGS. See below for the flags.
+
+Without automake
+----------------
+
+Create simple Makefile with contents for C++11:
+
+```
+OBJECTS=reqresp.o router.o
+CXX=gcc
+CXXFLAGS=-W -Wall -DHAVE_CXX11 -std=c++11
+```
+
+Or create simple Makefile with contents for boost:
+
+```
+OBJECTS=reqresp.o router.o
+CXX=gcc
+CXXFLAGS=-W -Wall -DHAVE_BOOST
+```
+
+Or if you don't need either one, just:
+
+```
+OBJECTS=reqresp.o
+CXX=gcc
+CXXFLAGS=-W -Wall
+```
+
+YaHTTP include files can be placed where the rest of your includes are. Then just add your own code there and it should work just fine.
diff --git a/ext/yahttp/yahttp/Makefile.am b/ext/yahttp/yahttp/Makefile.am
new file mode 100644
index 0000000..3ed41e5
--- /dev/null
+++ b/ext/yahttp/yahttp/Makefile.am
@@ -0,0 +1,13 @@
+noinst_LTLIBRARIES = libyahttp.la
+
+libyahttp_la_SOURCES = \
+ cookie.hpp \
+ exception.hpp \
+ reqresp.cpp \
+ reqresp.hpp \
+ router.cpp \
+ router.hpp \
+ url.hpp \
+ utility.hpp \
+ yahttp-config.h \
+ yahttp.hpp
diff --git a/ext/yahttp/yahttp/Makefile.in b/ext/yahttp/yahttp/Makefile.in
new file mode 100644
index 0000000..5dd68a8
--- /dev/null
+++ b/ext/yahttp/yahttp/Makefile.in
@@ -0,0 +1,748 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = ext/yahttp/yahttp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ac_pthread_set_name.m4 \
+ $(top_srcdir)/m4/ax_arg_default_enable_disable.m4 \
+ $(top_srcdir)/m4/ax_check_sign.m4 \
+ $(top_srcdir)/m4/ax_compile_check_sizeof.m4 \
+ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4/ax_cxx_compile_stdcxx_17.m4 \
+ $(top_srcdir)/m4/ax_python_module.m4 $(top_srcdir)/m4/boost.m4 \
+ $(top_srcdir)/m4/dnsdist_enable_dnscrypt.m4 \
+ $(top_srcdir)/m4/dnsdist_enable_doh.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/pdns_check_cdb.m4 \
+ $(top_srcdir)/m4/pdns_check_clock_gettime.m4 \
+ $(top_srcdir)/m4/pdns_check_dnstap.m4 \
+ $(top_srcdir)/m4/pdns_check_libcrypto.m4 \
+ $(top_srcdir)/m4/pdns_check_libedit.m4 \
+ $(top_srcdir)/m4/pdns_check_libh2o_evloop.m4 \
+ $(top_srcdir)/m4/pdns_check_lmdb.m4 \
+ $(top_srcdir)/m4/pdns_check_lua_hpp.m4 \
+ $(top_srcdir)/m4/pdns_check_network_libs.m4 \
+ $(top_srcdir)/m4/pdns_check_os.m4 \
+ $(top_srcdir)/m4/pdns_check_pthread_np.m4 \
+ $(top_srcdir)/m4/pdns_check_python_venv.m4 \
+ $(top_srcdir)/m4/pdns_check_ragel.m4 \
+ $(top_srcdir)/m4/pdns_check_secure_memset.m4 \
+ $(top_srcdir)/m4/pdns_check_time_t.m4 \
+ $(top_srcdir)/m4/pdns_d_fortify_source.m4 \
+ $(top_srcdir)/m4/pdns_enable_sanitizers.m4 \
+ $(top_srcdir)/m4/pdns_enable_tls.m4 \
+ $(top_srcdir)/m4/pdns_enable_unit_tests.m4 \
+ $(top_srcdir)/m4/pdns_param_ssp_buffer_size.m4 \
+ $(top_srcdir)/m4/pdns_pie.m4 $(top_srcdir)/m4/pdns_relro.m4 \
+ $(top_srcdir)/m4/pdns_stack_protector.m4 \
+ $(top_srcdir)/m4/pdns_with_ebpf.m4 \
+ $(top_srcdir)/m4/pdns_with_gnutls.m4 \
+ $(top_srcdir)/m4/pdns_with_libcap.m4 \
+ $(top_srcdir)/m4/pdns_with_libsodium.m4 \
+ $(top_srcdir)/m4/pdns_with_libssl.m4 \
+ $(top_srcdir)/m4/pdns_with_lua.m4 \
+ $(top_srcdir)/m4/pdns_with_net_snmp.m4 \
+ $(top_srcdir)/m4/pdns_with_nghttp2.m4 \
+ $(top_srcdir)/m4/pdns_with_re2.m4 \
+ $(top_srcdir)/m4/pdns_with_service_user.m4 \
+ $(top_srcdir)/m4/systemd.m4 $(top_srcdir)/m4/warnings.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libyahttp_la_LIBADD =
+am_libyahttp_la_OBJECTS = reqresp.lo router.lo
+libyahttp_la_OBJECTS = $(am_libyahttp_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/reqresp.Plo ./$(DEPDIR)/router.Plo
+am__mv = mv -f
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo " CXX " $@;
+am__v_CXX_1 =
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+am__v_CXXLD_1 =
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libyahttp_la_SOURCES)
+DIST_SOURCES = $(libyahttp_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CPPFLAGS = @AM_CPPFLAGS@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_CPPFLAGS = @BOOST_CPPFLAGS@
+BOOST_LDPATH = @BOOST_LDPATH@
+BOOST_ROOT = @BOOST_ROOT@
+BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS = @BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS@
+BOOST_UNIT_TEST_FRAMEWORK_LDPATH = @BOOST_UNIT_TEST_FRAMEWORK_LDPATH@
+BOOST_UNIT_TEST_FRAMEWORK_LIBS = @BOOST_UNIT_TEST_FRAMEWORK_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CDB_CFLAGS = @CDB_CFLAGS@
+CDB_LIBS = @CDB_LIBS@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+DYNLINKFLAGS = @DYNLINKFLAGS@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FSTRM_CFLAGS = @FSTRM_CFLAGS@
+FSTRM_LIBS = @FSTRM_LIBS@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+HAVE_CXX17 = @HAVE_CXX17@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+IPCRYPT_CFLAGS = @IPCRYPT_CFLAGS@
+IPCRYPT_LIBS = @IPCRYPT_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCAP_CFLAGS = @LIBCAP_CFLAGS@
+LIBCAP_LIBS = @LIBCAP_LIBS@
+LIBCRYPTO_INCLUDES = @LIBCRYPTO_INCLUDES@
+LIBCRYPTO_LDFLAGS = @LIBCRYPTO_LDFLAGS@
+LIBCRYPTO_LIBS = @LIBCRYPTO_LIBS@
+LIBEDIT_CFLAGS = @LIBEDIT_CFLAGS@
+LIBEDIT_LIBS = @LIBEDIT_LIBS@
+LIBH2OEVLOOP_CFLAGS = @LIBH2OEVLOOP_CFLAGS@
+LIBH2OEVLOOP_LIBS = @LIBH2OEVLOOP_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSODIUM_CFLAGS = @LIBSODIUM_CFLAGS@
+LIBSODIUM_LIBS = @LIBSODIUM_LIBS@
+LIBSSL_CFLAGS = @LIBSSL_CFLAGS@
+LIBSSL_LIBS = @LIBSSL_LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LMDB_CFLAGS = @LMDB_CFLAGS@
+LMDB_LIBS = @LMDB_LIBS@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LUA_CFLAGS = @LUA_CFLAGS@
+LUA_LIBS = @LUA_LIBS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NET_SNMP_CFLAGS = @NET_SNMP_CFLAGS@
+NET_SNMP_LIBS = @NET_SNMP_LIBS@
+NGHTTP2_CFLAGS = @NGHTTP2_CFLAGS@
+NGHTTP2_LIBS = @NGHTTP2_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGEVERSION = @PACKAGEVERSION@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROGRAM_LDFLAGS = @PROGRAM_LDFLAGS@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RAGEL = @RAGEL@
+RANLIB = @RANLIB@
+RE2_CFLAGS = @RE2_CFLAGS@
+RE2_LIBS = @RE2_LIBS@
+RELRO_LDFLAGS = @RELRO_LDFLAGS@
+RT_LIBS = @RT_LIBS@
+SANITIZER_FLAGS = @SANITIZER_FLAGS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEMCTL = @SYSTEMCTL@
+SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@
+SYSTEMD_DIR = @SYSTEMD_DIR@
+SYSTEMD_LIBS = @SYSTEMD_LIBS@
+SYSTEMD_MODULES_LOAD = @SYSTEMD_MODULES_LOAD@
+THREADFLAGS = @THREADFLAGS@
+VERSION = @VERSION@
+WARN_CFLAGS = @WARN_CFLAGS@
+YAHTTP_CFLAGS = @YAHTTP_CFLAGS@
+YAHTTP_LIBS = @YAHTTP_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+service_group = @service_group@
+service_user = @service_user@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+systemd = @systemd@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libyahttp.la
+libyahttp_la_SOURCES = \
+ cookie.hpp \
+ exception.hpp \
+ reqresp.cpp \
+ reqresp.hpp \
+ router.cpp \
+ router.hpp \
+ url.hpp \
+ utility.hpp \
+ yahttp-config.h \
+ yahttp.hpp
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cpp .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign ext/yahttp/yahttp/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign ext/yahttp/yahttp/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libyahttp.la: $(libyahttp_la_OBJECTS) $(libyahttp_la_DEPENDENCIES) $(EXTRA_libyahttp_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(CXXLINK) $(libyahttp_la_OBJECTS) $(libyahttp_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reqresp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/router.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.cpp.o:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/reqresp.Plo
+ -rm -f ./$(DEPDIR)/router.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/reqresp.Plo
+ -rm -f ./$(DEPDIR)/router.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/ext/yahttp/yahttp/cookie.hpp b/ext/yahttp/yahttp/cookie.hpp
new file mode 100644
index 0000000..aa5359b
--- /dev/null
+++ b/ext/yahttp/yahttp/cookie.hpp
@@ -0,0 +1,144 @@
+namespace YaHTTP {
+ /*! Implements a single cookie */
+ class Cookie {
+ public:
+ Cookie() {
+ secure = false;
+ httponly = false;
+ name = value = "";
+ expires = DateTime();
+ }; //!< Set the cookie to empty value
+
+ Cookie(const Cookie &rhs) {
+ name = rhs.name;
+ value = rhs.value;
+ domain = rhs.domain;
+ path = rhs.path;
+ secure = rhs.secure;
+ httponly = rhs.httponly;
+ expires = rhs.expires;
+ }; //<! Copy cookie values
+
+ Cookie& operator=(const Cookie &rhs) {
+ name = rhs.name;
+ value = rhs.value;
+ domain = rhs.domain;
+ path = rhs.path;
+ secure = rhs.secure;
+ httponly = rhs.httponly;
+ expires = rhs.expires;
+ return *this;
+ }
+
+ DateTime expires; /*!< Expiration date */
+ std::string domain; /*!< Domain where cookie is valid */
+ std::string path; /*!< Path where the cookie is valid */
+ bool httponly; /*!< Whether the cookie is for server use only */
+ bool secure; /*!< Whether the cookie is for HTTPS only */
+
+ std::string name; /*!< Cookie name */
+ std::string value; /*!< Cookie value */
+
+ std::string str() const {
+ std::ostringstream oss;
+ oss << YaHTTP::Utility::encodeURL(name) << "=" << YaHTTP::Utility::encodeURL(value);
+
+ if (expires.isSet)
+ oss << "; expires=" << expires.cookie_str();
+ if (domain.size()>0)
+ oss << "; domain=" << domain;
+ if (path.size()>0)
+ oss << "; path=" << path;
+ if (secure)
+ oss << "; secure";
+ if (httponly)
+ oss << "; httpOnly";
+ return oss.str();
+ }; //!< Stringify the cookie
+ };
+
+ /*! Implements a Cookie jar for storing multiple cookies */
+ class CookieJar {
+ public:
+ std::map<std::string, Cookie, ASCIICINullSafeComparator> cookies; //<! cookie container
+
+ CookieJar() {}; //<! constructs empty cookie jar
+ CookieJar(const CookieJar & rhs) {
+ this->cookies = rhs.cookies;
+ } //<! copy cookies from another cookie jar
+ CookieJar& operator=(const CookieJar & rhs) = default;
+
+ void clear() {
+ this->cookies.clear();
+ }
+
+ void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value) {
+ size_t pos;
+ pos = keyvalue.find("=");
+ if (pos == std::string::npos) throw ParseError("Not a Key-Value pair (cookie)");
+ key = std::string(keyvalue.begin(), keyvalue.begin()+pos);
+ value = std::string(keyvalue.begin()+pos+1, keyvalue.end());
+ } //<! key value pair parser
+
+ void parseCookieHeader(const std::string &cookiestr) {
+ size_t pos, npos;
+ std::list<Cookie> lcookies;
+ Cookie c;
+ pos = 0;
+ while(pos < cookiestr.size()) {
+ if ((npos = cookiestr.find("; ", pos)) == std::string::npos)
+ npos = cookiestr.size();
+ keyValuePair(cookiestr.substr(pos, npos-pos), c.name, c.value);
+ c.name = YaHTTP::Utility::decodeURL(c.name);
+ c.value = YaHTTP::Utility::decodeURL(c.value);
+ lcookies.push_back(c);
+ pos = npos+2;
+ }
+ for(std::list<Cookie>::iterator i = lcookies.begin(); i != lcookies.end(); i++) {
+ this->cookies[i->name] = *i;
+ }
+ }
+
+ void parseSetCookieHeader(const std::string &cookiestr) {
+ Cookie c;
+ size_t pos,npos;
+ std::string k, v;
+
+ if ((pos = cookiestr.find("; ", 0)) == std::string::npos)
+ pos = cookiestr.size();
+ keyValuePair(cookiestr.substr(0, pos), c.name, c.value);
+ c.name = YaHTTP::Utility::decodeURL(c.name);
+ c.value = YaHTTP::Utility::decodeURL(c.value);
+ if (pos < cookiestr.size()) pos+=2;
+
+ while(pos < cookiestr.size()) {
+ if ((npos = cookiestr.find("; ", pos)) == std::string::npos)
+ npos = cookiestr.size();
+ std::string s = cookiestr.substr(pos, npos-pos);
+ if (s.find("=") != std::string::npos)
+ keyValuePair(s, k, v);
+ else
+ k = s;
+ if (k == "expires") {
+ DateTime dt;
+ dt.parseCookie(v);
+ c.expires = dt;
+ } else if (k == "domain") {
+ c.domain = v;
+ } else if (k == "path") {
+ c.path = v;
+ } else if (k == "httpOnly") {
+ c.httponly = true;
+ } else if (k == "secure") {
+ c.secure = true;
+ } else {
+ // ignore crap
+ break;
+ }
+ pos = npos+2;
+ }
+
+ this->cookies[c.name] = c;
+ }; //<! Parse multiple cookies from header
+ };
+};
diff --git a/ext/yahttp/yahttp/exception.hpp b/ext/yahttp/yahttp/exception.hpp
new file mode 100644
index 0000000..d0ea0fe
--- /dev/null
+++ b/ext/yahttp/yahttp/exception.hpp
@@ -0,0 +1,24 @@
+#pragma once
+#include <exception>
+
+namespace YaHTTP {
+ /*! Generic error class */
+ class Error: public std::exception {
+ public:
+ Error() {};
+ Error(const std::string& reason_): reason(reason_) {};
+ virtual ~Error() throw() {};
+
+ virtual const char* what() const throw()
+ {
+ return reason.c_str();
+ }
+ const std::string reason; //<! Cause of the error
+ };
+ /*! Parse error class */
+ class ParseError: public YaHTTP::Error {
+ public:
+ ParseError() {};
+ ParseError(const std::string& reason_): Error(reason_) {};
+ };
+};
diff --git a/ext/yahttp/yahttp/reqresp.cpp b/ext/yahttp/yahttp/reqresp.cpp
new file mode 100644
index 0000000..dc49cb6
--- /dev/null
+++ b/ext/yahttp/yahttp/reqresp.cpp
@@ -0,0 +1,331 @@
+#include "yahttp.hpp"
+
+namespace YaHTTP {
+
+ template class AsyncLoader<Request>;
+ template class AsyncLoader<Response>;
+
+ bool isspace(char c) {
+ return std::isspace(c) != 0;
+ }
+
+ bool isspace(char c, const std::locale& loc) {
+ return std::isspace(c, loc);
+ }
+
+ bool isxdigit(char c) {
+ return std::isxdigit(c) != 0;
+ }
+
+ bool isxdigit(char c, const std::locale& loc) {
+ return std::isxdigit(c, loc);
+ }
+
+ bool isdigit(char c) {
+ return std::isdigit(c) != 0;
+ }
+
+ bool isdigit(char c, const std::locale& loc) {
+ return std::isdigit(c, loc);
+ }
+
+ bool isalnum(char c) {
+ return std::isalnum(c) != 0;
+ }
+
+ bool isalnum(char c, const std::locale& loc) {
+ return std::isalnum(c, loc);
+ }
+
+ template <class T>
+ bool AsyncLoader<T>::feed(const std::string& somedata) {
+ buffer.append(somedata);
+ while(state < 2) {
+ int cr=0;
+ pos = buffer.find_first_of("\n");
+ // need to find CRLF in buffer
+ if (pos == std::string::npos) return false;
+ if (pos>0 && buffer[pos-1]=='\r')
+ cr=1;
+ std::string line(buffer.begin(), buffer.begin()+pos-cr); // exclude CRLF
+ buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer including CRLF
+
+ if (state == 0) { // startup line
+ if (target->kind == YAHTTP_TYPE_REQUEST) {
+ std::string ver;
+ std::string tmpurl;
+ std::istringstream iss(line);
+ iss >> target->method >> tmpurl >> ver;
+ if (ver.size() == 0)
+ target->version = 9;
+ else if (ver.find("HTTP/0.9") == 0)
+ target->version = 9;
+ else if (ver.find("HTTP/1.0") == 0)
+ target->version = 10;
+ else if (ver.find("HTTP/1.1") == 0)
+ target->version = 11;
+ else
+ throw ParseError("HTTP version not supported");
+ // uppercase the target method
+ std::transform(target->method.begin(), target->method.end(), target->method.begin(), ::toupper);
+ target->url.parse(tmpurl);
+ target->getvars = Utility::parseUrlParameters(target->url.parameters);
+ state = 1;
+ } else if(target->kind == YAHTTP_TYPE_RESPONSE) {
+ std::string ver;
+ std::istringstream iss(line);
+ std::string::size_type pos1;
+ iss >> ver >> target->status;
+ std::getline(iss, target->statusText);
+ pos1=0;
+ while(pos1 < target->statusText.size() && YaHTTP::isspace(target->statusText.at(pos1))) pos1++;
+ target->statusText = target->statusText.substr(pos1);
+ if ((pos1 = target->statusText.find("\r")) != std::string::npos) {
+ target->statusText = target->statusText.substr(0, pos1-1);
+ }
+ if (ver.size() == 0) {
+ target->version = 9;
+ } else if (ver.find("HTTP/0.9") == 0)
+ target->version = 9;
+ else if (ver.find("HTTP/1.0") == 0)
+ target->version = 10;
+ else if (ver.find("HTTP/1.1") == 0)
+ target->version = 11;
+ else
+ throw ParseError("HTTP version not supported");
+ state = 1;
+ }
+ } else if (state == 1) {
+ std::string key,value;
+ size_t pos1;
+ if (line.empty()) {
+ chunked = (target->headers.find("transfer-encoding") != target->headers.end() && target->headers["transfer-encoding"] == "chunked");
+ state = 2;
+ break;
+ }
+ // split headers
+ if ((pos1 = line.find(":")) == std::string::npos) {
+ throw ParseError("Malformed header line");
+ }
+ key = line.substr(0, pos1);
+ value = line.substr(pos1 + 1);
+ for(std::string::iterator it=key.begin(); it != key.end(); it++)
+ if (YaHTTP::isspace(*it))
+ throw ParseError("Header key contains whitespace which is not allowed by RFC");
+
+ Utility::trim(value);
+ std::transform(key.begin(), key.end(), key.begin(), ::tolower);
+ // is it already defined
+
+ if (key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) {
+ target->jar.parseSetCookieHeader(value);
+ } else if (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST) {
+ target->jar.parseCookieHeader(value);
+ } else {
+ if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) {
+ // maybe it contains port?
+ if ((pos1 = value.find(":")) == std::string::npos) {
+ target->url.host = value;
+ } else {
+ target->url.host = value.substr(0, pos1);
+ target->url.port = ::atoi(value.substr(pos1).c_str());
+ }
+ }
+ if (target->headers.find(key) != target->headers.end()) {
+ target->headers[key] = target->headers[key] + ";" + value;
+ } else {
+ target->headers[key] = value;
+ }
+ }
+ }
+ }
+
+ minbody = 0;
+ // check for expected body size
+ if (target->kind == YAHTTP_TYPE_REQUEST) maxbody = target->max_request_size;
+ else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = target->max_response_size;
+ else maxbody = 0;
+
+ if (!chunked) {
+ if (target->headers.find("content-length") != target->headers.end()) {
+ std::istringstream maxbodyS(target->headers["content-length"]);
+ maxbodyS >> minbody;
+ maxbody = minbody;
+ }
+ if (minbody < 1) return true; // guess there isn't anything left.
+ if (target->kind == YAHTTP_TYPE_REQUEST && static_cast<ssize_t>(minbody) > target->max_request_size) throw ParseError("Max request body size exceeded");
+ else if (target->kind == YAHTTP_TYPE_RESPONSE && static_cast<ssize_t>(minbody) > target->max_response_size) throw ParseError("Max response body size exceeded");
+ }
+
+ if (maxbody == 0) hasBody = false;
+ else hasBody = true;
+
+ if (buffer.size() == 0) return ready();
+
+ while(buffer.size() > 0) {
+ if (chunked) {
+ if (chunk_size == 0) {
+ char buf[100];
+ // read chunk length
+ if ((pos = buffer.find('\n')) == std::string::npos) return false;
+ if (pos > 99)
+ throw ParseError("Impossible chunk_size");
+ buffer.copy(buf, pos);
+ buf[pos]=0; // just in case...
+ buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer
+ if (sscanf(buf, "%x", &chunk_size) != 1) {
+ throw ParseError("Unable to parse chunk size");
+ }
+ if (chunk_size == 0) { state = 3; break; } // last chunk
+ } else {
+ int crlf=1;
+ if (buffer.size() < static_cast<size_t>(chunk_size+1)) return false; // expect newline
+ if (buffer.at(chunk_size) == '\r') {
+ if (buffer.size() < static_cast<size_t>(chunk_size+2) || buffer.at(chunk_size+1) != '\n') return false; // expect newline after carriage return
+ crlf=2;
+ } else if (buffer.at(chunk_size) != '\n') return false;
+ std::string tmp = buffer.substr(0, chunk_size);
+ buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf);
+ bodybuf << tmp;
+ chunk_size = 0;
+ if (buffer.size() == 0) break; // just in case
+ }
+ } else {
+ if (bodybuf.str().length() + buffer.length() > maxbody)
+ bodybuf << buffer.substr(0, maxbody - bodybuf.str().length());
+ else
+ bodybuf << buffer;
+ buffer = "";
+ }
+ }
+
+ if (chunk_size!=0) return false; // need more data
+
+ return ready();
+ };
+
+ void HTTPBase::write(std::ostream& os) const {
+ if (kind == YAHTTP_TYPE_REQUEST) {
+ std::ostringstream getparmbuf;
+ std::string getparms;
+ // prepare URL
+ for(strstr_map_t::const_iterator i = getvars.begin(); i != getvars.end(); i++) {
+ getparmbuf << Utility::encodeURL(i->first, false) << "=" << Utility::encodeURL(i->second, false) << "&";
+ }
+ if (getparmbuf.str().length() > 0) {
+ std::string buf = getparmbuf.str();
+ getparms = "?" + std::string(buf.begin(), buf.end() - 1);
+ }
+ else
+ getparms = "";
+ os << method << " " << url.path << getparms << " HTTP/" << versionStr(this->version);
+ } else if (kind == YAHTTP_TYPE_RESPONSE) {
+ os << "HTTP/" << versionStr(this->version) << " " << status << " ";
+ if (statusText.empty())
+ os << Utility::status2text(status);
+ else
+ os << statusText;
+ }
+ os << "\r\n";
+
+ bool cookieSent = false;
+ bool sendChunked = false;
+
+ if (this->version > 10) { // 1.1 or better
+ if (headers.find("content-length") == headers.end() && !this->is_multipart) {
+ // must use chunked on response
+ sendChunked = (kind == YAHTTP_TYPE_RESPONSE);
+ if ((headers.find("transfer-encoding") != headers.end() && headers.find("transfer-encoding")->second != "chunked")) {
+ throw YaHTTP::Error("Transfer-encoding must be chunked, or Content-Length defined");
+ }
+ if ((headers.find("transfer-encoding") == headers.end() && kind == YAHTTP_TYPE_RESPONSE)) {
+ sendChunked = true;
+ os << "Transfer-Encoding: chunked\r\n";
+ }
+ } else {
+ sendChunked = false;
+ }
+ }
+
+ // write headers
+ strstr_map_t::const_iterator iter = headers.begin();
+ while(iter != headers.end()) {
+ if (iter->first == "host" && (kind != YAHTTP_TYPE_REQUEST || version < 10)) { iter++; continue; }
+ if (iter->first == "transfer-encoding" && sendChunked) { iter++; continue; }
+ std::string header = Utility::camelizeHeader(iter->first);
+ if (header == "Cookie" || header == "Set-Cookie") cookieSent = true;
+ os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n";
+ iter++;
+ }
+ if (version > 9 && !cookieSent && jar.cookies.size() > 0) { // write cookies
+ if (kind == YAHTTP_TYPE_REQUEST) {
+ bool first = true;
+ os << "Cookie: ";
+ for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
+ if (first)
+ first = false;
+ else
+ os << "; ";
+ os << Utility::encodeURL(i->second.name) << "=" << Utility::encodeURL(i->second.value);
+ }
+ } else if (kind == YAHTTP_TYPE_RESPONSE) {
+ for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
+ os << "Set-Cookie: ";
+ os << i->second.str() << "\r\n";
+ }
+ }
+ }
+ os << "\r\n";
+#ifdef HAVE_CPP_FUNC_PTR
+ this->renderer(this, os, sendChunked);
+#else
+ SendbodyRenderer r;
+ r(this, os, chunked)
+#endif
+ };
+
+ std::ostream& operator<<(std::ostream& os, const Response &resp) {
+ resp.write(os);
+ return os;
+ };
+
+ std::istream& operator>>(std::istream& is, Response &resp) {
+ YaHTTP::AsyncResponseLoader arl;
+ arl.initialize(&resp);
+ while(is.good()) {
+ char buf[1024];
+ is.read(buf, 1024);
+ if (is.gcount()>0) { // did we actually read anything
+ is.clear();
+ if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
+ }
+ }
+ // throw unless ready
+ if (arl.ready() == false)
+ throw ParseError("Was not able to extract a valid Response from stream");
+ arl.finalize();
+ return is;
+ };
+
+ std::ostream& operator<<(std::ostream& os, const Request &req) {
+ req.write(os);
+ return os;
+ };
+
+ std::istream& operator>>(std::istream& is, Request &req) {
+ YaHTTP::AsyncRequestLoader arl;
+ arl.initialize(&req);
+ while(is.good()) {
+ char buf[1024];
+ is.read(buf, 1024);
+ if (is.gcount() > 0) { // did we actually read anything
+ is.clear();
+ if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
+ }
+ }
+ if (arl.ready() == false)
+ throw ParseError("Was not able to extract a valid Request from stream");
+ arl.finalize();
+ return is;
+ };
+};
diff --git a/ext/yahttp/yahttp/reqresp.hpp b/ext/yahttp/yahttp/reqresp.hpp
new file mode 100644
index 0000000..00ba545
--- /dev/null
+++ b/ext/yahttp/yahttp/reqresp.hpp
@@ -0,0 +1,351 @@
+#ifdef HAVE_CXX11
+#include <functional>
+#define HAVE_CPP_FUNC_PTR
+namespace funcptr = std;
+#else
+#ifdef HAVE_BOOST
+#include <boost/function.hpp>
+namespace funcptr = boost;
+#define HAVE_CPP_FUNC_PTR
+#endif
+#endif
+
+#include <fstream>
+#include <cctype>
+
+#ifndef WIN32
+#include <cstdio>
+#include <unistd.h>
+#endif
+
+#include <algorithm>
+
+#ifndef YAHTTP_MAX_REQUEST_SIZE
+#define YAHTTP_MAX_REQUEST_SIZE 2097152
+#endif
+
+#ifndef YAHTTP_MAX_RESPONSE_SIZE
+#define YAHTTP_MAX_RESPONSE_SIZE 2097152
+#endif
+
+#define YAHTTP_TYPE_REQUEST 1
+#define YAHTTP_TYPE_RESPONSE 2
+
+namespace YaHTTP {
+ typedef std::map<std::string,Cookie,ASCIICINullSafeComparator> strcookie_map_t; //<! String to Cookie map
+
+ typedef enum {
+ urlencoded,
+ multipart
+ } postformat_t; //<! Enumeration of possible post encodings, url encoding or multipart
+
+ /*! Base class for request and response */
+ class HTTPBase {
+ public:
+ /*! Default renderer for request/response, simply copies body to response */
+ class SendBodyRender {
+ public:
+ SendBodyRender() {};
+
+ size_t operator()(const HTTPBase *doc, std::ostream& os, bool chunked) const {
+ if (chunked) {
+ std::string::size_type i,cl;
+ for(i=0;i<doc->body.length();i+=1024) {
+ cl = std::min(static_cast<std::string::size_type>(1024), doc->body.length()-i); // for less than 1k blocks
+ os << std::hex << cl << std::dec << "\r\n";
+ os << doc->body.substr(i, cl) << "\r\n";
+ }
+ os << 0 << "\r\n\r\n"; // last chunk
+ } else {
+ os << doc->body;
+ }
+ return doc->body.length();
+ }; //<! writes body to ostream and returns length
+ };
+ /* Simple sendfile renderer which streams file to ostream */
+ class SendFileRender {
+ public:
+ SendFileRender(const std::string& path_) {
+ this->path = path_;
+ };
+
+ size_t operator()(const HTTPBase *doc __attribute__((unused)), std::ostream& os, bool chunked) const {
+ char buf[4096];
+ size_t n,k;
+#ifdef HAVE_CXX11
+ std::ifstream ifs(path, std::ifstream::binary);
+#else
+ std::ifstream ifs(path.c_str(), std::ifstream::binary);
+#endif
+ n = 0;
+
+ while(ifs.good()) {
+ ifs.read(buf, sizeof buf);
+ n += (k = ifs.gcount());
+ if (k > 0) {
+ if (chunked) os << std::hex << k << std::dec << "\r\n";
+ os.write(buf, k);
+ if (chunked) os << "\r\n";
+ }
+ }
+ if (chunked) os << 0 << "\r\n\r\n";
+ return n;
+ }; //<! writes file to ostream and returns length
+
+ std::string path; //<! File to send
+ };
+
+ HTTPBase() {
+ HTTPBase::initialize();
+ };
+
+ virtual void initialize() {
+ kind = 0;
+ status = 0;
+#ifdef HAVE_CPP_FUNC_PTR
+ renderer = SendBodyRender();
+#endif
+ max_request_size = YAHTTP_MAX_REQUEST_SIZE;
+ max_response_size = YAHTTP_MAX_RESPONSE_SIZE;
+ url = "";
+ method = "";
+ statusText = "";
+ jar.clear();
+ headers.clear();
+ parameters.clear();
+ getvars.clear();
+ postvars.clear();
+ body = "";
+ routeName = "";
+ version = 11; // default to version 1.1
+ is_multipart = false;
+ }
+protected:
+ HTTPBase(const HTTPBase& rhs) {
+ this->url = rhs.url; this->kind = rhs.kind;
+ this->status = rhs.status; this->statusText = rhs.statusText;
+ this->method = rhs.method; this->headers = rhs.headers;
+ this->jar = rhs.jar; this->postvars = rhs.postvars;
+ this->parameters = rhs.parameters; this->getvars = rhs.getvars;
+ this->body = rhs.body; this->max_request_size = rhs.max_request_size;
+ this->max_response_size = rhs.max_response_size; this->version = rhs.version;
+#ifdef HAVE_CPP_FUNC_PTR
+ this->renderer = rhs.renderer;
+#endif
+ this->is_multipart = rhs.is_multipart;
+ };
+ virtual HTTPBase& operator=(const HTTPBase& rhs) {
+ this->url = rhs.url; this->kind = rhs.kind;
+ this->status = rhs.status; this->statusText = rhs.statusText;
+ this->method = rhs.method; this->headers = rhs.headers;
+ this->jar = rhs.jar; this->postvars = rhs.postvars;
+ this->parameters = rhs.parameters; this->getvars = rhs.getvars;
+ this->body = rhs.body; this->max_request_size = rhs.max_request_size;
+ this->max_response_size = rhs.max_response_size; this->version = rhs.version;
+#ifdef HAVE_CPP_FUNC_PTR
+ this->renderer = rhs.renderer;
+#endif
+ this->is_multipart = rhs.is_multipart;
+ return *this;
+ };
+public:
+ URL url; //<! URL of this request/response
+ int kind; //<! Type of object (1 = request, 2 = response)
+ int status; //<! status code
+ int version; //<! http version 9 = 0.9, 10 = 1.0, 11 = 1.1
+ std::string statusText; //<! textual representation of status code
+ std::string method; //<! http verb
+ strstr_map_t headers; //<! map of header(s)
+ CookieJar jar; //<! cookies
+ strstr_map_t postvars; //<! map of POST variables (from POST body)
+ strstr_map_t getvars; //<! map of GET variables (from URL)
+// these two are for Router
+ strstr_map_t parameters; //<! map of route parameters (only if you use YaHTTP::Router)
+ std::string routeName; //<! name of the current route (only if you use YaHTTP::Router)
+
+ std::string body; //<! the actual content
+
+ ssize_t max_request_size; //<! maximum size of request
+ ssize_t max_response_size; //<! maximum size of response
+ bool is_multipart; //<! if the request is multipart, prevents Content-Length header
+#ifdef HAVE_CPP_FUNC_PTR
+ funcptr::function<size_t(const HTTPBase*,std::ostream&,bool)> renderer; //<! rendering function
+#endif
+ void write(std::ostream& os) const; //<! writes request to the given output stream
+
+ strstr_map_t& GET() { return getvars; }; //<! acccessor for getvars
+ strstr_map_t& POST() { return postvars; }; //<! accessor for postvars
+ strcookie_map_t& COOKIES() { return jar.cookies; }; //<! accessor for cookies
+
+ std::string versionStr(int version_) const {
+ switch(version_) {
+ case 9: return "0.9";
+ case 10: return "1.0";
+ case 11: return "1.1";
+ default: throw YaHTTP::Error("Unsupported version");
+ }
+ };
+
+ std::string str() const {
+ std::ostringstream oss;
+ write(oss);
+ return oss.str();
+ }; //<! return string representation of this object
+ };
+
+ /*! Response class, represents a HTTP Response document */
+ class Response: public HTTPBase {
+ public:
+ Response() { Response::initialize(); };
+ Response(const HTTPBase& rhs): HTTPBase(rhs) {
+ this->kind = YAHTTP_TYPE_RESPONSE;
+ };
+ Response& operator=(const HTTPBase& rhs) override {
+ HTTPBase::operator=(rhs);
+ this->kind = YAHTTP_TYPE_RESPONSE;
+ return *this;
+ };
+ void initialize() override {
+ HTTPBase::initialize();
+ this->kind = YAHTTP_TYPE_RESPONSE;
+ }
+ void initialize(const HTTPBase& rhs) {
+ HTTPBase::initialize();
+ this->kind = YAHTTP_TYPE_RESPONSE;
+ // copy SOME attributes
+ this->url = rhs.url;
+ this->method = rhs.method;
+ this->jar = rhs.jar;
+ this->version = rhs.version;
+ }
+ friend std::ostream& operator<<(std::ostream& os, const Response &resp);
+ friend std::istream& operator>>(std::istream& is, Response &resp);
+ };
+
+ /* Request class, represents a HTTP Request document */
+ class Request: public HTTPBase {
+ public:
+ Request() { Request::initialize(); };
+ Request(const HTTPBase& rhs): HTTPBase(rhs) {
+ this->kind = YAHTTP_TYPE_REQUEST;
+ };
+ Request& operator=(const HTTPBase& rhs) override {
+ HTTPBase::operator=(rhs);
+ this->kind = YAHTTP_TYPE_REQUEST;
+ return *this;
+ };
+ void initialize() override {
+ HTTPBase::initialize();
+ this->kind = YAHTTP_TYPE_REQUEST;
+ }
+ void initialize(const HTTPBase& rhs) {
+ HTTPBase::initialize();
+ this->kind = YAHTTP_TYPE_REQUEST;
+ // copy SOME attributes
+ this->url = rhs.url;
+ this->method = rhs.method;
+ this->jar = rhs.jar;
+ this->version = rhs.version;
+ }
+ void setup(const std::string& method_, const std::string& url_) {
+ this->url.parse(url_);
+ this->headers["host"] = this->url.host.find(":") == std::string::npos ? this->url.host : "[" + this->url.host + "]";
+ this->method = method_;
+ std::transform(this->method.begin(), this->method.end(), this->method.begin(), ::toupper);
+ this->headers["user-agent"] = "YaHTTP v1.0";
+ }; //<! Set some initial things for a request
+
+ void preparePost(postformat_t format = urlencoded) {
+ std::ostringstream postbuf;
+ if (format == urlencoded) {
+ for(strstr_map_t::const_iterator i = POST().begin(); i != POST().end(); i++) {
+ postbuf << Utility::encodeURL(i->first, false) << "=" << Utility::encodeURL(i->second, false) << "&";
+ }
+ // remove last bit
+ if (postbuf.str().length()>0)
+ body = postbuf.str().substr(0, postbuf.str().length()-1);
+ else
+ body = "";
+ headers["content-type"] = "application/x-www-form-urlencoded; charset=utf-8";
+ } else if (format == multipart) {
+ headers["content-type"] = "multipart/form-data; boundary=YaHTTP-12ca543";
+ this->is_multipart = true;
+ for(strstr_map_t::const_iterator i = POST().begin(); i != POST().end(); i++) {
+ postbuf << "--YaHTTP-12ca543\r\nContent-Disposition: form-data; name=\"" << Utility::encodeURL(i->first, false) << "\"; charset=UTF-8\r\nContent-Length: " << i->second.size() << "\r\n\r\n"
+ << Utility::encodeURL(i->second, false) << "\r\n";
+ }
+ postbuf << "--";
+ body = postbuf.str();
+ }
+
+ postbuf.str("");
+ postbuf << body.length();
+ // set method and change headers
+ method = "POST";
+ if (!this->is_multipart)
+ headers["content-length"] = postbuf.str();
+ }; //<! convert all postvars into string and stuff it into body
+
+ friend std::ostream& operator<<(std::ostream& os, const Request &resp);
+ friend std::istream& operator>>(std::istream& is, Request &resp);
+ };
+
+ /*! Asynchronous HTTP document loader */
+ template <class T>
+ class AsyncLoader {
+ public:
+ T* target; //<! target to populate
+ int state; //<! reader state
+ size_t pos; //<! reader position
+
+ std::string buffer; //<! read buffer
+ bool chunked; //<! whether we are parsing chunked data
+ int chunk_size; //<! expected size of next chunk
+ std::ostringstream bodybuf; //<! buffer for body
+ size_t maxbody; //<! maximum size of body
+ size_t minbody; //<! minimum size of body
+ bool hasBody; //<! are we expecting body
+
+ void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value); //<! key value pair parser helper
+
+ void initialize(T* target_) {
+ chunked = false; chunk_size = 0;
+ bodybuf.str(""); minbody = 0; maxbody = 0;
+ pos = 0; state = 0; this->target = target_;
+ hasBody = false;
+ buffer = "";
+ this->target->initialize();
+ }; //<! Initialize the parser for target and clear state
+ bool feed(const std::string& somedata); //<! Feed data to the parser
+ bool ready() {
+ return (chunked == true && state == 3) || // if it's chunked we get end of data indication
+ (chunked == false && state > 1 &&
+ (!hasBody ||
+ (bodybuf.str().size() <= maxbody &&
+ bodybuf.str().size() >= minbody)
+ )
+ );
+ }; //<! whether we have received enough data
+ void finalize() {
+ bodybuf.flush();
+ if (ready()) {
+ strstr_map_t::iterator cpos = target->headers.find("content-type");
+ if (cpos != target->headers.end() && Utility::iequals(cpos->second, "application/x-www-form-urlencoded", 32)) {
+ target->postvars = Utility::parseUrlParameters(bodybuf.str());
+ }
+ target->body = bodybuf.str();
+ }
+ bodybuf.str("");
+ this->target = NULL;
+ }; //<! finalize and release target
+ };
+
+ /*! Asynchronous HTTP response loader */
+ class AsyncResponseLoader: public AsyncLoader<Response> {
+ };
+
+ /*! Asynchronous HTTP request loader */
+ class AsyncRequestLoader: public AsyncLoader<Request> {
+ };
+
+};
diff --git a/ext/yahttp/yahttp/router.cpp b/ext/yahttp/yahttp/router.cpp
new file mode 100644
index 0000000..489f8cf
--- /dev/null
+++ b/ext/yahttp/yahttp/router.cpp
@@ -0,0 +1,160 @@
+/* @file
+ * @brief Concrete implementation of Router
+ */
+#include "yahttp.hpp"
+#include "router.hpp"
+
+namespace YaHTTP {
+ typedef funcptr::tuple<int,int> TDelim;
+
+ // router is defined here.
+ YaHTTP::Router Router::router;
+
+ void Router::map(const std::string& method, const std::string& url, THandlerFunction handler, const std::string& name) {
+ std::string method2 = method;
+ bool isopen=false;
+ // add into vector
+ for(std::string::const_iterator i = url.begin(); i != url.end(); i++) {
+ if (*i == '<' && isopen) throw Error("Invalid URL mask, cannot have < after <");
+ if (*i == '<') isopen = true;
+ if (*i == '>' && !isopen) throw Error("Invalid URL mask, cannot have > without < first");
+ if (*i == '>') isopen = false;
+ }
+ std::transform(method2.begin(), method2.end(), method2.begin(), ::toupper);
+ routes.push_back(funcptr::make_tuple(method2, url, handler, name));
+ };
+
+ bool Router::route(Request *req, THandlerFunction& handler) {
+ std::map<std::string, TDelim> params;
+ int pos1,pos2;
+ bool matched = false;
+ std::string rname;
+
+ // iterate routes
+ for(TRouteList::iterator i = routes.begin(); !matched && i != routes.end(); i++) {
+ int k1,k2,k3;
+ std::string pname;
+ std::string method, url;
+ funcptr::tie(method, url, handler, rname) = *i;
+
+ if (method.empty() == false && req->method != method) continue; // no match on method
+ // see if we can't match the url
+ params.clear();
+ // simple matcher func
+ for(k1=0, k2=0; k1 < static_cast<int>(url.size()) && k2 < static_cast<int>(req->url.path.size()); ) {
+ if (url[k1] == '<') {
+ pos1 = k2;
+ k3 = k1+1;
+ // start of parameter
+ while(k1 < static_cast<int>(url.size()) && url[k1] != '>') k1++;
+ pname = std::string(url.begin()+k3, url.begin()+k1);
+ // then we also look it on the url
+ if (pname[0]=='*') {
+ pname = pname.substr(1);
+ // this matches whatever comes after it, basically end of string
+ pos2 = req->url.path.size();
+ if (pname != "")
+ params[pname] = funcptr::tie(pos1,pos2);
+ k1 = url.size();
+ k2 = req->url.path.size();
+ break;
+ } else {
+ // match until url[k1]
+ while(k2 < static_cast<int>(req->url.path.size()) && req->url.path[k2] != url[k1+1]) k2++;
+ pos2 = k2;
+ params[pname] = funcptr::tie(pos1,pos2);
+ }
+ k2--;
+ }
+ else if (url[k1] != req->url.path[k2]) {
+ break;
+ }
+
+ k1++; k2++;
+ }
+
+ // ensure.
+ if (url[k1] != req->url.path[k2])
+ matched = false;
+ else
+ matched = true;
+ }
+
+ if (!matched) { return false; } // no route
+ req->parameters.clear();
+
+ for(std::map<std::string, TDelim>::iterator i = params.begin(); i != params.end(); i++) {
+ int p1,p2;
+ funcptr::tie(p1,p2) = i->second;
+ std::string value(req->url.path.begin() + p1, req->url.path.begin() + p2);
+ value = Utility::decodeURL(value);
+ req->parameters[i->first] = value;
+ }
+
+ req->routeName = rname;
+
+ return true;
+ };
+
+ void Router::printRoutes(std::ostream &os) {
+ for(TRouteList::iterator i = routes.begin(); i != routes.end(); i++) {
+#ifdef HAVE_CXX11
+ std::streamsize ss = os.width();
+ std::ios::fmtflags ff = os.setf(std::ios::left);
+ os.width(10);
+ os << std::get<0>(*i);
+ os.width(50);
+ os << std::get<1>(*i);
+ os.width(ss);
+ os.setf(ff);
+ os << " " << std::get<3>(*i);
+ os << std::endl;
+#else
+ os << i->get<0>() << " " << i->get<1>() << " " << i->get<3>() << std::endl;
+#endif
+ }
+ };
+
+ std::pair<std::string,std::string> Router::urlFor(const std::string &name, const strstr_map_t& arguments) {
+ std::ostringstream path;
+ std::string mask,method,result;
+ int k1,k2,k3;
+
+ bool found = false;
+ for(TRouteList::iterator i = routes.begin(); !found && i != routes.end(); i++) {
+#ifdef HAVE_CXX11
+ if (std::get<3>(*i) == name) { mask = std::get<1>(*i); method = std::get<0>(*i); found = true; }
+#else
+ if (i->get<3>() == name) { mask = i->get<1>(); method = i->get<0>(); found = true; }
+#endif
+ }
+
+ if (!found)
+ throw Error("Route not found");
+
+ for(k1=0,k3=0;k1<static_cast<int>(mask.size());k1++) {
+ if (mask[k1] == '<') {
+ std::string pname;
+ strstr_map_t::const_iterator pptr;
+ k2=k1;
+ while(k1<static_cast<int>(mask.size()) && mask[k1]!='>') k1++;
+ path << mask.substr(k3,k2-k3);
+ if (mask[k2+1] == '*')
+ pname = std::string(mask.begin() + k2 + 2, mask.begin() + k1);
+ else
+ pname = std::string(mask.begin() + k2 + 1, mask.begin() + k1);
+ if ((pptr = arguments.find(pname)) != arguments.end())
+ path << Utility::encodeURL(pptr->second);
+ k3 = k1+1;
+ }
+ else if (mask[k1] == '*') {
+ // ready
+ k3++;
+ continue;
+ }
+ }
+ path << mask.substr(k3);
+ result = path.str();
+ return std::make_pair(method, result);
+ }
+};
diff --git a/ext/yahttp/yahttp/router.hpp b/ext/yahttp/yahttp/router.hpp
new file mode 100644
index 0000000..a0dbd13
--- /dev/null
+++ b/ext/yahttp/yahttp/router.hpp
@@ -0,0 +1,72 @@
+#pragma once
+/* @file
+ * @brief Defines router class and support structures
+ */
+#ifdef HAVE_CXX11
+#include <functional>
+#include <tuple>
+#define HAVE_CPP_FUNC_PTR
+#define IGNORE std::ignore
+namespace funcptr = std;
+#else
+#ifdef HAVE_BOOST
+#include <boost/function.hpp>
+#include <boost/tuple/tuple.hpp>
+#define IGNORE boost::tuples::ignore
+namespace funcptr = boost;
+#define HAVE_CPP_FUNC_PTR
+#else
+#warning "You need to configure with boost or have C++11 capable compiler for router"
+#endif
+#endif
+
+#ifdef HAVE_CPP_FUNC_PTR
+#include <vector>
+#include <utility>
+
+namespace YaHTTP {
+ typedef funcptr::function <void(Request* req, Response* resp)> THandlerFunction; //!< Handler function pointer
+ typedef funcptr::tuple<std::string, std::string, THandlerFunction, std::string> TRoute; //!< Route tuple (method, urlmask, handler, name)
+ typedef std::vector<TRoute> TRouteList; //!< List of routes in order of evaluation
+
+ /*! Implements simple router.
+
+This class implements a router for masked urls. The URL mask syntax is as of follows
+
+/&lt;masked&gt;/url&lt;number&gt;/&lt;hi&gt;.&lt;format&gt;
+
+You can use &lt;*param&gt; to denote that everything will be matched and consumed into the parameter, including slash (/). Use &lt;*&gt; to denote that URL
+is consumed but not stored. Note that only path is matched, scheme, host and url parameters are ignored.
+ */
+ class Router {
+ private:
+ Router() {};
+ static Router router; //<! Singleton instance of Router
+ public:
+ void map(const std::string& method, const std::string& url, THandlerFunction handler, const std::string& name); //<! Instance method for mapping urls
+ bool route(Request *req, THandlerFunction& handler); //<! Instance method for performing routing
+ void printRoutes(std::ostream &os); //<! Instance method for printing routes
+ std::pair<std::string, std::string> urlFor(const std::string &name, const strstr_map_t& arguments); //<! Instance method for generating paths
+
+/*! Map an URL.
+If method is left empty, it will match any method. Name is also optional, but needed if you want to find it for making URLs
+*/
+ static void Map(const std::string& method, const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map(method, url, handler, name); };
+ static void Get(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("GET", url, handler, name); }; //<! Helper for mapping GET
+ static void Post(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("POST", url, handler, name); }; //<! Helper for mapping POST
+ static void Put(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("PUT", url, handler, name); }; //<! Helper for mapping PUT
+ static void Patch(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("PATCH", url, handler, name); }; //<! Helper for mapping PATCH
+ static void Delete(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("DELETE", url, handler, name); }; //<! Helper for mapping DELETE
+ static void Any(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("", url, handler, name); }; //<! Helper for mapping any method
+
+ static bool Route(Request *req, THandlerFunction& handler) { return router.route(req, handler); }; //<! Performs routing based on req->url.path
+ static void PrintRoutes(std::ostream &os) { router.printRoutes(os); }; //<! Prints all known routes to given output stream
+
+ static std::pair<std::string, std::string> URLFor(const std::string &name, const strstr_map_t& arguments) { return router.urlFor(name,arguments); }; //<! Generates url from named route and arguments. Missing arguments are assumed empty
+ static const TRouteList& GetRoutes() { return router.routes; } //<! Reference to route list
+ static void Clear() { router.routes.clear(); } //<! Clear all routes
+
+ TRouteList routes; //<! Instance variable for routes
+ };
+};
+#endif
diff --git a/ext/yahttp/yahttp/url.hpp b/ext/yahttp/yahttp/url.hpp
new file mode 100644
index 0000000..fbdd48d
--- /dev/null
+++ b/ext/yahttp/yahttp/url.hpp
@@ -0,0 +1,200 @@
+#pragma once
+#include <sstream>
+#include <string>
+
+#include "utility.hpp"
+
+#ifndef YAHTTP_MAX_URL_LENGTH
+#define YAHTTP_MAX_URL_LENGTH 2048
+#endif
+
+namespace YaHTTP {
+ /*! URL parser and container */
+ class URL {
+ private:
+ bool parseSchema(const std::string& url, size_t &pos) {
+ size_t pos1;
+ if (pos >= url.size()) return false; // no data
+ if ( (pos1 = url.find_first_of(":",pos)) == std::string::npos ) return false; // schema is mandatory
+ protocol = url.substr(pos, pos1-pos);
+ if (protocol == "http") port = 80;
+ if (protocol == "https") port = 443;
+ pos = pos1+1; // after :
+ if (url.compare(pos, 2, "//") == 0) {
+ pathless = false; // if this is true we put rest into parameters
+ pos += 2;
+ }
+ return true;
+ }; //<! parse schema/protocol part
+
+ bool parseHost(const std::string& url, size_t &pos) {
+ size_t pos1;
+ if (pos >= url.size()) return true; // no data
+ if ( (pos1 = url.find_first_of("/", pos)) == std::string::npos ) {
+ host = url.substr(pos);
+ path = "/";
+ pos = url.size();
+ } else {
+ host = url.substr(pos, pos1-pos);
+ pos = pos1;
+ }
+ if (host.at(0) == '[') { // IPv6
+ if ((pos1 = host.find_first_of("]")) == std::string::npos) {
+ // incomplete address
+ return false;
+ }
+ size_t pos2;
+ if ((pos2 = host.find_first_of(":", pos1)) != std::string::npos) {
+ std::istringstream tmp(host.substr(pos2 + 1));
+ tmp >> port;
+ }
+ host = host.substr(1, pos1 - 1);
+ } else if ( (pos1 = host.find_first_of(":")) != std::string::npos ) {
+ std::istringstream tmp(host.substr(pos1+1));
+ tmp >> port;
+ host = host.substr(0, pos1);
+ }
+ return true;
+ }; //<! parse host and port
+
+ bool parseUserPass(const std::string& url, size_t &pos) {
+ size_t pos1,pos2;
+ if (pos >= url.size()) return true; // no data
+
+ if ( (pos1 = url.find_first_of("@",pos)) == std::string::npos ) return true; // no userinfo
+ pos2 = url.find_first_of(":",pos);
+
+ if (pos2 != std::string::npos) { // comes with password
+ username = url.substr(pos, pos2 - pos);
+ password = url.substr(pos2+1, pos1 - pos2 - 1);
+ password = Utility::decodeURL(password);
+ } else {
+ username = url.substr(pos, pos1 - pos);
+ }
+ pos = pos1+1;
+ username = Utility::decodeURL(username);
+ return true;
+ }; //<! parse possible username and password
+
+ bool parsePath(const std::string& url, size_t &pos) {
+ size_t pos1;
+ if (pos >= url.size()) return true; // no data
+ if (url[pos] != '/') return false; // not an url
+ if ( (pos1 = url.find_first_of("?", pos)) == std::string::npos ) {
+ path = url.substr(pos);
+ pos = url.size();
+ } else {
+ path = url.substr(pos, pos1-pos);
+ pos = pos1;
+ }
+ return true;
+ }; //<! parse path component
+
+ bool parseParameters(const std::string& url, size_t &pos) {
+ size_t pos1;
+ if (pos >= url.size()) return true; // no data
+ if (url[pos] == '#') return true; // anchor starts here
+ if (url[pos] != '?') return false; // not a parameter
+ if ( (pos1 = url.find_first_of("#", pos)) == std::string::npos ) {
+ parameters = url.substr(pos+1);;
+ pos = url.size();
+ } else {
+ parameters = url.substr(pos+1, pos1-pos-1);
+ pos = pos1;
+ }
+ if (parameters.size()>0 && *(parameters.end()-1) == '&') parameters.resize(parameters.size()-1);
+ return true;
+ }; //<! parse url parameters
+
+ bool parseAnchor(const std::string& url, size_t &pos) {
+ if (pos >= url.size()) return true; // no data
+ if (url[pos] != '#') return false; // not anchor
+ anchor = url.substr(pos+1);
+ return true;
+ }; //<! parse anchor
+
+ void initialize() {
+ protocol = ""; host = ""; port = 0; username = ""; password = ""; path = ""; parameters = ""; anchor =""; pathless = true;
+ }; //<! initialize to empty URL
+
+ public:
+ std::string to_string() const {
+ std::string tmp;
+ std::ostringstream oss;
+
+ if (protocol.empty() == false) {
+ oss << protocol << ":";
+ if (host.empty() == false) {
+ oss << "//";
+ }
+ }
+
+ if (username.empty() == false) {
+ if (password.empty() == false)
+ oss << Utility::encodeURL(username) << ":" << Utility::encodeURL(password) << "@";
+ else
+ oss << Utility::encodeURL(username) << "@";
+ }
+ if (host.empty() == false)
+ oss << host;
+ if (!(protocol == "http" && port == 80) &&
+ !(protocol == "https" && port == 443) &&
+ port > 0)
+ oss << ":" << port;
+
+ oss << path;
+ if (parameters.empty() == false) {
+ if (!pathless)
+ oss << "?";
+ oss << parameters;
+ }
+ if (anchor.empty() == false)
+ oss << "#" << anchor;
+ return oss.str();
+ }; //<! convert this URL to string
+
+ std::string protocol; //<! schema/protocol
+ std::string host; //<! host
+ int port; //<! port
+ std::string username; //<! username
+ std::string password; //<! password
+ std::string path; //<! path
+ std::string parameters; //<! url parameters
+ std::string anchor; //<! anchor
+ bool pathless; //<! whether this url has no path
+
+ URL() { initialize(); }; //<! construct empty url
+ URL(const std::string& url) {
+ parse(url);
+ }; //<! calls parse with url
+
+ URL(const char *url) {
+ parse(std::string(url));
+ }; //<! calls parse with url
+
+ bool parse(const std::string& url) {
+ // setup
+ initialize();
+
+ if (url.size() > YAHTTP_MAX_URL_LENGTH) return false;
+ size_t pos = 0;
+ if (*(url.begin()) != '/') { // full url?
+ if (parseSchema(url, pos) == false) return false;
+ if (pathless) {
+ parameters = url.substr(pos);
+ return true;
+ }
+ if (parseUserPass(url, pos) == false) return false;
+ if (parseHost(url, pos) == false) return false;
+ }
+ if (parsePath(url, pos) == false) return false;
+ if (parseParameters(url, pos) == false) return false;
+ return parseAnchor(url, pos);
+ }; //<! parse various formats of urls ranging from http://example.com/foo?bar=baz into data:base64:d089swt64wt...
+
+ friend std::ostream & operator<<(std::ostream& os, const URL& url) {
+ os<<url.to_string();
+ return os;
+ };
+ };
+};
diff --git a/ext/yahttp/yahttp/utility.hpp b/ext/yahttp/yahttp/utility.hpp
new file mode 100644
index 0000000..23e6b8a
--- /dev/null
+++ b/ext/yahttp/yahttp/utility.hpp
@@ -0,0 +1,462 @@
+#pragma once
+namespace YaHTTP {
+ static const char *MONTHS[] = {0,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",0}; //<! List of months
+ static const char *DAYS[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat",0}; //<! List of days
+
+ bool isspace(char c);
+ bool isspace(char c, const std::locale& loc);
+ bool isxdigit(char c);
+ bool isxdigit(char c, const std::locale& loc);
+ bool isdigit(char c);
+ bool isdigit(char c, const std::locale& loc);
+ bool isalnum(char c);
+ bool isalnum(char c, const std::locale& loc);
+
+ /*! Case-Insensitive NULL safe comparator for string maps */
+ struct ASCIICINullSafeComparator {
+ bool operator() (const std::string& lhs, const std::string& rhs) const {
+ int v;
+ std::string::const_iterator lhi = lhs.begin();
+ std::string::const_iterator rhi = rhs.begin();
+ for(;lhi != lhs.end() && rhi != rhs.end(); lhi++, rhi++)
+ if ((v = ::tolower(*lhi) - ::tolower(*rhi)) != 0) return v<0;
+ if (lhi == lhs.end() && rhi != rhs.end()) return true;
+ if (lhi != lhs.end() && rhi == rhs.end()) return false;
+ return false; // they are equal
+ }
+ };
+
+ typedef std::map<std::string,std::string,ASCIICINullSafeComparator> strstr_map_t; //<! String to String map
+
+ /*! Represents a date/time with utc offset */
+ class DateTime {
+ public:
+ bool isSet; //<! if this is initialized yet
+
+ int year; //<! year, 0 is year 0, not 1900
+
+ int month; //<! month, range 1-12
+ int day; //<! day, range 1-31
+ int wday; //<! week day, range 1-7
+
+ int hours; //<! hours, range 0-23
+ int minutes; //<! minutes, range 0-59
+ int seconds; //<! seconds, range 0-60
+
+ int utc_offset; //<! UTC offset with minutes (hhmm)
+
+ DateTime() {
+ initialize();
+ }; //<! Construct and initialize
+
+ void initialize() {
+ isSet = false;
+ year = month = day = wday = hours = minutes = seconds = utc_offset = 0;
+ month = 1; // it's invalid otherwise
+ }; //<! Creates year 0 date
+
+ void setLocal() {
+ fromLocaltime(time((time_t*)NULL));
+ }; //<! sets current local time
+
+ void setGm() {
+ fromGmtime(time((time_t*)NULL));
+ }; //<! sets current gmtime (almost UTC)
+
+ void fromLocaltime(time_t t) {
+#ifdef HAVE_LOCALTIME_R
+ struct tm tm;
+ localtime_r(&t, &tm);
+ fromTm(&tm);
+#else
+ struct tm *tm;
+ #error define HAVE_LOCALTIME_R
+ tm = localtime(&t); // lgtm [cpp/potentially-dangerous-function]
+ fromTm(tm);
+#endif
+#ifndef HAVE_TM_GMTOFF
+ time_t t2;
+# ifdef HAVE_LOCALTIME_R
+ gmtime_r(&t, &tm);
+ t2 = mktime(&tm);
+# else
+ #error define HAVE_LOCALTIME_R
+ tm = gmtime(&t); // lgtm [cpp/potentially-dangerous-function]
+ t2 = mktime(tm);
+# endif
+ this->utc_offset = ((t2-t)/10)*10; // removes any possible differences.
+#endif
+ }; //<! uses localtime for time
+
+ void fromGmtime(time_t t) {
+#ifdef HAVE_GMTIME_R
+ struct tm tm;
+ gmtime_r(&t, &tm);
+ fromTm(&tm);
+#else
+ struct tm *tm;
+ #error define HAVE_GMTIME_R
+ tm = gmtime(&t);// lgtm [cpp/potentially-dangerous-function]
+ fromTm(tm);
+#endif
+#ifndef HAVE_TM_GMTOFF
+ this->utc_offset = 0;
+#endif
+ }; //<! uses gmtime for time
+
+ void fromTm(const struct tm *tm) {
+ year = tm->tm_year + 1900;
+ month = tm->tm_mon + 1;
+ day = tm->tm_mday;
+ hours = tm->tm_hour;
+ minutes = tm->tm_min;
+ seconds = tm->tm_sec;
+ wday = tm->tm_wday;
+#ifdef HAVE_TM_GMTOFF
+ utc_offset = tm->tm_gmtoff;
+#endif
+ isSet = true;
+ }; //<! parses date from struct tm
+
+ void validate() const {
+ if (wday < 0 || wday > 6) throw std::range_error("Invalid date");
+ if (month < 1 || month > 12) throw std::range_error("Invalid date");
+ if (year < 0) throw std::range_error("Invalid date");
+ if (hours < 0 || hours > 23 ||
+ minutes < 0 || minutes > 59 ||
+ seconds < 0 || seconds > 60) throw std::range_error("Invalid date");
+ }; //<! make sure we are within ranges (not a *REAL* validation, just range check)
+
+ std::string rfc_str() const {
+ std::ostringstream oss;
+ validate();
+ oss << DAYS[wday] << ", " << std::setfill('0') << std::setw(2) << day << " " << MONTHS[month] << " " <<
+ std::setfill('0') << std::setw(2) << year << " " <<
+ std::setfill('0') << std::setw(2) << hours << ":" <<
+ std::setfill('0') << std::setw(2) << minutes << ":" <<
+ std::setfill('0') << std::setw(2) << seconds << " ";
+ if (utc_offset>=0) oss << "+";
+ else oss << "-";
+ int tmp_off = ( utc_offset < 0 ? utc_offset*-1 : utc_offset );
+ oss << std::setfill('0') << std::setw(2) << (tmp_off/3600);
+ oss << std::setfill('0') << std::setw(2) << (tmp_off%3600)/60;
+
+ return oss.str();
+ }; //<! converts this date into a RFC-822 format
+
+ std::string cookie_str() const {
+ std::ostringstream oss;
+ validate();
+ oss << std::setfill('0') << std::setw(2) << day << "-" << MONTHS[month] << "-" << year << " " <<
+ std::setfill('0') << std::setw(2) << hours << ":" <<
+ std::setfill('0') << std::setw(2) << minutes << ":" <<
+ std::setfill('0') << std::setw(2) << seconds << " GMT";
+ return oss.str();
+ }; //<! converts this date into a HTTP Cookie date
+
+ void parse822(const std::string &rfc822_date) {
+ struct tm tm;
+ const char *ptr;
+#ifdef HAVE_TM_GMTOFF
+ if ( (ptr = strptime(rfc822_date.c_str(), "%a, %d %b %Y %T %z", &tm)) != NULL) {
+#else
+ if ( (ptr = strptime(rfc822_date.c_str(), "%a, %d %b %Y %T", &tm)) != NULL) {
+ int sign;
+ // parse the timezone parameter
+ while(*ptr && YaHTTP::isspace(*ptr)) ptr++;
+ if (*ptr == '+') sign = 0;
+ else if (*ptr == '-') sign = -1;
+ else throw YaHTTP::ParseError("Unparseable date");
+ ptr++;
+ utc_offset = ::atoi(ptr) * sign;
+ while(*ptr != '\0' && YaHTTP::isdigit(*ptr)) ptr++;
+#endif
+ while(*ptr != '\0' && YaHTTP::isspace(*ptr)) ptr++;
+ if (*ptr != '\0') throw YaHTTP::ParseError("Unparseable date"); // must be final.
+ fromTm(&tm);
+ } else {
+ throw YaHTTP::ParseError("Unparseable date");
+ }
+ }; //<! parses RFC-822 date
+
+ void parseCookie(const std::string &cookie_date) {
+ struct tm tm;
+ const char *ptr;
+ if ( (ptr = strptime(cookie_date.c_str(), "%d-%b-%Y %T", &tm)) != NULL
+#ifdef HAVE_TM_GMTOFF
+ || (ptr = strptime(cookie_date.c_str(), "%d-%b-%Y %T %z", &tm)) != NULL
+ || (ptr = strptime(cookie_date.c_str(), "%a, %d-%b-%Y %T %Z", &tm)) != NULL
+#endif
+ ) {
+ while(*ptr != '\0' && ( YaHTTP::isspace(*ptr) || YaHTTP::isalnum(*ptr) )) ptr++;
+ if (*ptr != '\0') throw YaHTTP::ParseError("Unparseable date (non-final)"); // must be final.
+ fromTm(&tm);
+ this->utc_offset = 0;
+ } else {
+ std::cout << cookie_date << std::endl;
+ throw YaHTTP::ParseError("Unparseable date (did not match pattern cookie)");
+ }
+ }; //<! parses HTTP Cookie date
+
+ time_t unixtime() const {
+ struct tm tm;
+ tm.tm_year = year-1900;
+ tm.tm_mon = month-1;
+ tm.tm_mday = day;
+ tm.tm_hour = hours;
+ tm.tm_min = minutes;
+ tm.tm_sec = seconds;
+ tm.tm_isdst = 0;
+#ifdef HAVE_TM_GMTOFF
+ tm.tm_gmtoff = utc_offset;
+#endif
+ return mktime(&tm);
+ }; //<! returns this datetime as unixtime. will not work for dates before 1970/1/1 00:00:00 GMT
+ };
+
+ /*! Various helpers needed in the code */
+ class Utility {
+ public:
+ static std::string decodeURL(const std::string& component) {
+ std::string result = component;
+ size_t pos1,pos2;
+ pos2 = 0;
+ while((pos1 = result.find_first_of("%", pos2))!=std::string::npos) {
+ std::string code;
+ char a,b,c;
+ if (pos1 + 2 > result.length()) return result; // end of result
+ code = result.substr(pos1+1, 2);
+ a = std::tolower(code[0]); b = std::tolower(code[1]);
+
+ if ((( '0' > a || a > '9') && ('a' > a || a > 'f')) ||
+ (( '0' > b || b > '9') && ('a' > b || b > 'f'))) {
+ pos2 = pos1+3;
+ continue;
+ }
+
+ if ('0' <= a && a <= '9') a = a - '0';
+ else if ('a' <= a && a <= 'f') a = a - 'a' + 0x0a;
+ if ('0' <= b && b <= '9') b = b - '0';
+ else if ('a' <= b && b <= 'f') b = b - 'a' + 0x0a;
+
+ c = (a<<4)+b;
+ result = result.replace(pos1,3,1,c);
+ pos2=pos1;
+ }
+ return result;
+ }; //<! Decodes %xx from string into bytes
+
+ static std::string encodeURL(const std::string& component, bool asUrl = true) {
+ std::string result = component;
+ std::string skip = "+-.:,&;_#%[]?/@(){}=";
+ char repl[3];
+ size_t pos;
+ for(std::string::iterator iter = result.begin(); iter != result.end(); iter++) {
+ if (!YaHTTP::isalnum(*iter) && (!asUrl || skip.find(*iter) == std::string::npos)) {
+ // replace with different thing
+ pos = std::distance(result.begin(), iter);
+ ::snprintf(repl,3,"%02x", static_cast<unsigned char>(*iter));
+ result = result.replace(pos, 1, "%", 1).insert(pos+1, repl, 2);
+ iter = result.begin() + pos + 2;
+ }
+ }
+ return result;
+ }; //<! Escapes any characters into %xx representation when necessary, set asUrl to false to fully encode the url
+
+ static std::string encodeURL(const std::wstring& component, bool asUrl = true) {
+ unsigned char const *p = reinterpret_cast<unsigned char const*>(&component[0]);
+ std::size_t s = component.size() * sizeof((*component.begin()));
+ std::vector<unsigned char> vec(p, p+s);
+
+ std::ostringstream result;
+ std::string skip = "+-.,&;_#%[]?/@(){}=";
+ for(std::vector<unsigned char>::iterator iter = vec.begin(); iter != vec.end(); iter++) {
+ if (!YaHTTP::isalnum((char)*iter) && (!asUrl || skip.find((char)*iter) == std::string::npos)) {
+ // bit more complex replace
+ result << "%" << std::hex << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(*iter);
+ } else result << (char)*iter;
+ }
+ return result.str();
+ }; //<! Escapes any characters into %xx representation when necessary, set asUrl to false to fully encode the url, for wide strings, returns ordinary string
+
+ static std::string status2text(int status) {
+ switch(status) {
+ case 200:
+ return "OK";
+ case 201:
+ return "Created";
+ case 202:
+ return "Accepted";
+ case 203:
+ return "Non-Authoritative Information";
+ case 204:
+ return "No Content";
+ case 205:
+ return "Reset Content";
+ case 206:
+ return "Partial Content";
+ case 300:
+ return "Multiple Choices";
+ case 301:
+ return "Moved Permanently";
+ case 302:
+ return "Found";
+ case 303:
+ return "See Other";
+ case 304:
+ return "Not Modified";
+ case 305:
+ return "Use Proxy";
+ case 307:
+ return "Temporary Redirect";
+ case 400:
+ return "Bad Request";
+ case 401:
+ return "Unauthorized";
+ case 402:
+ return "Payment Required";
+ case 403:
+ return "Forbidden";
+ case 404:
+ return "Not Found";
+ case 405:
+ return "Method Not Allowed";
+ case 406:
+ return "Not Acceptable";
+ case 407:
+ return "Proxy Authentication Required";
+ case 408:
+ return "Request Time-out";
+ case 409:
+ return "Conflict";
+ case 410:
+ return "Gone";
+ case 411:
+ return "Length Required";
+ case 412:
+ return "Precondition Failed";
+ case 413:
+ return "Request Entity Too Large";
+ case 414:
+ return "Request-URI Too Large";
+ case 415:
+ return "Unsupported Media Type";
+ case 416:
+ return "Requested range not satisfiable";
+ case 417:
+ return "Expectation Failed";
+ case 422:
+ return "Unprocessable Entity";
+ case 500:
+ return "Internal Server Error";
+ case 501:
+ return "Not Implemented";
+ case 502:
+ return "Bad Gateway";
+ case 503:
+ return "Service Unavailable";
+ case 504:
+ return "Gateway Time-out";
+ case 505:
+ return "HTTP Version not supported";
+ default:
+ return "Unknown Status";
+ }
+ }; //<! static HTTP codes to text mappings
+
+ static strstr_map_t parseUrlParameters(std::string parameters) {
+ std::string::size_type pos = 0;
+ strstr_map_t parameter_map;
+ while (pos != std::string::npos) {
+ // find next parameter start
+ std::string::size_type nextpos = parameters.find("&", pos);
+ std::string::size_type delim = parameters.find("=", pos);
+ if (delim > nextpos) {
+ delim = nextpos;
+ }
+ std::string key;
+ std::string value;
+ if (delim == std::string::npos) {
+ key = parameters.substr(pos);
+ } else {
+ key = parameters.substr(pos, delim-pos);
+ if (nextpos == std::string::npos) {
+ value = parameters.substr(delim+1);
+ } else {
+ value = parameters.substr(delim+1, nextpos-delim-1);
+ }
+ }
+ if (key.empty()) {
+ // no parameters at all
+ break;
+ }
+ key = decodeURL(key);
+ value = decodeURL(value);
+ parameter_map[key] = value;
+ if (nextpos == std::string::npos) {
+ // no more parameters left
+ break;
+ }
+
+ pos = nextpos+1;
+ }
+ return parameter_map;
+ }; //<! parses URL parameters into string map
+
+ static bool iequals(const std::string& a, const std::string& b, size_t length) {
+ std::string::const_iterator ai, bi;
+ size_t i;
+ for(ai = a.begin(), bi = b.begin(), i = 0; ai != a.end() && bi != b.end() && i < length; ai++,bi++,i++) {
+ if (::toupper(*ai) != ::toupper(*bi)) return false;
+ }
+
+ if (ai == a.end() && bi == b.end()) return true;
+ if ((ai == a.end() && bi != b.end()) ||
+ (ai != a.end() && bi == b.end())) return false;
+
+ return ::toupper(*ai) == ::toupper(*bi);
+ }; //<! case-insensitive comparison with length
+
+ static bool iequals(const std::string& a, const std::string& b) {
+ if (a.size() != b.size()) return false;
+ return iequals(a,b,a.size());
+ }; //<! case-insensitive comparison
+
+ static void trimLeft(std::string &str) {
+ const std::locale &loc = std::locale::classic();
+ std::string::iterator iter = str.begin();
+ while(iter != str.end() && YaHTTP::isspace(*iter, loc)) iter++;
+ str.erase(str.begin(), iter);
+ }; //<! removes whitespace from left
+
+ static void trimRight(std::string &str) {
+ const std::locale &loc = std::locale::classic();
+ std::string::reverse_iterator iter = str.rbegin();
+ while(iter != str.rend() && YaHTTP::isspace(*iter, loc)) iter++;
+ str.erase(iter.base(), str.end());
+ }; //<! removes whitespace from right
+
+ static void trim(std::string &str) {
+ trimLeft(str);
+ trimRight(str);
+ }; //<! removes whitespace from left and right
+
+ static std::string camelizeHeader(const std::string &str) {
+ std::string::const_iterator iter = str.begin();
+ std::string result;
+ const std::locale &loc = std::locale::classic();
+
+ bool doNext = true;
+
+ while(iter != str.end()) {
+ if (doNext)
+ result.insert(result.end(), std::toupper(*iter, loc));
+ else
+ result.insert(result.end(), std::tolower(*iter, loc));
+ doNext = (*(iter++) == '-');
+ }
+
+ return result;
+ }; //<! camelizes headers, such as, content-type => Content-Type
+ };
+};
diff --git a/ext/yahttp/yahttp/yahttp-config.h b/ext/yahttp/yahttp/yahttp-config.h
new file mode 100644
index 0000000..1ac2545
--- /dev/null
+++ b/ext/yahttp/yahttp/yahttp-config.h
@@ -0,0 +1 @@
+#include "config.h"
diff --git a/ext/yahttp/yahttp/yahttp.hpp b/ext/yahttp/yahttp/yahttp.hpp
new file mode 100644
index 0000000..d60be7a
--- /dev/null
+++ b/ext/yahttp/yahttp/yahttp.hpp
@@ -0,0 +1,37 @@
+#include <map>
+#include <iostream>
+#include <locale>
+#include <algorithm>
+#include <string>
+#include <cstdio>
+#include <stdexcept>
+#include <sys/time.h>
+#include <iomanip>
+#include <list>
+#include <vector>
+
+#include "yahttp-config.h"
+#include "exception.hpp"
+#include "url.hpp"
+#include "utility.hpp"
+#include "url.hpp"
+#include "cookie.hpp"
+#include "reqresp.hpp"
+
+/*! \mainpage Yet Another HTTP Library Documentation
+\section sec_quick_start Quick start example
+
+@code
+#include <yahttp/yahttp.hpp>
+
+int main(void) {
+ std::ifstream ifs("request.txt");
+ YaHTTP::Request req;
+ ifs >> req;
+
+ std::cout << req.method " " << req.url.path << std::endl;
+ return 0;
+}
+@endcode
+\author Aki Tuomi
+*/