diff options
Diffstat (limited to 'ext/yahttp/yahttp')
-rw-r--r-- | ext/yahttp/yahttp/Makefile.am | 13 | ||||
-rw-r--r-- | ext/yahttp/yahttp/Makefile.in | 748 | ||||
-rw-r--r-- | ext/yahttp/yahttp/cookie.hpp | 144 | ||||
-rw-r--r-- | ext/yahttp/yahttp/exception.hpp | 24 | ||||
-rw-r--r-- | ext/yahttp/yahttp/reqresp.cpp | 331 | ||||
-rw-r--r-- | ext/yahttp/yahttp/reqresp.hpp | 351 | ||||
-rw-r--r-- | ext/yahttp/yahttp/router.cpp | 160 | ||||
-rw-r--r-- | ext/yahttp/yahttp/router.hpp | 72 | ||||
-rw-r--r-- | ext/yahttp/yahttp/url.hpp | 200 | ||||
-rw-r--r-- | ext/yahttp/yahttp/utility.hpp | 462 | ||||
-rw-r--r-- | ext/yahttp/yahttp/yahttp-config.h | 1 | ||||
-rw-r--r-- | ext/yahttp/yahttp/yahttp.hpp | 37 |
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 + +/<masked>/url<number>/<hi>.<format> + +You can use <*param> to denote that everything will be matched and consumed into the parameter, including slash (/). Use <*> 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 +*/ |