summaryrefslogtreecommitdiffstats
path: root/ext/yahttp/yahttp
diff options
context:
space:
mode:
Diffstat (limited to 'ext/yahttp/yahttp')
-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
12 files changed, 2543 insertions, 0 deletions
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
+*/