Add stock 0.9.42 source.

This is from http://www.gnu.org/software/libmicrohttpd/ (and
was verified to match the source from a local mirror).

Bug: 23101922
Change-Id: I7efc9d5065b49b8957ce41bb6afd2ce1f39c7cac
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..d496c95
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,31 @@
+# This Makefile.am is in the public domain
+if HAVE_CURL
+curltests = testcurl
+if HAVE_ZZUF
+if HAVE_SOCAT
+zzuftests = testzzuf
+endif
+endif
+endif
+if ENABLE_SPDY
+if HAVE_OPENSSL
+microspdy = microspdy
+if HAVE_CURL
+microspdy += spdy2http
+endif
+#if HAVE_SPDYLAY
+microspdy += testspdy
+#endif
+endif
+endif
+
+SUBDIRS = include platform microhttpd $(microspdy) $(curltests) $(zzuftests) .
+
+if BUILD_EXAMPLES
+SUBDIRS += examples
+endif
+
+EXTRA_DIST = \
+ datadir/cert-and-key.pem \
+ datadir/cert-and-key-for-wireshark.pem \
+ datadir/spdy-draft.txt
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..8c4db5b
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,678 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 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 = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+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@
+@ENABLE_SPDY_TRUE@@HAVE_CURL_TRUE@@HAVE_OPENSSL_TRUE@am__append_1 = spdy2http
+@BUILD_EXAMPLES_TRUE@am__append_2 = examples
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_compile_flags.m4 \
+	$(top_srcdir)/m4/ax_append_flag.m4 \
+	$(top_srcdir)/m4/ax_check_compile_flag.m4 \
+	$(top_srcdir)/m4/ax_check_link_flag.m4 \
+	$(top_srcdir)/m4/ax_check_openssl.m4 \
+	$(top_srcdir)/m4/ax_count_cpus.m4 \
+	$(top_srcdir)/m4/ax_have_epoll.m4 \
+	$(top_srcdir)/m4/ax_pthread.m4 \
+	$(top_srcdir)/m4/ax_require_defined.m4 \
+	$(top_srcdir)/m4/libcurl.m4 $(top_srcdir)/m4/libgcrypt.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)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/MHD_config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-recursive dvi-recursive html-recursive info-recursive \
+	install-data-recursive install-dvi-recursive \
+	install-exec-recursive install-html-recursive \
+	install-info-recursive install-pdf-recursive \
+	install-ps-recursive install-recursive installcheck-recursive \
+	installdirs-recursive pdf-recursive ps-recursive \
+	tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	distdir
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = include platform microhttpd microspdy spdy2http \
+	testspdy testcurl testzzuf . examples
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPU_COUNT = @CPU_COUNT@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_CPPFLAGS = @GNUTLS_CPPFLAGS@
+GNUTLS_LDFLAGS = @GNUTLS_LDFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+HAVE_CURL_BINARY = @HAVE_CURL_BINARY@
+HAVE_MAKEINFO_BINARY = @HAVE_MAKEINFO_BINARY@
+HIDDEN_VISIBILITY_CFLAGS = @HIDDEN_VISIBILITY_CFLAGS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSPDY_VERSION_AGE = @LIBSPDY_VERSION_AGE@
+LIBSPDY_VERSION_CURRENT = @LIBSPDY_VERSION_CURRENT@
+LIBSPDY_VERSION_REVISION = @LIBSPDY_VERSION_REVISION@
+LIBTOOL = @LIBTOOL@
+LIB_VERSION_AGE = @LIB_VERSION_AGE@
+LIB_VERSION_CURRENT = @LIB_VERSION_CURRENT@
+LIB_VERSION_REVISION = @LIB_VERSION_REVISION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MHD_LIBDEPS = @MHD_LIBDEPS@
+MHD_LIBDEPS_PKGCFG = @MHD_LIBDEPS_PKGCFG@
+MHD_LIB_CFLAGS = @MHD_LIB_CFLAGS@
+MHD_LIB_CPPFLAGS = @MHD_LIB_CPPFLAGS@
+MHD_LIB_LDFLAGS = @MHD_LIB_LDFLAGS@
+MHD_REQ_PRIVATE = @MHD_REQ_PRIVATE@
+MKDIR_P = @MKDIR_P@
+MS_LIB_TOOL = @MS_LIB_TOOL@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_INCLUDES = @OPENSSL_INCLUDES@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@
+PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@
+PACKAGE_VERSION_SUBMINOR = @PACKAGE_VERSION_SUBMINOR@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPDY_LIBDEPS = @SPDY_LIBDEPS@
+SPDY_LIB_CFLAGS = @SPDY_LIB_CFLAGS@
+SPDY_LIB_CPPFLAGS = @SPDY_LIB_CPPFLAGS@
+SPDY_LIB_LDFLAGS = @SPDY_LIB_LDFLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+_libcurl_config = @_libcurl_config@
+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_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@
+ax_pthread_config = @ax_pthread_config@
+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@
+have_socat = @have_socat@
+have_zzuf = @have_zzuf@
+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@
+lt_cv_objdir = @lt_cv_objdir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# This Makefile.am is in the public domain
+@HAVE_CURL_TRUE@curltests = testcurl
+@HAVE_CURL_TRUE@@HAVE_SOCAT_TRUE@@HAVE_ZZUF_TRUE@zzuftests = testzzuf
+#if HAVE_SPDYLAY
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@microspdy = microspdy \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@	$(am__append_1) testspdy
+#endif
+SUBDIRS = include platform microhttpd $(microspdy) $(curltests) \
+	$(zzuftests) . $(am__append_2)
+EXTRA_DIST = \
+ datadir/cert-and-key.pem \
+ datadir/cert-and-key-for-wireshark.pem \
+ datadir/spdy-draft.txt
+
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/Makefile
+.PRECIOUS: 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__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+#     (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    $(am__make_dryrun) \
+	      || test -d "$(distdir)/$$subdir" \
+	      || $(MKDIR_P) "$(distdir)/$$subdir" \
+	      || exit 1; \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+	check-am clean clean-generic clean-libtool cscopelist-am ctags \
+	ctags-am distclean distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am install-dvi \
+	install-dvi-am install-exec install-exec-am install-html \
+	install-html-am install-info install-info-am install-man \
+	install-pdf install-pdf-am install-ps install-ps-am \
+	install-strip installcheck installcheck-am installdirs \
+	installdirs-am maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+	ps ps-am tags tags-am uninstall uninstall-am
+
+
+# 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/src/datadir/cert-and-key-for-wireshark.pem b/src/datadir/cert-and-key-for-wireshark.pem
new file mode 100644
index 0000000..eacd4b4
--- /dev/null
+++ b/src/datadir/cert-and-key-for-wireshark.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDc1k7EFEspRcr6PdPmvAd02hBDUG2O5dDkoRK+6tgEBvQxsTxz
+50TGwJ8RbSV+qUOnncZBwhnI4i71QSEezMP6I6liRA+fUtdh3cZFvdDpxgU6P15y
+5JxfnnDeZJR5O4tfMxN99t34EOEMruZZ0CNYJJgbmIteE0hLI418oUs7cwIDAQAB
+AoGAW3WOLXrSHge/pp/QkLCyzdw5/AblONdJCkcDQnp0eEaA/8uNY9sWCtJfjpIL
+g0eKs3KOV1GR6DZ0iDIvC1h2mO6pwyrJhRYHKPO9pnx7xpv1T9zYTuVwoMfVjPfO
+UCWFedSsSKR76+oP0TrwPDqp3JoMFcAyAqZKMg2JrRUpL3ECQQD92cVSYxSEKwX3
+cHWXVp1mSkuRMR/KX70NC/9XpYr8FEjvtXBTkmp7oG3TaDwd6nhSAodtY+Zkseyj
+k5NXM6sNAkEA3rT6opc1YK0pL3uweg+AFP4lRUYrrft1bDELhLmVm9Nf4xEdTnDO
+IotiX4GYQ64Bm8J+yxVU204soGynVXbgfwJBALQLCqq+X0TGhvrSpnRqGET+mM4n
+u1Z7xMhGJBpz7TmQ4ZIya7K6fA+m3347xbeqHyB7brYlTrlIgIAcITqOCNkCQQDE
+3tuJC34mHi0ASqkw3a7t39R2rpdCT733jEuQYrY8b9id061CgDnZE7o8j0VY3uOR
+G5gWUp8W1r5gemxaAqJlAkEAwVImBKyKy3oIMsd3WsoqYCMBM+cpX8H2JAEEISPT
+Y5zSPZ7kT9JgY2zJwXf3qL+uG1oKZ6f0wn4BYujctTbXwQ==
+-----END RSA PRIVATE KEY-----
diff --git a/src/datadir/cert-and-key.pem b/src/datadir/cert-and-key.pem
new file mode 100644
index 0000000..e33608c
--- /dev/null
+++ b/src/datadir/cert-and-key.pem
@@ -0,0 +1,34 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDc1k7EFEspRcr6PdPmvAd02hBDUG2O5dDkoRK+6tgEBvQxsTxz
+50TGwJ8RbSV+qUOnncZBwhnI4i71QSEezMP6I6liRA+fUtdh3cZFvdDpxgU6P15y
+5JxfnnDeZJR5O4tfMxN99t34EOEMruZZ0CNYJJgbmIteE0hLI418oUs7cwIDAQAB
+AoGAW3WOLXrSHge/pp/QkLCyzdw5/AblONdJCkcDQnp0eEaA/8uNY9sWCtJfjpIL
+g0eKs3KOV1GR6DZ0iDIvC1h2mO6pwyrJhRYHKPO9pnx7xpv1T9zYTuVwoMfVjPfO
+UCWFedSsSKR76+oP0TrwPDqp3JoMFcAyAqZKMg2JrRUpL3ECQQD92cVSYxSEKwX3
+cHWXVp1mSkuRMR/KX70NC/9XpYr8FEjvtXBTkmp7oG3TaDwd6nhSAodtY+Zkseyj
+k5NXM6sNAkEA3rT6opc1YK0pL3uweg+AFP4lRUYrrft1bDELhLmVm9Nf4xEdTnDO
+IotiX4GYQ64Bm8J+yxVU204soGynVXbgfwJBALQLCqq+X0TGhvrSpnRqGET+mM4n
+u1Z7xMhGJBpz7TmQ4ZIya7K6fA+m3347xbeqHyB7brYlTrlIgIAcITqOCNkCQQDE
+3tuJC34mHi0ASqkw3a7t39R2rpdCT733jEuQYrY8b9id061CgDnZE7o8j0VY3uOR
+G5gWUp8W1r5gemxaAqJlAkEAwVImBKyKy3oIMsd3WsoqYCMBM+cpX8H2JAEEISPT
+Y5zSPZ7kT9JgY2zJwXf3qL+uG1oKZ6f0wn4BYujctTbXwQ==
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDJTCCAo6gAwIBAgIJAIjfJkuxM1pAMA0GCSqGSIb3DQEBBQUAMGsxCzAJBgNV
+BAYTAkJHMQ4wDAYDVQQIEwVTb2ZpYTEOMAwGA1UEBxMFU29maWExCzAJBgNVBAoT
+AkFVMQ8wDQYDVQQDEwZBbmRyZXkxHjAcBgkqhkiG9w0BCQEWD3Jvb3RAZ29vZ2xl
+LmNvbTAeFw0xMjA5MDgxMTU2MzNaFw0xMzA5MDgxMTU2MzNaMGsxCzAJBgNVBAYT
+AkJHMQ4wDAYDVQQIEwVTb2ZpYTEOMAwGA1UEBxMFU29maWExCzAJBgNVBAoTAkFV
+MQ8wDQYDVQQDEwZBbmRyZXkxHjAcBgkqhkiG9w0BCQEWD3Jvb3RAZ29vZ2xlLmNv
+bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3NZOxBRLKUXK+j3T5rwHdNoQ
+Q1BtjuXQ5KESvurYBAb0MbE8c+dExsCfEW0lfqlDp53GQcIZyOIu9UEhHszD+iOp
+YkQPn1LXYd3GRb3Q6cYFOj9ecuScX55w3mSUeTuLXzMTffbd+BDhDK7mWdAjWCSY
+G5iLXhNISyONfKFLO3MCAwEAAaOB0DCBzTAdBgNVHQ4EFgQUPp4dR3IT0t6bSE3Y
+b91MyHJU2+8wgZ0GA1UdIwSBlTCBkoAUPp4dR3IT0t6bSE3Yb91MyHJU2++hb6Rt
+MGsxCzAJBgNVBAYTAkJHMQ4wDAYDVQQIEwVTb2ZpYTEOMAwGA1UEBxMFU29maWEx
+CzAJBgNVBAoTAkFVMQ8wDQYDVQQDEwZBbmRyZXkxHjAcBgkqhkiG9w0BCQEWD3Jv
+b3RAZ29vZ2xlLmNvbYIJAIjfJkuxM1pAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN
+AQEFBQADgYEAeBPoIRueOJ+SwpVniE2gmnvogWH9+irJnapDKGZrC/JsTA7ArqRd
+EHO1xZxqF+v+v128LmwWAdaazgMUHjR7e7B0xo4Tcp+9+voczc/GBTO0wnp6HT76
+2kUB6rPwwg9bycW8hAJiJJtr3IW5eYMtXDqM4RrbxhA1n2EAaZPVa5s=
+-----END CERTIFICATE-----
diff --git a/src/datadir/spdy-draft.txt b/src/datadir/spdy-draft.txt
new file mode 100644
index 0000000..c31648c
--- /dev/null
+++ b/src/datadir/spdy-draft.txt
@@ -0,0 +1,2856 @@
+
+
+
+Network Working Group                                          M. Belshe
+Internet-Draft                                                     Twist
+Expires: August 4, 2012                                          R. Peon
+                                                             Google, Inc
+                                                                Feb 2012
+
+
+                             SPDY Protocol
+                     draft-mbelshe-httpbis-spdy-00
+
+Abstract
+
+   This document describes SPDY, a protocol designed for low-latency
+   transport of content over the World Wide Web. SPDY introduces two
+   layers of protocol.  The lower layer is a general purpose framing
+   layer which can be used atop a reliable transport (likely TCP) for
+   multiplexed, prioritized, and compressed data communication of many
+   concurrent streams.  The upper layer of the protocol provides HTTP-
+   like RFC2616 [RFC2616] semantics for compatibility with existing HTTP
+   application servers.
+
+Status of this Memo
+
+   This Internet-Draft is submitted in full conformance with the
+   provisions of BCP 78 and BCP 79.
+
+   Internet-Drafts are working documents of the Internet Engineering
+   Task Force (IETF).  Note that other groups may also distribute
+   working documents as Internet-Drafts.  The list of current Internet-
+   Drafts is at http://datatracker.ietf.org/drafts/current/.
+
+   Internet-Drafts are draft documents valid for a maximum of six months
+   and may be updated, replaced, or obsoleted by other documents at any
+   time.  It is inappropriate to use Internet-Drafts as reference
+   material or to cite them other than as "work in progress."
+
+   This Internet-Draft will expire on August 4, 2012.
+
+Copyright Notice
+
+   Copyright (c) 2012 IETF Trust and the persons identified as the
+   document authors.  All rights reserved.
+
+   This document is subject to BCP 78 and the IETF Trust's Legal
+   Provisions Relating to IETF Documents
+   (http://trustee.ietf.org/license-info) in effect on the date of
+   publication of this document.  Please review these documents
+   carefully, as they describe your rights and restrictions with respect
+
+
+
+Belshe & Peon            Expires August 4, 2012                 [Page 1]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   to this document.  Code Components extracted from this document must
+   include Simplified BSD License text as described in Section 4.e of
+   the Trust Legal Provisions and are provided without warranty as
+   described in the Simplified BSD License.
+
+
+Table of Contents
+
+   1.  Overview . . . . . . . . . . . . . . . . . . . . . . . . . . .  4
+     1.1.  Document Organization  . . . . . . . . . . . . . . . . . .  4
+     1.2.  Definitions  . . . . . . . . . . . . . . . . . . . . . . .  5
+   2.  SPDY Framing Layer . . . . . . . . . . . . . . . . . . . . . .  6
+     2.1.  Session (Connections)  . . . . . . . . . . . . . . . . . .  6
+     2.2.  Framing  . . . . . . . . . . . . . . . . . . . . . . . . .  6
+       2.2.1.  Control frames . . . . . . . . . . . . . . . . . . . .  6
+       2.2.2.  Data frames  . . . . . . . . . . . . . . . . . . . . .  7
+     2.3.  Streams  . . . . . . . . . . . . . . . . . . . . . . . . .  8
+       2.3.1.  Stream frames  . . . . . . . . . . . . . . . . . . . .  9
+       2.3.2.  Stream creation  . . . . . . . . . . . . . . . . . . .  9
+       2.3.3.  Stream priority  . . . . . . . . . . . . . . . . . . . 10
+       2.3.4.  Stream headers . . . . . . . . . . . . . . . . . . . . 10
+       2.3.5.  Stream data exchange . . . . . . . . . . . . . . . . . 10
+       2.3.6.  Stream half-close  . . . . . . . . . . . . . . . . . . 10
+       2.3.7.  Stream close . . . . . . . . . . . . . . . . . . . . . 11
+     2.4.  Error Handling . . . . . . . . . . . . . . . . . . . . . . 11
+       2.4.1.  Session Error Handling . . . . . . . . . . . . . . . . 11
+       2.4.2.  Stream Error Handling  . . . . . . . . . . . . . . . . 12
+     2.5.  Data flow  . . . . . . . . . . . . . . . . . . . . . . . . 12
+     2.6.  Control frame types  . . . . . . . . . . . . . . . . . . . 12
+       2.6.1.  SYN_STREAM . . . . . . . . . . . . . . . . . . . . . . 12
+       2.6.2.  SYN_REPLY  . . . . . . . . . . . . . . . . . . . . . . 14
+       2.6.3.  RST_STREAM . . . . . . . . . . . . . . . . . . . . . . 15
+       2.6.4.  SETTINGS . . . . . . . . . . . . . . . . . . . . . . . 16
+       2.6.5.  PING . . . . . . . . . . . . . . . . . . . . . . . . . 19
+       2.6.6.  GOAWAY . . . . . . . . . . . . . . . . . . . . . . . . 20
+       2.6.7.  HEADERS  . . . . . . . . . . . . . . . . . . . . . . . 21
+       2.6.8.  WINDOW_UPDATE  . . . . . . . . . . . . . . . . . . . . 22
+       2.6.9.  CREDENTIAL . . . . . . . . . . . . . . . . . . . . . . 24
+       2.6.10. Name/Value Header Block  . . . . . . . . . . . . . . . 26
+   3.  HTTP Layering over SPDY  . . . . . . . . . . . . . . . . . . . 33
+     3.1.  Connection Management  . . . . . . . . . . . . . . . . . . 33
+       3.1.1.  Use of GOAWAY  . . . . . . . . . . . . . . . . . . . . 33
+     3.2.  HTTP Request/Response  . . . . . . . . . . . . . . . . . . 34
+       3.2.1.  Request  . . . . . . . . . . . . . . . . . . . . . . . 34
+       3.2.2.  Response . . . . . . . . . . . . . . . . . . . . . . . 35
+       3.2.3.  Authentication . . . . . . . . . . . . . . . . . . . . 36
+     3.3.  Server Push Transactions . . . . . . . . . . . . . . . . . 37
+       3.3.1.  Server implementation  . . . . . . . . . . . . . . . . 38
+
+
+
+Belshe & Peon            Expires August 4, 2012                 [Page 2]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+       3.3.2.  Client implementation  . . . . . . . . . . . . . . . . 39
+   4.  Design Rationale and Notes . . . . . . . . . . . . . . . . . . 40
+     4.1.  Separation of Framing Layer and Application Layer  . . . . 40
+     4.2.  Error handling - Framing Layer . . . . . . . . . . . . . . 40
+     4.3.  One Connection Per Domain  . . . . . . . . . . . . . . . . 40
+     4.4.  Fixed vs Variable Length Fields  . . . . . . . . . . . . . 41
+     4.5.  Compression Context(s) . . . . . . . . . . . . . . . . . . 41
+     4.6.  Unidirectional streams . . . . . . . . . . . . . . . . . . 42
+     4.7.  Data Compression . . . . . . . . . . . . . . . . . . . . . 42
+     4.8.  Server Push  . . . . . . . . . . . . . . . . . . . . . . . 42
+   5.  Security Considerations  . . . . . . . . . . . . . . . . . . . 43
+     5.1.  Use of Same-origin constraints . . . . . . . . . . . . . . 43
+     5.2.  HTTP Headers and SPDY Headers  . . . . . . . . . . . . . . 43
+     5.3.  Cross-Protocol Attacks . . . . . . . . . . . . . . . . . . 43
+     5.4.  Server Push Implicit Headers . . . . . . . . . . . . . . . 43
+   6.  Privacy Considerations . . . . . . . . . . . . . . . . . . . . 44
+     6.1.  Long Lived Connections . . . . . . . . . . . . . . . . . . 44
+     6.2.  SETTINGS frame . . . . . . . . . . . . . . . . . . . . . . 44
+   7.  Incompatibilities with SPDY draft #2 . . . . . . . . . . . . . 45
+   8.  Requirements Notation  . . . . . . . . . . . . . . . . . . . . 46
+   9.  Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 47
+   10. Normative References . . . . . . . . . . . . . . . . . . . . . 48
+   Appendix A.  Changes . . . . . . . . . . . . . . . . . . . . . . . 50
+   Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 51
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                 [Page 3]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+1.  Overview
+
+   One of the bottlenecks of HTTP implementations is that HTTP relies on
+   multiple connections for concurrency.  This causes several problems,
+   including additional round trips for connection setup, slow-start
+   delays, and connection rationing by the client, where it tries to
+   avoid opening too many connections to any single server.  HTTP
+   pipelining helps some, but only achieves partial multiplexing.  In
+   addition, pipelining has proven non-deployable in existing browsers
+   due to intermediary interference.
+
+   SPDY adds a framing layer for multiplexing multiple, concurrent
+   streams across a single TCP connection (or any reliable transport
+   stream).  The framing layer is optimized for HTTP-like request-
+   response streams, such that applications which run over HTTP today
+   can work over SPDY with little or no change on behalf of the web
+   application writer.
+
+   The SPDY session offers four improvements over HTTP:
+
+      Multiplexed requests: There is no limit to the number of requests
+      that can be issued concurrently over a single SPDY connection.
+
+      Prioritized requests: Clients can request certain resources to be
+      delivered first.  This avoids the problem of congesting the
+      network channel with non-critical resources when a high-priority
+      request is pending.
+
+      Compressed headers: Clients today send a significant amount of
+      redundant data in the form of HTTP headers.  Because a single web
+      page may require 50 or 100 subrequests, this data is significant.
+
+      Server pushed streams: Server Push enables content to be pushed
+      from servers to clients without a request.
+
+   SPDY attempts to preserve the existing semantics of HTTP.  All
+   features such as cookies, ETags, Vary headers, Content-Encoding
+   negotiations, etc work as they do with HTTP; SPDY only replaces the
+   way the data is written to the network.
+
+1.1.  Document Organization
+
+   The SPDY Specification is split into two parts: a framing layer
+   (Section 2), which multiplexes a TCP connection into independent,
+   length-prefixed frames, and an HTTP layer (Section 3), which
+   specifies the mechanism for overlaying HTTP request/response pairs on
+   top of the framing layer.  While some of the framing layer concepts
+   are isolated from the HTTP layer, building a generic framing layer
+
+
+
+Belshe & Peon            Expires August 4, 2012                 [Page 4]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   has not been a goal.  The framing layer is tailored to the needs of
+   the HTTP protocol and server push.
+
+1.2.  Definitions
+
+      client: The endpoint initiating the SPDY session.
+
+      connection: A transport-level connection between two endpoints.
+
+      endpoint: Either the client or server of a connection.
+
+      frame: A header-prefixed sequence of bytes sent over a SPDY
+      session.
+
+      server: The endpoint which did not initiate the SPDY session.
+
+      session: A synonym for a connection.
+
+      session error: An error on the SPDY session.
+
+      stream: A bi-directional flow of bytes across a virtual channel
+      within a SPDY session.
+
+      stream error: An error on an individual SPDY stream.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                 [Page 5]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+2.  SPDY Framing Layer
+
+2.1.  Session (Connections)
+
+   The SPDY framing layer (or "session") runs atop a reliable transport
+   layer such as TCP [RFC0793].  The client is the TCP connection
+   initiator.  SPDY connections are persistent connections.
+
+   For best performance, it is expected that clients will not close open
+   connections until the user navigates away from all web pages
+   referencing a connection, or until the server closes the connection.
+   Servers are encouraged to leave connections open for as long as
+   possible, but can terminate idle connections if necessary.  When
+   either endpoint closes the transport-level connection, it MUST first
+   send a GOAWAY (Section 2.6.6) frame so that the endpoints can
+   reliably determine if requests finished before the close.
+
+2.2.  Framing
+
+   Once the connection is established, clients and servers exchange
+   framed messages.  There are two types of frames: control frames
+   (Section 2.2.1) and data frames (Section 2.2.2).  Frames always have
+   a common header which is 8 bytes in length.
+
+   The first bit is a control bit indicating whether a frame is a
+   control frame or data frame.  Control frames carry a version number,
+   a frame type, flags, and a length.  Data frames contain the stream
+   ID, flags, and the length for the payload carried after the common
+   header.  The simple header is designed to make reading and writing of
+   frames easy.
+
+   All integer values, including length, version, and type, are in
+   network byte order.  SPDY does not enforce alignment of types in
+   dynamically sized frames.
+
+2.2.1.  Control frames
+
+   +----------------------------------+
+   |C| Version(15bits) | Type(16bits) |
+   +----------------------------------+
+   | Flags (8)  |  Length (24 bits)   |
+   +----------------------------------+
+   |               Data               |
+   +----------------------------------+
+
+   Control bit: The 'C' bit is a single bit indicating if this is a
+   control message.  For control frames this value is always 1.
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                 [Page 6]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   Version: The version number of the SPDY protocol.  This document
+   describes SPDY version 3.
+
+   Type: The type of control frame.  See Control Frames for the complete
+   list of control frames.
+
+   Flags: Flags related to this frame.  Flags for control frames and
+   data frames are different.
+
+   Length: An unsigned 24-bit value representing the number of bytes
+   after the length field.
+
+   Data: data associated with this control frame.  The format and length
+   of this data is controlled by the control frame type.
+
+   Control frame processing requirements:
+
+      Note that full length control frames (16MB) can be large for
+      implementations running on resource-limited hardware.  In such
+      cases, implementations MAY limit the maximum length frame
+      supported.  However, all implementations MUST be able to receive
+      control frames of at least 8192 octets in length.
+
+2.2.2.  Data frames
+
+   +----------------------------------+
+   |C|       Stream-ID (31bits)       |
+   +----------------------------------+
+   | Flags (8)  |  Length (24 bits)   |
+   +----------------------------------+
+   |               Data               |
+   +----------------------------------+
+
+   Control bit: For data frames this value is always 0.
+
+   Stream-ID: A 31-bit value identifying the stream.
+
+   Flags: Flags related to this frame.  Valid flags are:
+
+      0x01 = FLAG_FIN - signifies that this frame represents the last
+      frame to be transmitted on this stream.  See Stream Close
+      (Section 2.3.7) below.
+
+      0x02 = FLAG_COMPRESS - indicates that the data in this frame has
+      been compressed.
+
+   Length: An unsigned 24-bit value representing the number of bytes
+   after the length field.  The total size of a data frame is 8 bytes +
+
+
+
+Belshe & Peon            Expires August 4, 2012                 [Page 7]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   length.  It is valid to have a zero-length data frame.
+
+   Data: The variable-length data payload; the length was defined in the
+   length field.
+
+   Data frame processing requirements:
+
+      If an endpoint receives a data frame for a stream-id which is not
+      open and the endpoint has not sent a GOAWAY (Section 2.6.6) frame,
+      it MUST send issue a stream error (Section 2.4.2) with the error
+      code INVALID_STREAM for the stream-id.
+
+      If the endpoint which created the stream receives a data frame
+      before receiving a SYN_REPLY on that stream, it is a protocol
+      error, and the recipient MUST issue a stream error (Section 2.4.2)
+      with the status code PROTOCOL_ERROR for the stream-id.
+
+      Implementors note: If an endpoint receives multiple data frames
+      for invalid stream-ids, it MAY close the session.
+
+      All SPDY endpoints MUST accept compressed data frames.
+      Compression of data frames is always done using zlib compression.
+      Each stream initializes and uses its own compression context
+      dedicated to use within that stream.  Endpoints are encouraged to
+      use application level compression rather than SPDY stream level
+      compression.
+
+      Each SPDY stream sending compressed frames creates its own zlib
+      context for that stream, and these compression contexts MUST be
+      distinct from the compression contexts used with SYN_STREAM/
+      SYN_REPLY/HEADER compression.  (Thus, if both endpoints of a
+      stream are compressing data on the stream, there will be two zlib
+      contexts, one for sending and one for receiving).
+
+2.3.  Streams
+
+   Streams are independent sequences of bi-directional data divided into
+   frames with several properties:
+
+      Streams may be created by either the client or server.
+
+      Streams optionally carry a set of name/value header pairs.
+
+      Streams can concurrently send data interleaved with other streams.
+
+      Streams may be cancelled.
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                 [Page 8]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+2.3.1.  Stream frames
+
+   SPDY defines 3 control frames to manage the lifecycle of a stream:
+
+      SYN_STREAM - Open a new stream
+
+      SYN_REPLY - Remote acknowledgement of a new, open stream
+
+      RST_STREAM - Close a stream
+
+2.3.2.  Stream creation
+
+   A stream is created by sending a control frame with the type set to
+   SYN_STREAM (Section 2.6.1).  If the server is initiating the stream,
+   the Stream-ID must be even.  If the client is initiating the stream,
+   the Stream-ID must be odd. 0 is not a valid Stream-ID.  Stream-IDs
+   from each side of the connection must increase monotonically as new
+   streams are created.  E.g.  Stream 2 may be created after stream 3,
+   but stream 7 must not be created after stream 9.  Stream IDs do not
+   wrap: when a client or server cannot create a new stream id without
+   exceeding a 31 bit value, it MUST NOT create a new stream.
+
+   The stream-id MUST increase with each new stream.  If an endpoint
+   receives a SYN_STREAM with a stream id which is less than any
+   previously received SYN_STREAM, it MUST issue a session error
+   (Section 2.4.1) with the status PROTOCOL_ERROR.
+
+   It is a protocol error to send two SYN_STREAMs with the same
+   stream-id.  If a recipient receives a second SYN_STREAM for the same
+   stream, it MUST issue a stream error (Section 2.4.2) with the status
+   code PROTOCOL_ERROR.
+
+   Upon receipt of a SYN_STREAM, the recipient can reject the stream by
+   sending a stream error (Section 2.4.2) with the error code
+   REFUSED_STREAM.  Note, however, that the creating endpoint may have
+   already sent additional frames for that stream which cannot be
+   immediately stopped.
+
+   Once the stream is created, the creator may immediately send HEADERS
+   or DATA frames for that stream, without needing to wait for the
+   recipient to acknowledge.
+
+2.3.2.1.  Unidirectional streams
+
+   When an endpoint creates a stream with the FLAG_UNIDIRECTIONAL flag
+   set, it creates a unidirectional stream which the creating endpoint
+   can use to send frames, but the receiving endpoint cannot.  The
+   receiving endpoint is implicitly already in the half-closed
+
+
+
+Belshe & Peon            Expires August 4, 2012                 [Page 9]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   (Section 2.3.6) state.
+
+2.3.2.2.  Bidirectional streams
+
+   SYN_STREAM frames which do not use the FLAG_UNIDIRECTIONAL flag are
+   bidirectional streams.  Both endpoints can send data on a bi-
+   directional stream.
+
+2.3.3.  Stream priority
+
+   The creator of a stream assigns a priority for that stream.  Priority
+   is represented as an integer from 0 to 7. 0 represents the highest
+   priority and 7 represents the lowest priority.
+
+   The sender and recipient SHOULD use best-effort to process streams in
+   the order of highest priority to lowest priority.
+
+2.3.4.  Stream headers
+
+   Streams carry optional sets of name/value pair headers which carry
+   metadata about the stream.  After the stream has been created, and as
+   long as the sender is not closed (Section 2.3.7) or half-closed
+   (Section 2.3.6), each side may send HEADERS frame(s) containing the
+   header data.  Header data can be sent in multiple HEADERS frames, and
+   HEADERS frames may be interleaved with data frames.
+
+2.3.5.  Stream data exchange
+
+   Once a stream is created, it can be used to send arbitrary amounts of
+   data.  Generally this means that a series of data frames will be sent
+   on the stream until a frame containing the FLAG_FIN flag is set.  The
+   FLAG_FIN can be set on a SYN_STREAM (Section 2.6.1), SYN_REPLY
+   (Section 2.6.2), HEADERS (Section 2.6.7) or a DATA (Section 2.2.2)
+   frame.  Once the FLAG_FIN has been sent, the stream is considered to
+   be half-closed.
+
+2.3.6.  Stream half-close
+
+   When one side of the stream sends a frame with the FLAG_FIN flag set,
+   the stream is half-closed from that endpoint.  The sender of the
+   FLAG_FIN MUST NOT send further frames on that stream.  When both
+   sides have half-closed, the stream is closed.
+
+   If an endpoint receives a data frame after the stream is half-closed
+   from the sender (e.g. the endpoint has already received a prior frame
+   for the stream with the FIN flag set), it MUST send a RST_STREAM to
+   the sender with the status STREAM_ALREADY_CLOSED.
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 10]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+2.3.7.  Stream close
+
+   There are 3 ways that streams can be terminated:
+
+      Normal termination: Normal stream termination occurs when both
+      sender and recipient have half-closed the stream by sending a
+      FLAG_FIN.
+
+      Abrupt termination: Either the client or server can send a
+      RST_STREAM control frame at any time.  A RST_STREAM contains an
+      error code to indicate the reason for failure.  When a RST_STREAM
+      is sent from the stream originator, it indicates a failure to
+      complete the stream and that no further data will be sent on the
+      stream.  When a RST_STREAM is sent from the stream recipient, the
+      sender, upon receipt, should stop sending any data on the stream.
+      The stream recipient should be aware that there is a race between
+      data already in transit from the sender and the time the
+      RST_STREAM is received.  See Stream Error Handling (Section 2.4.2)
+
+      TCP connection teardown: If the TCP connection is torn down while
+      un-closed streams exist, then the endpoint must assume that the
+      stream was abnormally interrupted and may be incomplete.
+
+   If an endpoint receives a data frame after the stream is closed, it
+   must send a RST_STREAM to the sender with the status PROTOCOL_ERROR.
+
+2.4.  Error Handling
+
+   The SPDY framing layer has only two types of errors, and they are
+   always handled consistently.  Any reference in this specification to
+   "issue a session error" refers to Section 2.4.1.  Any reference to
+   "issue a stream error" refers to Section 2.4.2.
+
+2.4.1.  Session Error Handling
+
+   A session error is any error which prevents further processing of the
+   framing layer or which corrupts the session compression state.  When
+   a session error occurs, the endpoint encountering the error MUST
+   first send a GOAWAY (Section 2.6.6) frame with the stream id of most
+   recently received stream from the remote endpoint, and the error code
+   for why the session is terminating.  After sending the GOAWAY frame,
+   the endpoint MUST close the TCP connection.
+
+   Note that the session compression state is dependent upon both
+   endpoints always processing all compressed data.  If an endpoint
+   partially processes a frame containing compressed data without
+   updating compression state properly, future control frames which use
+   compression will be always be errored.  Implementations SHOULD always
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 11]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   try to process compressed data so that errors which could be handled
+   as stream errors do not become session errors.
+
+   Note that because this GOAWAY is sent during a session error case, it
+   is possible that the GOAWAY will not be reliably received by the
+   receiving endpoint.  It is a best-effort attempt to communicate with
+   the remote about why the session is going down.
+
+2.4.2.  Stream Error Handling
+
+   A stream error is an error related to a specific stream-id which does
+   not affect processing of other streams at the framing layer.  Upon a
+   stream error, the endpoint MUST send a RST_STREAM (Section 2.6.3)
+   frame which contains the stream id of the stream where the error
+   occurred and the error status which caused the error.  After sending
+   the RST_STREAM, the stream is closed to the sending endpoint.  After
+   sending the RST_STREAM, if the sender receives any frames other than
+   a RST_STREAM for that stream id, it will result in sending additional
+   RST_STREAM frames.  An endpoint MUST NOT send a RST_STREAM in
+   response to an RST_STREAM, as doing so would lead to RST_STREAM
+   loops.  Sending a RST_STREAM does not cause the SPDY session to be
+   closed.
+
+   If an endpoint has multiple RST_STREAM frames to send in succession
+   for the same stream-id and the same error code, it MAY coalesce them
+   into a single RST_STREAM frame.  (This can happen if a stream is
+   closed, but the remote sends multiple data frames.  There is no
+   reason to send a RST_STREAM for each frame in succession).
+
+2.5.  Data flow
+
+   Because TCP provides a single stream of data on which SPDY
+   multiplexes multiple logical streams, clients and servers must
+   intelligently interleave data messages for concurrent sessions.
+
+2.6.  Control frame types
+
+2.6.1.  SYN_STREAM
+
+   The SYN_STREAM control frame allows the sender to asynchronously
+   create a stream between the endpoints.  See Stream Creation
+   (Section 2.3.2)
+
+
+
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 12]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
++------------------------------------+
+|1|    version    |         1        |
++------------------------------------+
+|  Flags (8)  |  Length (24 bits)    |
++------------------------------------+
+|X|           Stream-ID (31bits)     |
++------------------------------------+
+|X| Associated-To-Stream-ID (31bits) |
++------------------------------------+
+| Pri|Unused | Slot |                |
++-------------------+                |
+| Number of Name/Value pairs (int32) |   <+
++------------------------------------+    |
+|     Length of name (int32)         |    | This section is the "Name/Value
++------------------------------------+    | Header Block", and is compressed.
+|           Name (string)            |    |
++------------------------------------+    |
+|     Length of value  (int32)       |    |
++------------------------------------+    |
+|          Value   (string)          |    |
++------------------------------------+    |
+|           (repeats)                |   <+
+
+   Flags: Flags related to this frame.  Valid flags are:
+
+      0x01 = FLAG_FIN - marks this frame as the last frame to be
+      transmitted on this stream and puts the sender in the half-closed
+      (Section 2.3.6) state.
+
+      0x02 = FLAG_UNIDIRECTIONAL - a stream created with this flag puts
+      the recipient in the half-closed (Section 2.3.6) state.
+
+   Length: The length is the number of bytes which follow the length
+   field in the frame.  For SYN_STREAM frames, this is 10 bytes plus the
+   length of the compressed Name/Value block.
+
+   Stream-ID: The 31-bit identifier for this stream.  This stream-id
+   will be used in frames which are part of this stream.
+
+   Associated-To-Stream-ID: The 31-bit identifier for a stream which
+   this stream is associated to.  If this stream is independent of all
+   other streams, it should be 0.
+
+   Priority: A 3-bit priority (Section 2.3.3) field.
+
+   Unused: 5 bits of unused space, reserved for future use.
+
+   Slot: An 8 bit unsigned integer specifying the index in the server's
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 13]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   CREDENTIAL vector of the client certificate to be used for this
+   request. see CREDENTIAL frame (Section 2.6.9).  The value 0 means no
+   client certificate should be associated with this stream.
+
+   Name/Value Header Block: A set of name/value pairs carried as part of
+   the SYN_STREAM. see Name/Value Header Block (Section 2.6.10).
+
+   If an endpoint receives a SYN_STREAM which is larger than the
+   implementation supports, it MAY send a RST_STREAM with error code
+   FRAME_TOO_LARGE.  All implementations MUST support the minimum size
+   limits defined in the Control Frames section (Section 2.2.1).
+
+2.6.2.  SYN_REPLY
+
+   SYN_REPLY indicates the acceptance of a stream creation by the
+   recipient of a SYN_STREAM frame.
+
++------------------------------------+
+|1|    version    |         2        |
++------------------------------------+
+|  Flags (8)  |  Length (24 bits)    |
++------------------------------------+
+|X|           Stream-ID (31bits)     |
++------------------------------------+
+| Number of Name/Value pairs (int32) |   <+
++------------------------------------+    |
+|     Length of name (int32)         |    | This section is the "Name/Value
++------------------------------------+    | Header Block", and is compressed.
+|           Name (string)            |    |
++------------------------------------+    |
+|     Length of value  (int32)       |    |
++------------------------------------+    |
+|          Value   (string)          |    |
++------------------------------------+    |
+|           (repeats)                |   <+
+
+   Flags: Flags related to this frame.  Valid flags are:
+
+      0x01 = FLAG_FIN - marks this frame as the last frame to be
+      transmitted on this stream and puts the sender in the half-closed
+      (Section 2.3.6) state.
+
+   Length: The length is the number of bytes which follow the length
+   field in the frame.  For SYN_REPLY frames, this is 4 bytes plus the
+   length of the compressed Name/Value block.
+
+   Stream-ID: The 31-bit identifier for this stream.
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 14]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   If an endpoint receives multiple SYN_REPLY frames for the same active
+   stream ID, it MUST issue a stream error (Section 2.4.2) with the
+   error code STREAM_IN_USE.
+
+   Name/Value Header Block: A set of name/value pairs carried as part of
+   the SYN_STREAM. see Name/Value Header Block (Section 2.6.10).
+
+   If an endpoint receives a SYN_REPLY which is larger than the
+   implementation supports, it MAY send a RST_STREAM with error code
+   FRAME_TOO_LARGE.  All implementations MUST support the minimum size
+   limits defined in the Control Frames section (Section 2.2.1).
+
+2.6.3.  RST_STREAM
+
+   The RST_STREAM frame allows for abnormal termination of a stream.
+   When sent by the creator of a stream, it indicates the creator wishes
+   to cancel the stream.  When sent by the recipient of a stream, it
+   indicates an error or that the recipient did not want to accept the
+   stream, so the stream should be closed.
+
+   +----------------------------------+
+   |1|   version    |         3       |
+   +----------------------------------+
+   | Flags (8)  |         8           |
+   +----------------------------------+
+   |X|          Stream-ID (31bits)    |
+   +----------------------------------+
+   |          Status code             |
+   +----------------------------------+
+
+   Flags: Flags related to this frame.  RST_STREAM does not define any
+   flags.  This value must be 0.
+
+   Length: An unsigned 24-bit value representing the number of bytes
+   after the length field.  For RST_STREAM control frames, this value is
+   always 8.
+
+   Stream-ID: The 31-bit identifier for this stream.
+
+   Status code: (32 bits) An indicator for why the stream is being
+   terminated.The following status codes are defined:
+
+      1 - PROTOCOL_ERROR.  This is a generic error, and should only be
+      used if a more specific error is not available.
+
+      2 - INVALID_STREAM.  This is returned when a frame is received for
+      a stream which is not active.
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 15]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+      3 - REFUSED_STREAM.  Indicates that the stream was refused before
+      any processing has been done on the stream.
+
+      4 - UNSUPPORTED_VERSION.  Indicates that the recipient of a stream
+      does not support the SPDY version requested.
+
+      5 - CANCEL.  Used by the creator of a stream to indicate that the
+      stream is no longer needed.
+
+      6 - INTERNAL_ERROR.  This is a generic error which can be used
+      when the implementation has internally failed, not due to anything
+      in the protocol.
+
+      7 - FLOW_CONTROL_ERROR.  The endpoint detected that its peer
+      violated the flow control protocol.
+
+      8 - STREAM_IN_USE.  The endpoint received a SYN_REPLY for a stream
+      already open.
+
+      9 - STREAM_ALREADY_CLOSED.  The endpoint received a data or
+      SYN_REPLY frame for a stream which is half closed.
+
+      10 - INVALID_CREDENTIALS.  The server received a request for a
+      resource whose origin does not have valid credentials in the
+      client certificate vector.
+
+      11 - FRAME_TOO_LARGE.  The endpoint received a frame which this
+      implementation could not support.  If FRAME_TOO_LARGE is sent for
+      a SYN_STREAM, HEADERS, or SYN_REPLY frame without fully processing
+      the compressed portion of those frames, then the compression state
+      will be out-of-sync with the other endpoint.  In this case,
+      senders of FRAME_TOO_LARGE MUST close the session.
+
+      Note: 0 is not a valid status code for a RST_STREAM.
+
+   After receiving a RST_STREAM on a stream, the recipient must not send
+   additional frames for that stream, and the stream moves into the
+   closed state.
+
+2.6.4.  SETTINGS
+
+   A SETTINGS frame contains a set of id/value pairs for communicating
+   configuration data about how the two endpoints may communicate.
+   SETTINGS frames can be sent at any time by either endpoint, are
+   optionally sent, and are fully asynchronous.  When the server is the
+   sender, the sender can request that configuration data be persisted
+   by the client across SPDY sessions and returned to the server in
+   future communications.
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 16]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   Persistence of SETTINGS ID/Value pairs is done on a per origin/IP
+   pair (the "origin" is the set of scheme, host, and port from the URI.
+   See [RFC6454]).  That is, when a client connects to a server, and the
+   server persists settings within the client, the client SHOULD return
+   the persisted settings on future connections to the same origin AND
+   IP address and TCP port.  Clients MUST NOT request servers to use the
+   persistence features of the SETTINGS frames, and servers MUST ignore
+   persistence related flags sent by a client.
+
+   +----------------------------------+
+   |1|   version    |         4       |
+   +----------------------------------+
+   | Flags (8)  |  Length (24 bits)   |
+   +----------------------------------+
+   |         Number of entries        |
+   +----------------------------------+
+   |          ID/Value Pairs          |
+   |             ...                  |
+
+   Control bit: The control bit is always 1 for this message.
+
+   Version: The SPDY version number.
+
+   Type: The message type for a SETTINGS message is 4.
+
+   Flags: FLAG_SETTINGS_CLEAR_SETTINGS (0x1): When set, the client
+   should clear any previously persisted SETTINGS ID/Value pairs.  If
+   this frame contains ID/Value pairs with the
+   FLAG_SETTINGS_PERSIST_VALUE set, then the client will first clear its
+   existing, persisted settings, and then persist the values with the
+   flag set which are contained within this frame.  Because persistence
+   is only implemented on the client, this flag can only be used when
+   the sender is the server.
+
+   Length: An unsigned 24-bit value representing the number of bytes
+   after the length field.  The total size of a SETTINGS frame is 8
+   bytes + length.
+
+   Number of entries: A 32-bit value representing the number of ID/value
+   pairs in this message.
+
+   ID: A 32-bit ID number, comprised of 8 bits of flags and 24 bits of
+   unique ID.
+
+      ID.flags:
+
+         FLAG_SETTINGS_PERSIST_VALUE (0x1): When set, the sender of this
+         SETTINGS frame is requesting that the recipient persist the ID/
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 17]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+         Value and return it in future SETTINGS frames sent from the
+         sender to this recipient.  Because persistence is only
+         implemented on the client, this flag is only sent by the
+         server.
+
+         FLAG_SETTINGS_PERSISTED (0x2): When set, the sender is
+         notifying the recipient that this ID/Value pair was previously
+         sent to the sender by the recipient with the
+         FLAG_SETTINGS_PERSIST_VALUE, and the sender is returning it.
+         Because persistence is only implemented on the client, this
+         flag is only sent by the client.
+
+      Defined IDs:
+
+         1 - SETTINGS_UPLOAD_BANDWIDTH allows the sender to send its
+         expected upload bandwidth on this channel.  This number is an
+         estimate.  The value should be the integral number of kilobytes
+         per second that the sender predicts as an expected maximum
+         upload channel capacity.
+
+         2 - SETTINGS_DOWNLOAD_BANDWIDTH allows the sender to send its
+         expected download bandwidth on this channel.  This number is an
+         estimate.  The value should be the integral number of kilobytes
+         per second that the sender predicts as an expected maximum
+         download channel capacity.
+
+         3 - SETTINGS_ROUND_TRIP_TIME allows the sender to send its
+         expected round-trip-time on this channel.  The round trip time
+         is defined as the minimum amount of time to send a control
+         frame from this client to the remote and receive a response.
+         The value is represented in milliseconds.
+
+         4 - SETTINGS_MAX_CONCURRENT_STREAMS allows the sender to inform
+         the remote endpoint the maximum number of concurrent streams
+         which it will allow.  By default there is no limit.  For
+         implementors it is recommended that this value be no smaller
+         than 100.
+
+         5 - SETTINGS_CURRENT_CWND allows the sender to inform the
+         remote endpoint of the current TCP CWND value.
+
+         6 - SETTINGS_DOWNLOAD_RETRANS_RATE allows the sender to inform
+         the remote endpoint the retransmission rate (bytes
+         retransmitted / total bytes transmitted).
+
+         7 - SETTINGS_INITIAL_WINDOW_SIZE allows the sender to inform
+         the remote endpoint the initial window size (in bytes) for new
+         streams.
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 18]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+         8 - SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE allows the server
+         to inform the client if the new size of the client certificate
+         vector.
+
+   Value: A 32-bit value.
+
+   The message is intentionally extensible for future information which
+   may improve client-server communications.  The sender does not need
+   to send every type of ID/value.  It must only send those for which it
+   has accurate values to convey.  When multiple ID/value pairs are
+   sent, they should be sent in order of lowest id to highest id.  A
+   single SETTINGS frame MUST not contain multiple values for the same
+   ID.  If the recipient of a SETTINGS frame discovers multiple values
+   for the same ID, it MUST ignore all values except the first one.
+
+   A server may send multiple SETTINGS frames containing different ID/
+   Value pairs.  When the same ID/Value is sent twice, the most recent
+   value overrides any previously sent values.  If the server sends IDs
+   1, 2, and 3 with the FLAG_SETTINGS_PERSIST_VALUE in a first SETTINGS
+   frame, and then sends IDs 4 and 5 with the
+   FLAG_SETTINGS_PERSIST_VALUE, when the client returns the persisted
+   state on its next SETTINGS frame, it SHOULD send all 5 settings (1,
+   2, 3, 4, and 5 in this example) to the server.
+
+2.6.5.  PING
+
+   The PING control frame is a mechanism for measuring a minimal round-
+   trip time from the sender.  It can be sent from the client or the
+   server.  Recipients of a PING frame should send an identical frame to
+   the sender as soon as possible (if there is other pending data
+   waiting to be sent, PING should take highest priority).  Each ping
+   sent by a sender should use a unique ID.
+
+   +----------------------------------+
+   |1|   version    |         6       |
+   +----------------------------------+
+   | 0 (flags) |     4 (length)       |
+   +----------------------------------|
+   |            32-bit ID             |
+   +----------------------------------+
+
+   Control bit: The control bit is always 1 for this message.
+
+   Version: The SPDY version number.
+
+   Type: The message type for a PING message is 6.
+
+   Length: This frame is always 4 bytes long.
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 19]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   ID: A unique ID for this ping, represented as an unsigned 32 bit
+   value.  When the client initiates a ping, it must use an odd numbered
+   ID.  When the server initiates a ping, it must use an even numbered
+   ping.  Use of odd/even IDs is required in order to avoid accidental
+   looping on PINGs (where each side initiates an identical PING at the
+   same time).
+
+   Note: If a sender uses all possible PING ids (e.g. has sent all 2^31
+   possible IDs), it can wrap and start re-using IDs.
+
+   If a server receives an even numbered PING which it did not initiate,
+   it must ignore the PING.  If a client receives an odd numbered PING
+   which it did not initiate, it must ignore the PING.
+
+2.6.6.  GOAWAY
+
+   The GOAWAY control frame is a mechanism to tell the remote side of
+   the connection to stop creating streams on this session.  It can be
+   sent from the client or the server.  Once sent, the sender will not
+   respond to any new SYN_STREAMs on this session.  Recipients of a
+   GOAWAY frame must not send additional streams on this session,
+   although a new session can be established for new streams.  The
+   purpose of this message is to allow an endpoint to gracefully stop
+   accepting new streams (perhaps for a reboot or maintenance), while
+   still finishing processing of previously established streams.
+
+   There is an inherent race condition between an endpoint sending
+   SYN_STREAMs and the remote sending a GOAWAY message.  To deal with
+   this case, the GOAWAY contains a last-stream-id indicating the
+   stream-id of the last stream which was created on the sending
+   endpoint in this session.  If the receiver of the GOAWAY sent new
+   SYN_STREAMs for sessions after this last-stream-id, they were not
+   processed by the server and the receiver may treat the stream as
+   though it had never been created at all (hence the receiver may want
+   to re-create the stream later on a new session).
+
+   Endpoints should always send a GOAWAY message before closing a
+   connection so that the remote can know whether a stream has been
+   partially processed or not.  (For example, if an HTTP client sends a
+   POST at the same time that a server closes a connection, the client
+   cannot know if the server started to process that POST request if the
+   server does not send a GOAWAY frame to indicate where it stopped
+   working).
+
+   After sending a GOAWAY message, the sender must ignore all SYN_STREAM
+   frames for new streams.
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 20]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   +----------------------------------+
+   |1|   version    |         7       |
+   +----------------------------------+
+   | 0 (flags) |     8 (length)       |
+   +----------------------------------|
+   |X|  Last-good-stream-ID (31 bits) |
+   +----------------------------------+
+   |          Status code             |
+   +----------------------------------+
+
+   Control bit: The control bit is always 1 for this message.
+
+   Version: The SPDY version number.
+
+   Type: The message type for a GOAWAY message is 7.
+
+   Length: This frame is always 8 bytes long.
+
+   Last-good-stream-Id: The last stream id which was replied to (with
+   either a SYN_REPLY or RST_STREAM) by the sender of the GOAWAY
+   message.  If no streams were replied to, this value MUST be 0.
+
+   Status: The reason for closing the session.
+
+      0 - OK.  This is a normal session teardown.
+
+      1 - PROTOCOL_ERROR.  This is a generic error, and should only be
+      used if a more specific error is not available.
+
+      11 - INTERNAL_ERROR.  This is a generic error which can be used
+      when the implementation has internally failed, not due to anything
+      in the protocol.
+
+2.6.7.  HEADERS
+
+   The HEADERS frame augments a stream with additional headers.  It may
+   be optionally sent on an existing stream at any time.  Specific
+   application of the headers in this frame is application-dependent.
+   The name/value header block within this frame is compressed.
+
+
+
+
+
+
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 21]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
++------------------------------------+
+|1|   version     |          8       |
++------------------------------------+
+| Flags (8)  |   Length (24 bits)    |
++------------------------------------+
+|X|          Stream-ID (31bits)      |
++------------------------------------+
+| Number of Name/Value pairs (int32) |   <+
++------------------------------------+    |
+|     Length of name (int32)         |    | This section is the "Name/Value
++------------------------------------+    | Header Block", and is compressed.
+|           Name (string)            |    |
++------------------------------------+    |
+|     Length of value  (int32)       |    |
++------------------------------------+    |
+|          Value   (string)          |    |
++------------------------------------+    |
+|           (repeats)                |   <+
+
+   Flags: Flags related to this frame.  Valid flags are:
+
+      0x01 = FLAG_FIN - marks this frame as the last frame to be
+      transmitted on this stream and puts the sender in the half-closed
+      (Section 2.3.6) state.
+
+   Length: An unsigned 24 bit value representing the number of bytes
+   after the length field.  The minimum length of the length field is 4
+   (when the number of name value pairs is 0).
+
+   Stream-ID: The stream this HEADERS block is associated with.
+
+   Name/Value Header Block: A set of name/value pairs carried as part of
+   the SYN_STREAM. see Name/Value Header Block (Section 2.6.10).
+
+2.6.8.  WINDOW_UPDATE
+
+   The WINDOW_UPDATE control frame is used to implement per stream flow
+   control in SPDY.  Flow control in SPDY is per hop, that is, only
+   between the two endpoints of a SPDY connection.  If there are one or
+   more intermediaries between the client and the origin server, flow
+   control signals are not explicitly forwarded by the intermediaries.
+   (However, throttling of data transfer by any recipient may have the
+   effect of indirectly propagating flow control information upstream
+   back to the original sender.)  Flow control only applies to the data
+   portion of data frames.  Recipients must buffer all control frames.
+   If a recipient fails to buffer an entire control frame, it MUST issue
+   a stream error (Section 2.4.2) with the status code
+   FLOW_CONTROL_ERROR for the stream.
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 22]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   Flow control in SPDY is implemented by a data transfer window kept by
+   the sender of each stream.  The data transfer window is a simple
+   uint32 that indicates how many bytes of data the sender can transmit.
+   After a stream is created, but before any data frames have been
+   transmitted, the sender begins with the initial window size.  This
+   window size is a measure of the buffering capability of the
+   recipient.  The sender must not send a data frame with data length
+   greater than the transfer window size.  After sending each data
+   frame, the sender decrements its transfer window size by the amount
+   of data transmitted.  When the window size becomes less than or equal
+   to 0, the sender must pause transmitting data frames.  At the other
+   end of the stream, the recipient sends a WINDOW_UPDATE control back
+   to notify the sender that it has consumed some data and freed up
+   buffer space to receive more data.
+
+   +----------------------------------+
+   |1|   version    |         9       |
+   +----------------------------------+
+   | 0 (flags) |     8 (length)       |
+   +----------------------------------+
+   |X|     Stream-ID (31-bits)        |
+   +----------------------------------+
+   |X|  Delta-Window-Size (31-bits)   |
+   +----------------------------------+
+
+   Control bit: The control bit is always 1 for this message.
+
+   Version: The SPDY version number.
+
+   Type: The message type for a WINDOW_UPDATE message is 9.
+
+   Length: The length field is always 8 for this frame (there are 8
+   bytes after the length field).
+
+   Stream-ID: The stream ID that this WINDOW_UPDATE control frame is
+   for.
+
+   Delta-Window-Size: The additional number of bytes that the sender can
+   transmit in addition to existing remaining window size.  The legal
+   range for this field is 1 to 2^31 - 1 (0x7fffffff) bytes.
+
+   The window size as kept by the sender must never exceed 2^31
+   (although it can become negative in one special case).  If a sender
+   receives a WINDOW_UPDATE that causes the its window size to exceed
+   this limit, it must send RST_STREAM with status code
+   FLOW_CONTROL_ERROR to terminate the stream.
+
+   When a SPDY connection is first established, the default initial
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 23]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   window size for all streams is 64KB.  An endpoint can use the
+   SETTINGS control frame to adjust the initial window size for the
+   connection.  That is, its peer can start out using the 64KB default
+   initial window size when sending data frames before receiving the
+   SETTINGS.  Because SETTINGS is asynchronous, there may be a race
+   condition if the recipient wants to decrease the initial window size,
+   but its peer immediately sends 64KB on the creation of a new
+   connection, before waiting for the SETTINGS to arrive.  This is one
+   case where the window size kept by the sender will become negative.
+   Once the sender detects this condition, it must stop sending data
+   frames and wait for the recipient to catch up.  The recipient has two
+   choices:
+
+      immediately send RST_STREAM with FLOW_CONTROL_ERROR status code.
+
+      allow the head of line blocking (as there is only one stream for
+      the session and the amount of data in flight is bounded by the
+      default initial window size), and send WINDOW_UPDATE as it
+      consumes data.
+
+   In the case of option 2, both sides must compute the window size
+   based on the initial window size in the SETTINGS.  For example, if
+   the recipient sets the initial window size to be 16KB, and the sender
+   sends 64KB immediately on connection establishment, the sender will
+   discover its window size is -48KB on receipt of the SETTINGS.  As the
+   recipient consumes the first 16KB, it must send a WINDOW_UPDATE of
+   16KB back to the sender.  This interaction continues until the
+   sender's window size becomes positive again, and it can resume
+   transmitting data frames.
+
+   After the recipient reads in a data frame with FLAG_FIN that marks
+   the end of the data stream, it should not send WINDOW_UPDATE frames
+   as it consumes the last data frame.  A sender should ignore all the
+   WINDOW_UPDATE frames associated with the stream after it send the
+   last frame for the stream.
+
+   The data frames from the sender and the WINDOW_UPDATE frames from the
+   recipient are completely asynchronous with respect to each other.
+   This property allows a recipient to aggressively update the window
+   size kept by the sender to prevent the stream from stalling.
+
+2.6.9.  CREDENTIAL
+
+   The CREDENTIAL control frame is used by the client to send additional
+   client certificates to the server.  A SPDY client may decide to send
+   requests for resources from different origins on the same SPDY
+   session if it decides that that server handles both origins.  For
+   example if the IP address associated with both hostnames matches and
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 24]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   the SSL server certificate presented in the initial handshake is
+   valid for both hostnames.  However, because the SSL connection can
+   contain at most one client certificate, the client needs a mechanism
+   to send additional client certificates to the server.
+
+   The server is required to maintain a vector of client certificates
+   associated with a SPDY session.  When the client needs to send a
+   client certificate to the server, it will send a CREDENTIAL frame
+   that specifies the index of the slot in which to store the
+   certificate as well as proof that the client posesses the
+   corresponding private key.  The initial size of this vector must be
+   8.  If the client provides a client certificate during the first TLS
+   handshake, the contents of this certificate must be copied into the
+   first slot (index 1) in the CREDENTIAL vector, though it may be
+   overwritten by subsequent CREDENTIAL frames.  The server must
+   exclusively use the CREDNETIAL vector when evaluating the client
+   certificates associated with an origin.  The server may change the
+   size of this vector by sending a SETTINGS frame with the setting
+   SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE value specified.  In the
+   event that the new size is smaller than the current size, truncation
+   occurs preserving lower-index slots as possible.
+
+   TLS renegotiation with client authentication is incompatible with
+   SPDY given the multiplexed nature of SPDY.  Specifically, imagine
+   that the client has 2 requests outstanding to the server for two
+   different pages (in different tabs).  When the renegotiation + client
+   certificate request comes in, the browser is unable to determine
+   which resource triggered the client certificate request, in order to
+   prompt the user accordingly.
+
+   +----------------------------------+
+   |1|000000000000001|0000000000001011|
+   +----------------------------------+
+   | flags (8)  |  Length (24 bits)   |
+   +----------------------------------+
+   |  Slot (16 bits) |                |
+   +-----------------+                |
+   |      Proof Length (32 bits)      |
+   +----------------------------------+
+   |               Proof              |
+   +----------------------------------+ <+
+   |   Certificate Length (32 bits)   |  |
+   +----------------------------------+  | Repeated until end of frame
+   |            Certificate           |  |
+   +----------------------------------+ <+
+
+   Slot: The index in the server's client certificate vector where this
+   certificate should be stored.  If there is already a certificate
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 25]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   stored at this index, it will be overwritten.  The index is one
+   based, not zero based; zero is an invalid slot index.
+
+   Proof: Cryptographic proof that the client has possession of the
+   private key associated with the certificate.  The format is a TLS
+   digitally-signed element
+   (http://tools.ietf.org/html/rfc5246#section-4.7).  The signature
+   algorithm must be the same as that used in the CertificateVerify
+   message.  However, since the MD5+SHA1 signature type used in TLS 1.0
+   connections can not be correctly encoded in a digitally-signed
+   element, SHA1 must be used when MD5+SHA1 was used in the SSL
+   connection.  The signature is calculated over a 32 byte TLS extractor
+   value (http://tools.ietf.org/html/rfc5705) with a label of "EXPORTER
+   SPDY certificate proof" using the empty string as context.  ForRSA
+   certificates the signature would be a PKCS#1 v1.5 signature.  For
+   ECDSA, it would be an ECDSA-Sig-Value
+   (http://tools.ietf.org/html/rfc5480#appendix-A).  For a 1024-bit RSA
+   key, the CREDENTIAL message would be ~500 bytes.
+
+   Certificate: The certificate chain, starting with the leaf
+   certificate.  Each certificate must be encoded as a 32 bit length,
+   followed by a DER encoded certificate.  The certificate must be of
+   the same type (RSA, ECDSA, etc) as the client certificate associated
+   with the SSL connection.
+
+   If the server receives a request for a resource with unacceptable
+   credential (either missing or invalid), it must reply with a
+   RST_STREAM frame with the status code INVALID_CREDENTIALS.  Upon
+   receipt of a RST_STREAM frame with INVALID_CREDENTIALS, the client
+   should initiate a new stream directly to the requested origin and
+   resend the request.  Note, SPDY does not allow the server to request
+   different client authentication for different resources in the same
+   origin.
+
+   If the server receives an invalid CREDENTIAL frame, it MUST respond
+   with a GOAWAY frame and shutdown the session.
+
+2.6.10.  Name/Value Header Block
+
+   The Name/Value Header Block is found in the SYN_STREAM, SYN_REPLY and
+   HEADERS control frames, and shares a common format:
+
+
+
+
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 26]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   +------------------------------------+
+   | Number of Name/Value pairs (int32) |
+   +------------------------------------+
+   |     Length of name (int32)         |
+   +------------------------------------+
+   |           Name (string)            |
+   +------------------------------------+
+   |     Length of value  (int32)       |
+   +------------------------------------+
+   |          Value   (string)          |
+   +------------------------------------+
+   |           (repeats)                |
+
+   Number of Name/Value pairs: The number of repeating name/value pairs
+   following this field.
+
+   List of Name/Value pairs:
+
+      Length of Name: a 32-bit value containing the number of octets in
+      the name field.  Note that in practice, this length must not
+      exceed 2^24, as that is the maximum size of a SPDY frame.
+
+      Name: 0 or more octets, 8-bit sequences of data, excluding 0.
+
+      Length of Value: a 32-bit value containing the number of octets in
+      the value field.  Note that in practice, this length must not
+      exceed 2^24, as that is the maximum size of a SPDY frame.
+
+      Value: 0 or more octets, 8-bit sequences of data, excluding 0.
+
+   Each header name must have at least one value.  Header names are
+   encoded using the US-ASCII character set [ASCII] and must be all
+   lower case.  The length of each name must be greater than zero.  A
+   recipient of a zero-length name MUST issue a stream error
+   (Section 2.4.2) with the status code PROTOCOL_ERROR for the
+   stream-id.
+
+   Duplicate header names are not allowed.  To send two identically
+   named headers, send a header with two values, where the values are
+   separated by a single NUL (0) byte.  A header value can either be
+   empty (e.g. the length is zero) or it can contain multiple, NUL-
+   separated values, each with length greater than zero.  The value
+   never starts nor ends with a NUL character.  Recipients of illegal
+   value fields MUST issue a stream error (Section 2.4.2) with the
+   status code PROTOCOL_ERROR for the stream-id.
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 27]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+2.6.10.1.  Compression
+
+   The Name/Value Header Block is a section of the SYN_STREAM,
+   SYN_REPLY, and HEADERS frames used to carry header meta-data.  This
+   block is always compressed using zlib compression.  Within this
+   specification, any reference to 'zlib' is referring to the ZLIB
+   Compressed Data Format Specification Version 3.3 as part of RFC1950.
+   [RFC1950]
+
+   For each HEADERS compression instance, the initial state is
+   initialized using the following dictionary [UDELCOMPRESSION]:
+
+   const unsigned char SPDY_dictionary_txt[] = {
+           0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69,   \\ - - - - o p t i
+           0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68,   \\ o n s - - - - h
+           0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70,   \\ e a d - - - - p
+           0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70,   \\ o s t - - - - p
+           0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65,   \\ u t - - - - d e
+           0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05,   \\ l e t e - - - -
+           0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00,   \\ t r a c e - - -
+           0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00,   \\ - a c c e p t -
+           0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70,   \\ - - - a c c e p
+           0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,   \\ t - c h a r s e
+           0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63,   \\ t - - - - a c c
+           0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,   \\ e p t - e n c o
+           0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f,   \\ d i n g - - - -
+           0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c,   \\ a c c e p t - l
+           0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00,   \\ a n g u a g e -
+           0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70,   \\ - - - a c c e p
+           0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73,   \\ t - r a n g e s
+           0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00,   \\ - - - - a g e -
+           0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77,   \\ - - - a l l o w
+           0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68,   \\ - - - - a u t h
+           0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,   \\ o r i z a t i o
+           0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63,   \\ n - - - - c a c
+           0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72,   \\ h e - c o n t r
+           0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f,   \\ o l - - - - c o
+           0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,   \\ n n e c t i o n
+           0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74,   \\ - - - - c o n t
+           0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65,   \\ e n t - b a s e
+           0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74,   \\ - - - - c o n t
+           0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,   \\ e n t - e n c o
+           0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10,   \\ d i n g - - - -
+           0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,   \\ c o n t e n t -
+           0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65,   \\ l a n g u a g e
+           0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74,   \\ - - - - c o n t
+           0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67,   \\ e n t - l e n g
+           0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f,   \\ t h - - - - c o
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 28]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+           0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f,   \\ n t e n t - l o
+           0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00,   \\ c a t i o n - -
+           0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,   \\ - - c o n t e n
+           0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00,   \\ t - m d 5 - - -
+           0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,   \\ - c o n t e n t
+           0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00,   \\ - r a n g e - -
+           0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,   \\ - - c o n t e n
+           0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00,   \\ t - t y p e - -
+           0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00,   \\ - - d a t e - -
+           0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00,   \\ - - e t a g - -
+           0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74,   \\ - - e x p e c t
+           0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69,   \\ - - - - e x p i
+           0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66,   \\ r e s - - - - f
+           0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68,   \\ r o m - - - - h
+           0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69,   \\ o s t - - - - i
+           0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00,   \\ f - m a t c h -
+           0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f,   \\ - - - i f - m o
+           0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73,   \\ d i f i e d - s
+           0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d,   \\ i n c e - - - -
+           0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d,   \\ i f - n o n e -
+           0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00,   \\ m a t c h - - -
+           0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67,   \\ - i f - r a n g
+           0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d,   \\ e - - - - i f -
+           0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69,   \\ u n m o d i f i
+           0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65,   \\ e d - s i n c e
+           0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74,   \\ - - - - l a s t
+           0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65,   \\ - m o d i f i e
+           0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63,   \\ d - - - - l o c
+           0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00,   \\ a t i o n - - -
+           0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72,   \\ - m a x - f o r
+           0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00,   \\ w a r d s - - -
+           0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00,   \\ - p r a g m a -
+           0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79,   \\ - - - p r o x y
+           0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,   \\ - a u t h e n t
+           0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00,   \\ i c a t e - - -
+           0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61,   \\ - p r o x y - a
+           0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,   \\ u t h o r i z a
+           0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05,   \\ t i o n - - - -
+           0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00,   \\ r a n g e - - -
+           0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72,   \\ - r e f e r e r
+           0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72,   \\ - - - - r e t r
+           0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00,   \\ y - a f t e r -
+           0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65,   \\ - - - s e r v e
+           0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00,   \\ r - - - - t e -
+           0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c,   \\ - - - t r a i l
+           0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72,   \\ e r - - - - t r
+           0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65,   \\ a n s f e r - e
+           0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00,   \\ n c o d i n g -
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 29]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+           0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61,   \\ - - - u p g r a
+           0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73,   \\ d e - - - - u s
+           0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74,   \\ e r - a g e n t
+           0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79,   \\ - - - - v a r y
+           0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00,   \\ - - - - v i a -
+           0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69,   \\ - - - w a r n i
+           0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77,   \\ n g - - - - w w
+           0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e,   \\ w - a u t h e n
+           0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00,   \\ t i c a t e - -
+           0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,   \\ - - m e t h o d
+           0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00,   \\ - - - - g e t -
+           0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,   \\ - - - s t a t u
+           0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30,   \\ s - - - - 2 0 0
+           0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76,   \\ - O K - - - - v
+           0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00,   \\ e r s i o n - -
+           0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31,   \\ - - H T T P - 1
+           0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72,   \\ - 1 - - - - u r
+           0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62,   \\ l - - - - p u b
+           0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73,   \\ l i c - - - - s
+           0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69,   \\ e t - c o o k i
+           0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65,   \\ e - - - - k e e
+           0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00,   \\ p - a l i v e -
+           0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69,   \\ - - - o r i g i
+           0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32,   \\ n 1 0 0 1 0 1 2
+           0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35,   \\ 0 1 2 0 2 2 0 5
+           0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30,   \\ 2 0 6 3 0 0 3 0
+           0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33,   \\ 2 3 0 3 3 0 4 3
+           0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37,   \\ 0 5 3 0 6 3 0 7
+           0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30,   \\ 4 0 2 4 0 5 4 0
+           0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34,   \\ 6 4 0 7 4 0 8 4
+           0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31,   \\ 0 9 4 1 0 4 1 1
+           0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31,   \\ 4 1 2 4 1 3 4 1
+           0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34,   \\ 4 4 1 5 4 1 6 4
+           0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34,   \\ 1 7 5 0 2 5 0 4
+           0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e,   \\ 5 0 5 2 0 3 - N
+           0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f,   \\ o n - A u t h o
+           0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65,   \\ r i t a t i v e
+           0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61,   \\ - I n f o r m a
+           0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20,   \\ t i o n 2 0 4 -
+           0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65,   \\ N o - C o n t e
+           0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f,   \\ n t 3 0 1 - M o
+           0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d,   \\ v e d - P e r m
+           0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34,   \\ a n e n t l y 4
+           0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52,   \\ 0 0 - B a d - R
+           0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30,   \\ e q u e s t 4 0
+           0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68,   \\ 1 - U n a u t h
+           0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30,   \\ o r i z e d 4 0
+           0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64,   \\ 3 - F o r b i d
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 30]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+           0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e,   \\ d e n 4 0 4 - N
+           0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64,   \\ o t - F o u n d
+           0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65,   \\ 5 0 0 - I n t e
+           0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72,   \\ r n a l - S e r
+           0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f,   \\ v e r - E r r o
+           0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74,   \\ r 5 0 1 - N o t
+           0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65,   \\ - I m p l e m e
+           0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20,   \\ n t e d 5 0 3 -
+           0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20,   \\ S e r v i c e -
+           0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61,   \\ U n a v a i l a
+           0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46,   \\ b l e J a n - F
+           0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41,   \\ e b - M a r - A
+           0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a,   \\ p r - M a y - J
+           0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41,   \\ u n - J u l - A
+           0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20,   \\ u g - S e p t -
+           0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20,   \\ O c t - N o v -
+           0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30,   \\ D e c - 0 0 - 0
+           0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e,   \\ 0 - 0 0 - M o n
+           0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57,   \\ - - T u e - - W
+           0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c,   \\ e d - - T h u -
+           0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61,   \\ - F r i - - S a
+           0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20,   \\ t - - S u n - -
+           0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b,   \\ G M T c h u n k
+           0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f,   \\ e d - t e x t -
+           0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61,   \\ h t m l - i m a
+           0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69,   \\ g e - p n g - i
+           0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67,   \\ m a g e - j p g
+           0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67,   \\ - i m a g e - g
+           0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,   \\ i f - a p p l i
+           0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,   \\ c a t i o n - x
+           0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,   \\ m l - a p p l i
+           0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,   \\ c a t i o n - x
+           0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c,   \\ h t m l - x m l
+           0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c,   \\ - t e x t - p l
+           0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74,   \\ a i n - t e x t
+           0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72,   \\ - j a v a s c r
+           0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c,   \\ i p t - p u b l
+           0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,   \\ i c p r i v a t
+           0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65,   \\ e m a x - a g e
+           0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65,   \\ - g z i p - d e
+           0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64,   \\ f l a t e - s d
+           0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,   \\ c h c h a r s e
+           0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63,   \\ t - u t f - 8 c
+           0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69,   \\ h a r s e t - i
+           0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d,   \\ s o - 8 8 5 9 -
+           0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a,   \\ 1 - u t f - - -
+           0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e          \\ - e n q - 0 -
+   };
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 31]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   The entire contents of the name/value header block is compressed
+   using zlib.  There is a single zlib stream for all name value pairs
+   in one direction on a connection.  SPDY uses a SYNC_FLUSH between
+   each compressed frame.
+
+   Implementation notes: the compression engine can be tuned to favor
+   speed or size.  Optimizing for size increases memory use and CPU
+   consumption.  Because header blocks are generally small, implementors
+   may want to reduce the window-size of the compression engine from the
+   default 15bits (a 32KB window) to more like 11bits (a 2KB window).
+   The exact setting is chosen by the compressor, the decompressor will
+   work with any setting.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 32]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+3.  HTTP Layering over SPDY
+
+   SPDY is intended to be as compatible as possible with current web-
+   based applications.  This means that, from the perspective of the
+   server business logic or application API, the features of HTTP are
+   unchanged.  To achieve this, all of the application request and
+   response header semantics are preserved, although the syntax of
+   conveying those semantics has changed.  Thus, the rules from the
+   HTTP/1.1 specification in RFC2616 [RFC2616] apply with the changes in
+   the sections below.
+
+3.1.  Connection Management
+
+   Clients SHOULD NOT open more than one SPDY session to a given origin
+   [RFC6454] concurrently.
+
+   Note that it is possible for one SPDY session to be finishing (e.g. a
+   GOAWAY message has been sent, but not all streams have finished),
+   while another SPDY session is starting.
+
+3.1.1.  Use of GOAWAY
+
+   SPDY provides a GOAWAY message which can be used when closing a
+   connection from either the client or server.  Without a server GOAWAY
+   message, HTTP has a race condition where the client sends a request
+   (a new SYN_STREAM) just as the server is closing the connection, and
+   the client cannot know if the server received the stream or not.  By
+   using the last-stream-id in the GOAWAY, servers can indicate to the
+   client if a request was processed or not.
+
+   Note that some servers will choose to send the GOAWAY and immediately
+   terminate the connection without waiting for active streams to
+   finish.  The client will be able to determine this because SPDY
+   streams are determinstically closed.  This abrupt termination will
+   force the client to heuristically decide whether to retry the pending
+   requests.  Clients always need to be capable of dealing with this
+   case because they must deal with accidental connection termination
+   cases, which are the same as the server never having sent a GOAWAY.
+
+   More sophisticated servers will use GOAWAY to implement a graceful
+   teardown.  They will send the GOAWAY and provide some time for the
+   active streams to finish before terminating the connection.
+
+   If a SPDY client closes the connection, it should also send a GOAWAY
+   message.  This allows the server to know if any server-push streams
+   were received by the client.
+
+   If the endpoint closing the connection has not received any
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 33]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   SYN_STREAMs from the remote, the GOAWAY will contain a last-stream-id
+   of 0.
+
+3.2.  HTTP Request/Response
+
+3.2.1.  Request
+
+   The client initiates a request by sending a SYN_STREAM frame.  For
+   requests which do not contain a body, the SYN_STREAM frame MUST set
+   the FLAG_FIN, indicating that the client intends to send no further
+   data on this stream.  For requests which do contain a body, the
+   SYN_STREAM will not contain the FLAG_FIN, and the body will follow
+   the SYN_STREAM in a series of DATA frames.  The last DATA frame will
+   set the FLAG_FIN to indicate the end of the body.
+
+   The SYN_STREAM Name/Value section will contain all of the HTTP
+   headers which are associated with an HTTP request.  The header block
+   in SPDY is mostly unchanged from today's HTTP header block, with the
+   following differences:
+
+      The first line of the request is unfolded into name/value pairs
+      like other HTTP headers and MUST be present:
+
+         ":method" - the HTTP method for this request (e.g.  "GET",
+         "POST", "HEAD", etc)
+
+         ":path" - the url-path for this url with "/" prefixed.  (See
+         RFC1738 [RFC1738]).  For example, for
+         "http://www.google.com/search?q=dogs" the path would be
+         "/search?q=dogs".
+
+         ":version" - the HTTP version of this request (e.g.
+         "HTTP/1.1")
+
+      In addition, the following two name/value pairs must also be
+      present in every request:
+
+         ":host" - the hostport (See RFC1738 [RFC1738]) portion of the
+         URL for this request (e.g. "www.google.com:1234").  This header
+         is the same as the HTTP 'Host' header.
+
+         ":scheme" - the scheme portion of the URL for this request
+         (e.g. "https"))
+
+      Header names are all lowercase.
+
+      The Connection, Host, Keep-Alive, Proxy-Connection, and Transfer-
+      Encoding headers are not valid and MUST not be sent.
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 34]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+      User-agents MUST support gzip compression.  Regardless of the
+      Accept-Encoding sent by the user-agent, the server may always send
+      content encoded with gzip or deflate encoding.
+
+      If a server receives a request where the sum of the data frame
+      payload lengths does not equal the size of the Content-Length
+      header, the server MUST return a 400 (Bad Request) error.
+
+      POST-specific changes:
+
+         Although POSTs are inherently chunked, POST requests SHOULD
+         also be accompanied by a Content-Length header.  There are two
+         reasons for this: First, it assists with upload progress meters
+         for an improved user experience.  But second, we know from
+         early versions of SPDY that failure to send a content length
+         header is incompatible with many existing HTTP server
+         implementations.  Existing user-agents do not omit the Content-
+         Length header, and server implementations have come to depend
+         upon this.
+
+   The user-agent is free to prioritize requests as it sees fit.  If the
+   user-agent cannot make progress without receiving a resource, it
+   should attempt to raise the priority of that resource.  Resources
+   such as images, SHOULD generally use the lowest priority.
+
+   If a client sends a SYN_STREAM without all of the method, host, path,
+   scheme, and version headers, the server MUST reply with a HTTP 400
+   Bad Request reply.
+
+3.2.2.  Response
+
+   The server responds to a client request with a SYN_REPLY frame.
+   Symmetric to the client's upload stream, server will send data after
+   the SYN_REPLY frame via a series of DATA frames, and the last data
+   frame will contain the FLAG_FIN to indicate successful end-of-stream.
+   If a response (like a 202 or 204 response) contains no body, the
+   SYN_REPLY frame may contain the FLAG_FIN flag to indicate no further
+   data will be sent on the stream.
+
+      The response status line is unfolded into name/value pairs like
+      other HTTP headers and must be present:
+
+         ":status" - The HTTP response status code (e.g. "200" or "200
+         OK")
+
+         ":version" - The HTTP response version (e.g.  "HTTP/1.1")
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 35]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+      All header names must be lowercase.
+
+      The Connection, Keep-Alive, Proxy-Connection, and Transfer-
+      Encoding headers are not valid and MUST not be sent.
+
+      Responses MAY be accompanied by a Content-Length header for
+      advisory purposes. (e.g. for UI progress meters)
+
+      If a client receives a response where the sum of the data frame
+      payload lengths does not equal the size of the Content-Length
+      header, the client MUST ignore the content length header.
+
+   If a client receives a SYN_REPLY without a status or without a
+   version header, the client must reply with a RST_STREAM frame
+   indicating a PROTOCOL ERROR.
+
+3.2.3.  Authentication
+
+   When a client sends a request to an origin server that requires
+   authentication, the server can reply with a "401 Unauthorized"
+   response, and include a WWW-Authenticate challenge header that
+   defines the authentication scheme to be used.  The client then
+   retries the request with an Authorization header appropriate to the
+   specified authentication scheme.
+
+   There are four options for proxy authentication, Basic, Digest, NTLM
+   and Negotiate (SPNEGO).  The first two options were defined in
+   RFC2617 [RFC2617], and are stateless.  The second two options were
+   developed by Microsoft and specified in RFC4559 [RFC4559], and are
+   stateful; otherwise known as multi-round authentication, or
+   connection authentication.
+
+3.2.3.1.  Stateless Authentication
+
+   Stateless Authentication over SPDY is identical to how it is
+   performed over HTTP.  If multiple SPDY streams are concurrently sent
+   to a single server, each will authenticate independently, similar to
+   how two HTTP connections would independently authenticate to a proxy
+   server.
+
+3.2.3.2.  Stateful Authentication
+
+   Unfortunately, the stateful authentication mechanisms were
+   implemented and defined in a such a way that directly violates
+   RFC2617 - they do not include a "realm" as part of the request.  This
+   is problematic in SPDY because it makes it impossible for a client to
+   disambiguate two concurrent server authentication challenges.
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 36]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   To deal with this case, SPDY servers using Stateful Authentication
+   MUST implement one of two changes:
+
+      Servers can add a "realm=<desired realm>" header so that the two
+      authentication requests can be disambiguated and run concurrently.
+      Unfortunately, given how these mechanisms work, this is probably
+      not practical.
+
+      Upon sending the first stateful challenge response, the server
+      MUST buffer and defer all further frames which are not part of
+      completing the challenge until the challenge has completed.
+      Completing the authentication challenge may take multiple round
+      trips.  Once the client receives a "401 Authenticate" response for
+      a stateful authentication type, it MUST stop sending new requests
+      to the server until the authentication has completed by receiving
+      a non-401 response on at least one stream.
+
+3.3.  Server Push Transactions
+
+   SPDY enables a server to send multiple replies to a client for a
+   single request.  The rationale for this feature is that sometimes a
+   server knows that it will need to send multiple resources in response
+   to a single request.  Without server push features, the client must
+   first download the primary resource, then discover the secondary
+   resource(s), and request them.  Pushing of resources avoids the
+   round-trip delay, but also creates a potential race where a server
+   can be pushing content which a user-agent is in the process of
+   requesting.  The following mechanics attempt to prevent the race
+   condition while enabling the performance benefit.
+
+   Browsers receiving a pushed response MUST validate that the server is
+   authorized to push the URL using the browser same-origin [RFC6454]
+   policy.  For example, a SPDY connection to www.foo.com is generally
+   not permitted to push a response for www.evil.com.
+
+   If the browser accepts a pushed response (e.g. it does not send a
+   RST_STREAM), the browser MUST attempt to cache the pushed response in
+   same way that it would cache any other response.  This means
+   validating the response headers and inserting into the disk cache.
+
+   Because pushed responses have no request, they have no request
+   headers associated with them.  At the framing layer, SPDY pushed
+   streams contain an "associated-stream-id" which indicates the
+   requested stream for which the pushed stream is related.  The pushed
+   stream inherits all of the headers from the associated-stream-id with
+   the exception of ":host", ":scheme", and ":path", which are provided
+   as part of the pushed response stream headers.  The browser MUST
+   store these inherited and implied request headers with the cached
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 37]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   resource.
+
+   Implementation note: With server push, it is theoretically possible
+   for servers to push unreasonable amounts of content or resources to
+   the user-agent.  Browsers MUST implement throttles to protect against
+   unreasonable push attacks.
+
+3.3.1.  Server implementation
+
+   When the server intends to push a resource to the user-agent, it
+   opens a new stream by sending a unidirectional SYN_STREAM.  The
+   SYN_STREAM MUST include an Associated-To-Stream-ID, and MUST set the
+   FLAG_UNIDIRECTIONAL flag.  The SYN_STREAM MUST include headers for
+   ":scheme", ":host", ":path", which represent the URL for the resource
+   being pushed.  Subsequent headers may follow in HEADERS frames.  The
+   purpose of the association is so that the user-agent can
+   differentiate which request induced the pushed stream; without it, if
+   the user-agent had two tabs open to the same page, each pushing
+   unique content under a fixed URL, the user-agent would not be able to
+   differentiate the requests.
+
+   The Associated-To-Stream-ID must be the ID of an existing, open
+   stream.  The reason for this restriction is to have a clear endpoint
+   for pushed content.  If the user-agent requested a resource on stream
+   11, the server replies on stream 11.  It can push any number of
+   additional streams to the client before sending a FLAG_FIN on stream
+   11.  However, once the originating stream is closed no further push
+   streams may be associated with it.  The pushed streams do not need to
+   be closed (FIN set) before the originating stream is closed, they
+   only need to be created before the originating stream closes.
+
+   It is illegal for a server to push a resource with the Associated-To-
+   Stream-ID of 0.
+
+   To minimize race conditions with the client, the SYN_STREAM for the
+   pushed resources MUST be sent prior to sending any content which
+   could allow the client to discover the pushed resource and request
+   it.
+
+   The server MUST only push resources which would have been returned
+   from a GET request.
+
+   Note: If the server does not have all of the Name/Value Response
+   headers available at the time it issues the HEADERS frame for the
+   pushed resource, it may later use an additional HEADERS frame to
+   augment the name/value pairs to be associated with the pushed stream.
+   The subsequent HEADERS frame(s) must not contain a header for
+   ':host', ':scheme', or ':path' (e.g. the server can't change the
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 38]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   identity of the resource to be pushed).  The HEADERS frame must not
+   contain duplicate headers with a previously sent HEADERS frame.  The
+   server must send a HEADERS frame including the scheme/host/port
+   headers before sending any data frames on the stream.
+
+3.3.2.  Client implementation
+
+   When fetching a resource the client has 3 possibilities:
+
+      the resource is not being pushed
+
+      the resource is being pushed, but the data has not yet arrived
+
+      the resource is being pushed, and the data has started to arrive
+
+   When a SYN_STREAM and HEADERS frame which contains an Associated-To-
+   Stream-ID is received, the client must not issue GET requests for the
+   resource in the pushed stream, and instead wait for the pushed stream
+   to arrive.
+
+   If a client receives a server push stream with stream-id 0, it MUST
+   issue a session error (Section 2.4.1) with the status code
+   PROTOCOL_ERROR.
+
+   When a client receives a SYN_STREAM from the server without a the
+   ':host', ':scheme', and ':path' headers in the Name/Value section, it
+   MUST reply with a RST_STREAM with error code HTTP_PROTOCOL_ERROR.
+
+   To cancel individual server push streams, the client can issue a
+   stream error (Section 2.4.2) with error code CANCEL.  Upon receipt,
+   the server MUST stop sending on this stream immediately (this is an
+   Abrupt termination).
+
+   To cancel all server push streams related to a request, the client
+   may issue a stream error (Section 2.4.2) with error code CANCEL on
+   the associated-stream-id.  By cancelling that stream, the server MUST
+   immediately stop sending frames for any streams with
+   in-association-to for the original stream.
+
+   If the server sends a HEADER frame containing duplicate headers with
+   a previous HEADERS frame for the same stream, the client must issue a
+   stream error (Section 2.4.2) with error code PROTOCOL ERROR.
+
+   If the server sends a HEADERS frame after sending a data frame for
+   the same stream, the client MAY ignore the HEADERS frame.  Ignoring
+   the HEADERS frame after a data frame prevents handling of HTTP's
+   trailing headers
+   (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.40).
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 39]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+4.  Design Rationale and Notes
+
+   Authors' notes: The notes in this section have no bearing on the SPDY
+   protocol as specified within this document, and none of these notes
+   should be considered authoritative about how the protocol works.
+   However, these notes may prove useful in future debates about how to
+   resolve protocol ambiguities or how to evolve the protocol going
+   forward.  They may be removed before the final draft.
+
+4.1.  Separation of Framing Layer and Application Layer
+
+   Readers may note that this specification sometimes blends the framing
+   layer (Section 2) with requirements of a specific application - HTTP
+   (Section 3).  This is reflected in the request/response nature of the
+   streams, the definition of the HEADERS and compression contexts which
+   are very similar to HTTP, and other areas as well.
+
+   This blending is intentional - the primary goal of this protocol is
+   to create a low-latency protocol for use with HTTP.  Isolating the
+   two layers is convenient for description of the protocol and how it
+   relates to existing HTTP implementations.  However, the ability to
+   reuse the SPDY framing layer is a non goal.
+
+4.2.  Error handling - Framing Layer
+
+   Error handling at the SPDY layer splits errors into two groups: Those
+   that affect an individual SPDY stream, and those that do not.
+
+   When an error is confined to a single stream, but general framing is
+   in tact, SPDY attempts to use the RST_STREAM as a mechanism to
+   invalidate the stream but move forward without aborting the
+   connection altogether.
+
+   For errors occuring outside of a single stream context, SPDY assumes
+   the entire session is hosed.  In this case, the endpoint detecting
+   the error should initiate a connection close.
+
+4.3.  One Connection Per Domain
+
+   SPDY attempts to use fewer connections than other protocols have
+   traditionally used.  The rationale for this behavior is because it is
+   very difficult to provide a consistent level of service (e.g.  TCP
+   slow-start), prioritization, or optimal compression when the client
+   is connecting to the server through multiple channels.
+
+   Through lab measurements, we have seen consistent latency benefits by
+   using fewer connections from the client.  The overall number of
+   packets sent by SPDY can be as much as 40% less than HTTP.  Handling
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 40]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   large numbers of concurrent connections on the server also does
+   become a scalability problem, and SPDY reduces this load.
+
+   The use of multiple connections is not without benefit, however.
+   Because SPDY multiplexes multiple, independent streams onto a single
+   stream, it creates a potential for head-of-line blocking problems at
+   the transport level.  In tests so far, the negative effects of head-
+   of-line blocking (especially in the presence of packet loss) is
+   outweighed by the benefits of compression and prioritization.
+
+4.4.  Fixed vs Variable Length Fields
+
+   SPDY favors use of fixed length 32bit fields in cases where smaller,
+   variable length encodings could have been used.  To some, this seems
+   like a tragic waste of bandwidth.  SPDY choses the simple encoding
+   for speed and simplicity.
+
+   The goal of SPDY is to reduce latency on the network.  The overhead
+   of SPDY frames is generally quite low.  Each data frame is only an 8
+   byte overhead for a 1452 byte payload (~0.6%).  At the time of this
+   writing, bandwidth is already plentiful, and there is a strong trend
+   indicating that bandwidth will continue to increase.  With an average
+   worldwide bandwidth of 1Mbps, and assuming that a variable length
+   encoding could reduce the overhead by 50%, the latency saved by using
+   a variable length encoding would be less than 100 nanoseconds.  More
+   interesting are the effects when the larger encodings force a packet
+   boundary, in which case a round-trip could be induced.  However, by
+   addressing other aspects of SPDY and TCP interactions, we believe
+   this is completely mitigated.
+
+4.5.  Compression Context(s)
+
+   When isolating the compression contexts used for communicating with
+   multiple origins, we had a few choices to make.  We could have
+   maintained a map (or list) of compression contexts usable for each
+   origin.  The basic case is easy - each HEADERS frame would need to
+   identify the context to use for that frame.  However, compression
+   contexts are not cheap, so the lifecycle of each context would need
+   to be bounded.  For proxy servers, where we could churn through many
+   contexts, this would be a concern.  We considered using a static set
+   of contexts, say 16 of them, which would bound the memory use.  We
+   also considered dynamic contexts, which could be created on the fly,
+   and would need to be subsequently destroyed.  All of these are
+   complicated, and ultimately we decided that such a mechanism creates
+   too many problems to solve.
+
+   Alternatively, we've chosen the simple approach, which is to simply
+   provide a flag for resetting the compression context.  For the common
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 41]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   case (no proxy), this fine because most requests are to the same
+   origin and we never need to reset the context.  For cases where we
+   are using two different origins over a single SPDY session, we simply
+   reset the compression state between each transition.
+
+4.6.  Unidirectional streams
+
+   Many readers notice that unidirectional streams are both a bit
+   confusing in concept and also somewhat redundant.  If the recipient
+   of a stream doesn't wish to send data on a stream, it could simply
+   send a SYN_REPLY with the FLAG_FIN bit set.  The FLAG_UNIDIRECTIONAL
+   is, therefore, not necessary.
+
+   It is true that we don't need the UNIDIRECTIONAL markings.  It is
+   added because it avoids the recipient of pushed streams from needing
+   to send a set of empty frames (e.g. the SYN_STREAM w/ FLAG_FIN) which
+   otherwise serve no purpose.
+
+4.7.  Data Compression
+
+   Generic compression of data portion of the streams (as opposed to
+   compression of the headers) without knowing the content of the stream
+   is redundant.  There is no value in compressing a stream which is
+   already compressed.  Because of this, SPDY does allow data
+   compression to be optional.  We included it because study of existing
+   websites shows that many sites are not using compression as they
+   should, and users suffer because of it.  We wanted a mechanism where,
+   at the SPDY layer, site administrators could simply force compression
+   - it is better to compress twice than to not compress.
+
+   Overall, however, with this feature being optional and sometimes
+   redundant, it is unclear if it is useful at all.  We will likely
+   remove it from the specification.
+
+4.8.  Server Push
+
+   A subtle but important point is that server push streams must be
+   declared before the associated stream is closed.  The reason for this
+   is so that proxies have a lifetime for which they can discard
+   information about previous streams.  If a pushed stream could
+   associate itself with an already-closed stream, then endpoints would
+   not have a specific lifecycle for when they could disavow knowledge
+   of the streams which went before.
+
+
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 42]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+5.  Security Considerations
+
+5.1.  Use of Same-origin constraints
+
+   This specification uses the same-origin policy [RFC6454] in all cases
+   where verification of content is required.
+
+5.2.  HTTP Headers and SPDY Headers
+
+   At the application level, HTTP uses name/value pairs in its headers.
+   Because SPDY merges the existing HTTP headers with SPDY headers,
+   there is a possibility that some HTTP applications already use a
+   particular header name.  To avoid any conflicts, all headers
+   introduced for layering HTTP over SPDY are prefixed with ":". ":" is
+   not a valid sequence in HTTP header naming, preventing any possible
+   conflict.
+
+5.3.  Cross-Protocol Attacks
+
+   By utilizing TLS, we believe that SPDY introduces no new cross-
+   protocol attacks.  TLS encrypts the contents of all transmission
+   (except the handshake itself), making it difficult for attackers to
+   control the data which could be used in a cross-protocol attack.
+
+5.4.  Server Push Implicit Headers
+
+   Pushed resources do not have an associated request.  In order for
+   existing HTTP cache control validations (such as the Vary header) to
+   work, however, all cached resources must have a set of request
+   headers.  For this reason, browsers MUST be careful to inherit
+   request headers from the associated stream for the push.  This
+   includes the 'Cookie' header.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 43]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+6.  Privacy Considerations
+
+6.1.  Long Lived Connections
+
+   SPDY aims to keep connections open longer between clients and servers
+   in order to reduce the latency when a user makes a request.  The
+   maintenance of these connections over time could be used to expose
+   private information.  For example, a user using a browser hours after
+   the previous user stopped using that browser may be able to learn
+   about what the previous user was doing.  This is a problem with HTTP
+   in its current form as well, however the short lived connections make
+   it less of a risk.
+
+6.2.  SETTINGS frame
+
+   The SPDY SETTINGS frame allows servers to store out-of-band
+   transmitted information about the communication between client and
+   server on the client.  Although this is intended only to be used to
+   reduce latency, renegade servers could use it as a mechanism to store
+   identifying information about the client in future requests.
+
+   Clients implementing privacy modes, such as Google Chrome's
+   "incognito mode", may wish to disable client-persisted SETTINGS
+   storage.
+
+   Clients MUST clear persisted SETTINGS information when clearing the
+   cookies.
+
+   TODO: Put range maximums on each type of setting to limit
+   inappropriate uses.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 44]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+7.  Incompatibilities with SPDY draft #2
+
+   Here is a list of the major changes between this draft and draft #2.
+
+      Addition of flow control
+
+      Increased 16 bit length fields in SYN_STREAM and SYN_REPLY to 32
+      bits.
+
+      Changed definition of compression for DATA frames
+
+      Updated compression dictionary
+
+      Fixed off-by-one on the compression dictionary for headers
+
+      Increased priority field from 2bits to 3bits.
+
+      Removed NOOP frame
+
+      Split the request "url" into "scheme", "host", and "path"
+
+      Added the requirement that POSTs contain content-length.
+
+      Removed wasted 16bits of unused space from the end of the
+      SYN_REPLY and HEADERS frames.
+
+      Fixed bug: Priorities were described backward (0 was lowest
+      instead of highest).
+
+      Fixed bug: Name/Value header counts were duplicated in both the
+      Name Value header block and also the containing frame.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 45]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+8.  Requirements Notation
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+   document are to be interpreted as described in RFC 2119 [RFC2119].
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 46]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+9.  Acknowledgements
+
+   Many individuals have contributed to the design and evolution of
+   SPDY: Adam Langley, Wan-Teh Chang, Jim Morrison, Mark Nottingham,
+   Alyssa Wilk, Costin Manolache, William Chan, Vitaliy Lvin, Joe Chan,
+   Adam Barth, Ryan Hamilton, Gavin Peters, Kent Alstad, Kevin Lindsay,
+   Paul Amer, Fan Yang, Jonathan Leighton
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 47]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+10.  Normative References
+
+   [RFC0793]  Postel, J., "Transmission Control Protocol", STD 7,
+              RFC 793, September 1981.
+
+   [RFC1738]  Berners-Lee, T., Masinter, L., and M. McCahill, "Uniform
+              Resource Locators (URL)", RFC 1738, December 1994.
+
+   [RFC1950]  Deutsch, L. and J-L. Gailly, "ZLIB Compressed Data Format
+              Specification version 3.3", RFC 1950, May 1996.
+
+   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
+              Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+   [RFC2285]  Mandeville, R., "Benchmarking Terminology for LAN
+              Switching Devices", RFC 2285, February 1998.
+
+   [RFC2616]  Fielding, R., Gettys, J., Mogul, J., Frystyk, H.,
+              Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext
+              Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.
+
+   [RFC2617]  Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S.,
+              Leach, P., Luotonen, A., and L. Stewart, "HTTP
+              Authentication: Basic and Digest Access Authentication",
+              RFC 2617, June 1999.
+
+   [RFC4559]  Jaganathan, K., Zhu, L., and J. Brezak, "SPNEGO-based
+              Kerberos and NTLM HTTP Authentication in Microsoft
+              Windows", RFC 4559, June 2006.
+
+   [RFC4366]  Blake-Wilson, S., Nystrom, M., Hopwood, D., Mikkelsen, J.,
+              and T. Wright, "Transport Layer Security (TLS)
+              Extensions", RFC 4366, April 2006.
+
+   [RFC5246]  Dierks, T. and E. Rescorla, "The Transport Layer Security
+              (TLS) Protocol Version 1.2", RFC 5246, August 2008.
+
+   [RFC6454]  Barth, A., "The Web Origin Concept", RFC 6454,
+              December 2011.
+
+   [TLSNPN]   Langley, A., "TLS Next Protocol Negotiation",
+              <http://tools.ietf.org/html/
+              draft-agl-tls-nextprotoneg-01>.
+
+   [ASCII]    "US-ASCII. Coded Character Set - 7-Bit American Standard
+              Code for Information Interchange. Standard ANSI X3.4-1986,
+              ANSI, 1986.".
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 48]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+   [UDELCOMPRESSION]
+              Yang, F., Amer, P., and J. Leighton, "A Methodology to
+              Derive SPDY's Initial Dictionary for Zlib Compression",
+              <http://www.eecis.udel.edu/~amer/PEL/poc/pdf/
+              SPDY-Fan.pdf>.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 49]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+Appendix A.  Changes
+
+   To be removed by RFC Editor before publication
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 50]
+
+Internet-Draft                    SPDY                          Feb 2012
+
+
+Authors' Addresses
+
+   Mike Belshe
+   Twist
+
+   Email: mbelshe@chromium.org
+
+
+   Roberto Peon
+   Google, Inc
+
+   Email: fenix@google.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Belshe & Peon            Expires August 4, 2012                [Page 51]
+
diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am
new file mode 100644
index 0000000..edc80dc
--- /dev/null
+++ b/src/examples/Makefile.am
@@ -0,0 +1,198 @@
+# This Makefile.am is in the public domain
+SUBDIRS  = .
+
+AM_CPPFLAGS = \
+  -I$(top_srcdir)/src/include \
+  -DDATA_DIR=\"$(top_srcdir)/src/datadir/\"
+
+AM_CFLAGS = @LIBGCRYPT_CFLAGS@
+
+CPU_COUNT_DEF = -DCPU_COUNT=$(CPU_COUNT)
+
+if USE_COVERAGE
+  AM_CFLAGS += --coverage
+endif
+
+if ENABLE_SPDY
+spdyex = \
+  spdy_event_loop \
+  spdy_fileserver \
+  spdy_response_with_callback
+
+if HAVE_SPDYLAY
+spdyex += mhd2spdy
+endif
+endif
+
+
+# example programs
+noinst_PROGRAMS = \
+ benchmark \
+ benchmark_https \
+ chunked_example \
+ minimal_example \
+ dual_stack_example \
+ minimal_example_comet \
+ querystring_example \
+ fileserver_example \
+ fileserver_example_dirs \
+ fileserver_example_external_select \
+ refuse_post_example \
+ $(spdyex)
+
+
+if ENABLE_HTTPS
+noinst_PROGRAMS += https_fileserver_example
+endif
+if HAVE_POSTPROCESSOR
+noinst_PROGRAMS += \
+  post_example
+if HAVE_MAGIC
+noinst_PROGRAMS += \
+  demo \
+  demo_https
+endif
+endif
+
+if ENABLE_DAUTH
+noinst_PROGRAMS += \
+ digest_auth_example
+endif
+
+if ENABLE_BAUTH
+noinst_PROGRAMS += \
+ authorization_example
+endif
+
+if HAVE_W32
+AM_CFLAGS += -DWINDOWS
+endif
+
+minimal_example_SOURCES = \
+ minimal_example.c
+minimal_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+chunked_example_SOURCES = \
+ chunked_example.c
+chunked_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+demo_SOURCES = \
+ demo.c
+demo_CFLAGS = \
+ $(PTHREAD_CFLAGS) $(AM_CFLAGS)
+demo_CPPFLAGS = \
+ $(AM_CPPFLAGS) $(CPU_COUNT_DEF)
+demo_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la  \
+ $(PTHREAD_LIBS) -lmagic
+
+demo_https_SOURCES = \
+ demo_https.c
+demo_https_CFLAGS = \
+ $(PTHREAD_CFLAGS) $(AM_CFLAGS)
+demo_https_CPPFLAGS = \
+ $(AM_CPPFLAGS) $(CPU_COUNT_DEF)
+demo_https_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la  \
+ $(PTHREAD_LIBS) -lmagic
+
+mhd2spdy_SOURCES = \
+ mhd2spdy.c \
+ mhd2spdy_spdy.c mhd2spdy_spdy.h \
+ mhd2spdy_http.c mhd2spdy_http.h \
+ mhd2spdy_structures.c mhd2spdy_structures.h
+mhd2spdy_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la  \
+ -lssl -lcrypto -lspdylay
+
+benchmark_SOURCES = \
+ benchmark.c
+benchmark_CPPFLAGS = \
+ $(AM_CPPFLAGS) $(CPU_COUNT_DEF)
+benchmark_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+benchmark_https_SOURCES = \
+ benchmark_https.c
+benchmark_https_CPPFLAGS = \
+ $(AM_CPPFLAGS) $(CPU_COUNT_DEF)
+benchmark_https_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+dual_stack_example_SOURCES = \
+ dual_stack_example.c
+dual_stack_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+post_example_SOURCES = \
+ post_example.c
+post_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+minimal_example_comet_SOURCES = \
+ minimal_example_comet.c
+minimal_example_comet_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+authorization_example_SOURCES = \
+ authorization_example.c
+authorization_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+digest_auth_example_SOURCES = \
+ digest_auth_example.c
+digest_auth_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+refuse_post_example_SOURCES = \
+ refuse_post_example.c
+refuse_post_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+querystring_example_SOURCES = \
+ querystring_example.c
+querystring_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+fileserver_example_SOURCES = \
+ fileserver_example.c
+fileserver_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+fileserver_example_dirs_SOURCES = \
+ fileserver_example_dirs.c
+fileserver_example_dirs_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+fileserver_example_external_select_SOURCES = \
+ fileserver_example_external_select.c
+fileserver_example_external_select_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+https_fileserver_example_SOURCES = \
+https_fileserver_example.c
+https_fileserver_example_CPPFLAGS = \
+ $(AM_CPPFLAGS) $(GNUTLS_CPPFLAGS)
+https_fileserver_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+
+spdy_event_loop_SOURCES = \
+ spdy_event_loop.c
+spdy_event_loop_LDADD = \
+  $(top_builddir)/src/microspdy/libmicrospdy.la \
+ -lz
+
+spdy_fileserver_SOURCES = \
+ spdy_fileserver.c
+spdy_fileserver_LDADD = \
+  $(top_builddir)/src/microspdy/libmicrospdy.la \
+ -lz
+
+spdy_response_with_callback_SOURCES = \
+ spdy_response_with_callback.c
+spdy_response_with_callback_LDADD = \
+  $(top_builddir)/src/microspdy/libmicrospdy.la \
+ -lz
diff --git a/src/examples/Makefile.in b/src/examples/Makefile.in
new file mode 100644
index 0000000..5643f96
--- /dev/null
+++ b/src/examples/Makefile.in
@@ -0,0 +1,1246 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 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 = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+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@
+@USE_COVERAGE_TRUE@am__append_1 = --coverage
+@ENABLE_SPDY_TRUE@@HAVE_SPDYLAY_TRUE@am__append_2 = mhd2spdy
+noinst_PROGRAMS = benchmark$(EXEEXT) benchmark_https$(EXEEXT) \
+	chunked_example$(EXEEXT) minimal_example$(EXEEXT) \
+	dual_stack_example$(EXEEXT) minimal_example_comet$(EXEEXT) \
+	querystring_example$(EXEEXT) fileserver_example$(EXEEXT) \
+	fileserver_example_dirs$(EXEEXT) \
+	fileserver_example_external_select$(EXEEXT) \
+	refuse_post_example$(EXEEXT) $(am__EXEEXT_2) $(am__EXEEXT_3) \
+	$(am__EXEEXT_4) $(am__EXEEXT_5) $(am__EXEEXT_6) \
+	$(am__EXEEXT_7)
+@ENABLE_HTTPS_TRUE@am__append_3 = https_fileserver_example
+@HAVE_POSTPROCESSOR_TRUE@am__append_4 = \
+@HAVE_POSTPROCESSOR_TRUE@  post_example
+
+@HAVE_MAGIC_TRUE@@HAVE_POSTPROCESSOR_TRUE@am__append_5 = \
+@HAVE_MAGIC_TRUE@@HAVE_POSTPROCESSOR_TRUE@  demo \
+@HAVE_MAGIC_TRUE@@HAVE_POSTPROCESSOR_TRUE@  demo_https
+
+@ENABLE_DAUTH_TRUE@am__append_6 = \
+@ENABLE_DAUTH_TRUE@ digest_auth_example
+
+@ENABLE_BAUTH_TRUE@am__append_7 = \
+@ENABLE_BAUTH_TRUE@ authorization_example
+
+@HAVE_W32_TRUE@am__append_8 = -DWINDOWS
+subdir = src/examples
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_compile_flags.m4 \
+	$(top_srcdir)/m4/ax_append_flag.m4 \
+	$(top_srcdir)/m4/ax_check_compile_flag.m4 \
+	$(top_srcdir)/m4/ax_check_link_flag.m4 \
+	$(top_srcdir)/m4/ax_check_openssl.m4 \
+	$(top_srcdir)/m4/ax_count_cpus.m4 \
+	$(top_srcdir)/m4/ax_have_epoll.m4 \
+	$(top_srcdir)/m4/ax_pthread.m4 \
+	$(top_srcdir)/m4/ax_require_defined.m4 \
+	$(top_srcdir)/m4/libcurl.m4 $(top_srcdir)/m4/libgcrypt.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)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/MHD_config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+@ENABLE_SPDY_TRUE@@HAVE_SPDYLAY_TRUE@am__EXEEXT_1 = mhd2spdy$(EXEEXT)
+@ENABLE_SPDY_TRUE@am__EXEEXT_2 = spdy_event_loop$(EXEEXT) \
+@ENABLE_SPDY_TRUE@	spdy_fileserver$(EXEEXT) \
+@ENABLE_SPDY_TRUE@	spdy_response_with_callback$(EXEEXT) \
+@ENABLE_SPDY_TRUE@	$(am__EXEEXT_1)
+@ENABLE_HTTPS_TRUE@am__EXEEXT_3 = https_fileserver_example$(EXEEXT)
+@HAVE_POSTPROCESSOR_TRUE@am__EXEEXT_4 = post_example$(EXEEXT)
+@HAVE_MAGIC_TRUE@@HAVE_POSTPROCESSOR_TRUE@am__EXEEXT_5 =  \
+@HAVE_MAGIC_TRUE@@HAVE_POSTPROCESSOR_TRUE@	demo$(EXEEXT) \
+@HAVE_MAGIC_TRUE@@HAVE_POSTPROCESSOR_TRUE@	demo_https$(EXEEXT)
+@ENABLE_DAUTH_TRUE@am__EXEEXT_6 = digest_auth_example$(EXEEXT)
+@ENABLE_BAUTH_TRUE@am__EXEEXT_7 = authorization_example$(EXEEXT)
+PROGRAMS = $(noinst_PROGRAMS)
+am_authorization_example_OBJECTS = authorization_example.$(OBJEXT)
+authorization_example_OBJECTS = $(am_authorization_example_OBJECTS)
+authorization_example_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+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_benchmark_OBJECTS = benchmark-benchmark.$(OBJEXT)
+benchmark_OBJECTS = $(am_benchmark_OBJECTS)
+benchmark_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_benchmark_https_OBJECTS =  \
+	benchmark_https-benchmark_https.$(OBJEXT)
+benchmark_https_OBJECTS = $(am_benchmark_https_OBJECTS)
+benchmark_https_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_chunked_example_OBJECTS = chunked_example.$(OBJEXT)
+chunked_example_OBJECTS = $(am_chunked_example_OBJECTS)
+chunked_example_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_demo_OBJECTS = demo-demo.$(OBJEXT)
+demo_OBJECTS = $(am_demo_OBJECTS)
+am__DEPENDENCIES_1 =
+demo_DEPENDENCIES = $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+	$(am__DEPENDENCIES_1)
+demo_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(demo_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+am_demo_https_OBJECTS = demo_https-demo_https.$(OBJEXT)
+demo_https_OBJECTS = $(am_demo_https_OBJECTS)
+demo_https_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la \
+	$(am__DEPENDENCIES_1)
+demo_https_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(demo_https_CFLAGS) \
+	$(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+am_digest_auth_example_OBJECTS = digest_auth_example.$(OBJEXT)
+digest_auth_example_OBJECTS = $(am_digest_auth_example_OBJECTS)
+digest_auth_example_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_dual_stack_example_OBJECTS = dual_stack_example.$(OBJEXT)
+dual_stack_example_OBJECTS = $(am_dual_stack_example_OBJECTS)
+dual_stack_example_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_fileserver_example_OBJECTS = fileserver_example.$(OBJEXT)
+fileserver_example_OBJECTS = $(am_fileserver_example_OBJECTS)
+fileserver_example_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_fileserver_example_dirs_OBJECTS =  \
+	fileserver_example_dirs.$(OBJEXT)
+fileserver_example_dirs_OBJECTS =  \
+	$(am_fileserver_example_dirs_OBJECTS)
+fileserver_example_dirs_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_fileserver_example_external_select_OBJECTS =  \
+	fileserver_example_external_select.$(OBJEXT)
+fileserver_example_external_select_OBJECTS =  \
+	$(am_fileserver_example_external_select_OBJECTS)
+fileserver_example_external_select_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_https_fileserver_example_OBJECTS =  \
+	https_fileserver_example-https_fileserver_example.$(OBJEXT)
+https_fileserver_example_OBJECTS =  \
+	$(am_https_fileserver_example_OBJECTS)
+https_fileserver_example_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_mhd2spdy_OBJECTS = mhd2spdy.$(OBJEXT) mhd2spdy_spdy.$(OBJEXT) \
+	mhd2spdy_http.$(OBJEXT) mhd2spdy_structures.$(OBJEXT)
+mhd2spdy_OBJECTS = $(am_mhd2spdy_OBJECTS)
+mhd2spdy_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_minimal_example_OBJECTS = minimal_example.$(OBJEXT)
+minimal_example_OBJECTS = $(am_minimal_example_OBJECTS)
+minimal_example_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_minimal_example_comet_OBJECTS = minimal_example_comet.$(OBJEXT)
+minimal_example_comet_OBJECTS = $(am_minimal_example_comet_OBJECTS)
+minimal_example_comet_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_post_example_OBJECTS = post_example.$(OBJEXT)
+post_example_OBJECTS = $(am_post_example_OBJECTS)
+post_example_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_querystring_example_OBJECTS = querystring_example.$(OBJEXT)
+querystring_example_OBJECTS = $(am_querystring_example_OBJECTS)
+querystring_example_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_refuse_post_example_OBJECTS = refuse_post_example.$(OBJEXT)
+refuse_post_example_OBJECTS = $(am_refuse_post_example_OBJECTS)
+refuse_post_example_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_spdy_event_loop_OBJECTS = spdy_event_loop.$(OBJEXT)
+spdy_event_loop_OBJECTS = $(am_spdy_event_loop_OBJECTS)
+spdy_event_loop_DEPENDENCIES =  \
+	$(top_builddir)/src/microspdy/libmicrospdy.la
+am_spdy_fileserver_OBJECTS = spdy_fileserver.$(OBJEXT)
+spdy_fileserver_OBJECTS = $(am_spdy_fileserver_OBJECTS)
+spdy_fileserver_DEPENDENCIES =  \
+	$(top_builddir)/src/microspdy/libmicrospdy.la
+am_spdy_response_with_callback_OBJECTS =  \
+	spdy_response_with_callback.$(OBJEXT)
+spdy_response_with_callback_OBJECTS =  \
+	$(am_spdy_response_with_callback_OBJECTS)
+spdy_response_with_callback_DEPENDENCIES =  \
+	$(top_builddir)/src/microspdy/libmicrospdy.la
+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__depfiles_maybe = depfiles
+am__mv = mv -f
+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 = $(authorization_example_SOURCES) $(benchmark_SOURCES) \
+	$(benchmark_https_SOURCES) $(chunked_example_SOURCES) \
+	$(demo_SOURCES) $(demo_https_SOURCES) \
+	$(digest_auth_example_SOURCES) $(dual_stack_example_SOURCES) \
+	$(fileserver_example_SOURCES) \
+	$(fileserver_example_dirs_SOURCES) \
+	$(fileserver_example_external_select_SOURCES) \
+	$(https_fileserver_example_SOURCES) $(mhd2spdy_SOURCES) \
+	$(minimal_example_SOURCES) $(minimal_example_comet_SOURCES) \
+	$(post_example_SOURCES) $(querystring_example_SOURCES) \
+	$(refuse_post_example_SOURCES) $(spdy_event_loop_SOURCES) \
+	$(spdy_fileserver_SOURCES) \
+	$(spdy_response_with_callback_SOURCES)
+DIST_SOURCES = $(authorization_example_SOURCES) $(benchmark_SOURCES) \
+	$(benchmark_https_SOURCES) $(chunked_example_SOURCES) \
+	$(demo_SOURCES) $(demo_https_SOURCES) \
+	$(digest_auth_example_SOURCES) $(dual_stack_example_SOURCES) \
+	$(fileserver_example_SOURCES) \
+	$(fileserver_example_dirs_SOURCES) \
+	$(fileserver_example_external_select_SOURCES) \
+	$(https_fileserver_example_SOURCES) $(mhd2spdy_SOURCES) \
+	$(minimal_example_SOURCES) $(minimal_example_comet_SOURCES) \
+	$(post_example_SOURCES) $(querystring_example_SOURCES) \
+	$(refuse_post_example_SOURCES) $(spdy_event_loop_SOURCES) \
+	$(spdy_fileserver_SOURCES) \
+	$(spdy_response_with_callback_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-recursive dvi-recursive html-recursive info-recursive \
+	install-data-recursive install-dvi-recursive \
+	install-exec-recursive install-html-recursive \
+	install-info-recursive install-pdf-recursive \
+	install-ps-recursive install-recursive installcheck-recursive \
+	installdirs-recursive pdf-recursive ps-recursive \
+	tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	distdir
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPU_COUNT = @CPU_COUNT@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_CPPFLAGS = @GNUTLS_CPPFLAGS@
+GNUTLS_LDFLAGS = @GNUTLS_LDFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+HAVE_CURL_BINARY = @HAVE_CURL_BINARY@
+HAVE_MAKEINFO_BINARY = @HAVE_MAKEINFO_BINARY@
+HIDDEN_VISIBILITY_CFLAGS = @HIDDEN_VISIBILITY_CFLAGS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSPDY_VERSION_AGE = @LIBSPDY_VERSION_AGE@
+LIBSPDY_VERSION_CURRENT = @LIBSPDY_VERSION_CURRENT@
+LIBSPDY_VERSION_REVISION = @LIBSPDY_VERSION_REVISION@
+LIBTOOL = @LIBTOOL@
+LIB_VERSION_AGE = @LIB_VERSION_AGE@
+LIB_VERSION_CURRENT = @LIB_VERSION_CURRENT@
+LIB_VERSION_REVISION = @LIB_VERSION_REVISION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MHD_LIBDEPS = @MHD_LIBDEPS@
+MHD_LIBDEPS_PKGCFG = @MHD_LIBDEPS_PKGCFG@
+MHD_LIB_CFLAGS = @MHD_LIB_CFLAGS@
+MHD_LIB_CPPFLAGS = @MHD_LIB_CPPFLAGS@
+MHD_LIB_LDFLAGS = @MHD_LIB_LDFLAGS@
+MHD_REQ_PRIVATE = @MHD_REQ_PRIVATE@
+MKDIR_P = @MKDIR_P@
+MS_LIB_TOOL = @MS_LIB_TOOL@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_INCLUDES = @OPENSSL_INCLUDES@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@
+PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@
+PACKAGE_VERSION_SUBMINOR = @PACKAGE_VERSION_SUBMINOR@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPDY_LIBDEPS = @SPDY_LIBDEPS@
+SPDY_LIB_CFLAGS = @SPDY_LIB_CFLAGS@
+SPDY_LIB_CPPFLAGS = @SPDY_LIB_CPPFLAGS@
+SPDY_LIB_LDFLAGS = @SPDY_LIB_LDFLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+_libcurl_config = @_libcurl_config@
+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_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@
+ax_pthread_config = @ax_pthread_config@
+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@
+have_socat = @have_socat@
+have_zzuf = @have_zzuf@
+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@
+lt_cv_objdir = @lt_cv_objdir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# This Makefile.am is in the public domain
+SUBDIRS = .
+AM_CPPFLAGS = \
+  -I$(top_srcdir)/src/include \
+  -DDATA_DIR=\"$(top_srcdir)/src/datadir/\"
+
+AM_CFLAGS = @LIBGCRYPT_CFLAGS@ $(am__append_1) $(am__append_8)
+CPU_COUNT_DEF = -DCPU_COUNT=$(CPU_COUNT)
+@ENABLE_SPDY_TRUE@spdyex = spdy_event_loop spdy_fileserver \
+@ENABLE_SPDY_TRUE@	spdy_response_with_callback $(am__append_2)
+minimal_example_SOURCES = \
+ minimal_example.c
+
+minimal_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+chunked_example_SOURCES = \
+ chunked_example.c
+
+chunked_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+demo_SOURCES = \
+ demo.c
+
+demo_CFLAGS = \
+ $(PTHREAD_CFLAGS) $(AM_CFLAGS)
+
+demo_CPPFLAGS = \
+ $(AM_CPPFLAGS) $(CPU_COUNT_DEF)
+
+demo_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la  \
+ $(PTHREAD_LIBS) -lmagic
+
+demo_https_SOURCES = \
+ demo_https.c
+
+demo_https_CFLAGS = \
+ $(PTHREAD_CFLAGS) $(AM_CFLAGS)
+
+demo_https_CPPFLAGS = \
+ $(AM_CPPFLAGS) $(CPU_COUNT_DEF)
+
+demo_https_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la  \
+ $(PTHREAD_LIBS) -lmagic
+
+mhd2spdy_SOURCES = \
+ mhd2spdy.c \
+ mhd2spdy_spdy.c mhd2spdy_spdy.h \
+ mhd2spdy_http.c mhd2spdy_http.h \
+ mhd2spdy_structures.c mhd2spdy_structures.h
+
+mhd2spdy_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la  \
+ -lssl -lcrypto -lspdylay
+
+benchmark_SOURCES = \
+ benchmark.c
+
+benchmark_CPPFLAGS = \
+ $(AM_CPPFLAGS) $(CPU_COUNT_DEF)
+
+benchmark_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+benchmark_https_SOURCES = \
+ benchmark_https.c
+
+benchmark_https_CPPFLAGS = \
+ $(AM_CPPFLAGS) $(CPU_COUNT_DEF)
+
+benchmark_https_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+dual_stack_example_SOURCES = \
+ dual_stack_example.c
+
+dual_stack_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+post_example_SOURCES = \
+ post_example.c
+
+post_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+minimal_example_comet_SOURCES = \
+ minimal_example_comet.c
+
+minimal_example_comet_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+authorization_example_SOURCES = \
+ authorization_example.c
+
+authorization_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+digest_auth_example_SOURCES = \
+ digest_auth_example.c
+
+digest_auth_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+refuse_post_example_SOURCES = \
+ refuse_post_example.c
+
+refuse_post_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+querystring_example_SOURCES = \
+ querystring_example.c
+
+querystring_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+fileserver_example_SOURCES = \
+ fileserver_example.c
+
+fileserver_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+fileserver_example_dirs_SOURCES = \
+ fileserver_example_dirs.c
+
+fileserver_example_dirs_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+fileserver_example_external_select_SOURCES = \
+ fileserver_example_external_select.c
+
+fileserver_example_external_select_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+https_fileserver_example_SOURCES = \
+https_fileserver_example.c
+
+https_fileserver_example_CPPFLAGS = \
+ $(AM_CPPFLAGS) $(GNUTLS_CPPFLAGS)
+
+https_fileserver_example_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+spdy_event_loop_SOURCES = \
+ spdy_event_loop.c
+
+spdy_event_loop_LDADD = \
+  $(top_builddir)/src/microspdy/libmicrospdy.la \
+ -lz
+
+spdy_fileserver_SOURCES = \
+ spdy_fileserver.c
+
+spdy_fileserver_LDADD = \
+  $(top_builddir)/src/microspdy/libmicrospdy.la \
+ -lz
+
+spdy_response_with_callback_SOURCES = \
+ spdy_response_with_callback.c
+
+spdy_response_with_callback_LDADD = \
+  $(top_builddir)/src/microspdy/libmicrospdy.la \
+ -lz
+
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .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) --gnu src/examples/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/examples/Makefile
+.PRECIOUS: 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__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	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-noinstPROGRAMS:
+	@list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+
+authorization_example$(EXEEXT): $(authorization_example_OBJECTS) $(authorization_example_DEPENDENCIES) $(EXTRA_authorization_example_DEPENDENCIES) 
+	@rm -f authorization_example$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(authorization_example_OBJECTS) $(authorization_example_LDADD) $(LIBS)
+
+benchmark$(EXEEXT): $(benchmark_OBJECTS) $(benchmark_DEPENDENCIES) $(EXTRA_benchmark_DEPENDENCIES) 
+	@rm -f benchmark$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(benchmark_OBJECTS) $(benchmark_LDADD) $(LIBS)
+
+benchmark_https$(EXEEXT): $(benchmark_https_OBJECTS) $(benchmark_https_DEPENDENCIES) $(EXTRA_benchmark_https_DEPENDENCIES) 
+	@rm -f benchmark_https$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(benchmark_https_OBJECTS) $(benchmark_https_LDADD) $(LIBS)
+
+chunked_example$(EXEEXT): $(chunked_example_OBJECTS) $(chunked_example_DEPENDENCIES) $(EXTRA_chunked_example_DEPENDENCIES) 
+	@rm -f chunked_example$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(chunked_example_OBJECTS) $(chunked_example_LDADD) $(LIBS)
+
+demo$(EXEEXT): $(demo_OBJECTS) $(demo_DEPENDENCIES) $(EXTRA_demo_DEPENDENCIES) 
+	@rm -f demo$(EXEEXT)
+	$(AM_V_CCLD)$(demo_LINK) $(demo_OBJECTS) $(demo_LDADD) $(LIBS)
+
+demo_https$(EXEEXT): $(demo_https_OBJECTS) $(demo_https_DEPENDENCIES) $(EXTRA_demo_https_DEPENDENCIES) 
+	@rm -f demo_https$(EXEEXT)
+	$(AM_V_CCLD)$(demo_https_LINK) $(demo_https_OBJECTS) $(demo_https_LDADD) $(LIBS)
+
+digest_auth_example$(EXEEXT): $(digest_auth_example_OBJECTS) $(digest_auth_example_DEPENDENCIES) $(EXTRA_digest_auth_example_DEPENDENCIES) 
+	@rm -f digest_auth_example$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(digest_auth_example_OBJECTS) $(digest_auth_example_LDADD) $(LIBS)
+
+dual_stack_example$(EXEEXT): $(dual_stack_example_OBJECTS) $(dual_stack_example_DEPENDENCIES) $(EXTRA_dual_stack_example_DEPENDENCIES) 
+	@rm -f dual_stack_example$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(dual_stack_example_OBJECTS) $(dual_stack_example_LDADD) $(LIBS)
+
+fileserver_example$(EXEEXT): $(fileserver_example_OBJECTS) $(fileserver_example_DEPENDENCIES) $(EXTRA_fileserver_example_DEPENDENCIES) 
+	@rm -f fileserver_example$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(fileserver_example_OBJECTS) $(fileserver_example_LDADD) $(LIBS)
+
+fileserver_example_dirs$(EXEEXT): $(fileserver_example_dirs_OBJECTS) $(fileserver_example_dirs_DEPENDENCIES) $(EXTRA_fileserver_example_dirs_DEPENDENCIES) 
+	@rm -f fileserver_example_dirs$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(fileserver_example_dirs_OBJECTS) $(fileserver_example_dirs_LDADD) $(LIBS)
+
+fileserver_example_external_select$(EXEEXT): $(fileserver_example_external_select_OBJECTS) $(fileserver_example_external_select_DEPENDENCIES) $(EXTRA_fileserver_example_external_select_DEPENDENCIES) 
+	@rm -f fileserver_example_external_select$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(fileserver_example_external_select_OBJECTS) $(fileserver_example_external_select_LDADD) $(LIBS)
+
+https_fileserver_example$(EXEEXT): $(https_fileserver_example_OBJECTS) $(https_fileserver_example_DEPENDENCIES) $(EXTRA_https_fileserver_example_DEPENDENCIES) 
+	@rm -f https_fileserver_example$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(https_fileserver_example_OBJECTS) $(https_fileserver_example_LDADD) $(LIBS)
+
+mhd2spdy$(EXEEXT): $(mhd2spdy_OBJECTS) $(mhd2spdy_DEPENDENCIES) $(EXTRA_mhd2spdy_DEPENDENCIES) 
+	@rm -f mhd2spdy$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(mhd2spdy_OBJECTS) $(mhd2spdy_LDADD) $(LIBS)
+
+minimal_example$(EXEEXT): $(minimal_example_OBJECTS) $(minimal_example_DEPENDENCIES) $(EXTRA_minimal_example_DEPENDENCIES) 
+	@rm -f minimal_example$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(minimal_example_OBJECTS) $(minimal_example_LDADD) $(LIBS)
+
+minimal_example_comet$(EXEEXT): $(minimal_example_comet_OBJECTS) $(minimal_example_comet_DEPENDENCIES) $(EXTRA_minimal_example_comet_DEPENDENCIES) 
+	@rm -f minimal_example_comet$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(minimal_example_comet_OBJECTS) $(minimal_example_comet_LDADD) $(LIBS)
+
+post_example$(EXEEXT): $(post_example_OBJECTS) $(post_example_DEPENDENCIES) $(EXTRA_post_example_DEPENDENCIES) 
+	@rm -f post_example$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(post_example_OBJECTS) $(post_example_LDADD) $(LIBS)
+
+querystring_example$(EXEEXT): $(querystring_example_OBJECTS) $(querystring_example_DEPENDENCIES) $(EXTRA_querystring_example_DEPENDENCIES) 
+	@rm -f querystring_example$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(querystring_example_OBJECTS) $(querystring_example_LDADD) $(LIBS)
+
+refuse_post_example$(EXEEXT): $(refuse_post_example_OBJECTS) $(refuse_post_example_DEPENDENCIES) $(EXTRA_refuse_post_example_DEPENDENCIES) 
+	@rm -f refuse_post_example$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(refuse_post_example_OBJECTS) $(refuse_post_example_LDADD) $(LIBS)
+
+spdy_event_loop$(EXEEXT): $(spdy_event_loop_OBJECTS) $(spdy_event_loop_DEPENDENCIES) $(EXTRA_spdy_event_loop_DEPENDENCIES) 
+	@rm -f spdy_event_loop$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(spdy_event_loop_OBJECTS) $(spdy_event_loop_LDADD) $(LIBS)
+
+spdy_fileserver$(EXEEXT): $(spdy_fileserver_OBJECTS) $(spdy_fileserver_DEPENDENCIES) $(EXTRA_spdy_fileserver_DEPENDENCIES) 
+	@rm -f spdy_fileserver$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(spdy_fileserver_OBJECTS) $(spdy_fileserver_LDADD) $(LIBS)
+
+spdy_response_with_callback$(EXEEXT): $(spdy_response_with_callback_OBJECTS) $(spdy_response_with_callback_DEPENDENCIES) $(EXTRA_spdy_response_with_callback_DEPENDENCIES) 
+	@rm -f spdy_response_with_callback$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(spdy_response_with_callback_OBJECTS) $(spdy_response_with_callback_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/authorization_example.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/benchmark-benchmark.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/benchmark_https-benchmark_https.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chunked_example.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo-demo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_https-demo_https.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/digest_auth_example.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dual_stack_example.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileserver_example.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileserver_example_dirs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileserver_example_external_select.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/https_fileserver_example-https_fileserver_example.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mhd2spdy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mhd2spdy_http.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mhd2spdy_spdy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mhd2spdy_structures.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/minimal_example.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/minimal_example_comet.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/post_example.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/querystring_example.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refuse_post_example.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spdy_event_loop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spdy_fileserver.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spdy_response_with_callback.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+benchmark-benchmark.o: benchmark.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(benchmark_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT benchmark-benchmark.o -MD -MP -MF $(DEPDIR)/benchmark-benchmark.Tpo -c -o benchmark-benchmark.o `test -f 'benchmark.c' || echo '$(srcdir)/'`benchmark.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/benchmark-benchmark.Tpo $(DEPDIR)/benchmark-benchmark.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='benchmark.c' object='benchmark-benchmark.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(benchmark_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o benchmark-benchmark.o `test -f 'benchmark.c' || echo '$(srcdir)/'`benchmark.c
+
+benchmark-benchmark.obj: benchmark.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(benchmark_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT benchmark-benchmark.obj -MD -MP -MF $(DEPDIR)/benchmark-benchmark.Tpo -c -o benchmark-benchmark.obj `if test -f 'benchmark.c'; then $(CYGPATH_W) 'benchmark.c'; else $(CYGPATH_W) '$(srcdir)/benchmark.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/benchmark-benchmark.Tpo $(DEPDIR)/benchmark-benchmark.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='benchmark.c' object='benchmark-benchmark.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(benchmark_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o benchmark-benchmark.obj `if test -f 'benchmark.c'; then $(CYGPATH_W) 'benchmark.c'; else $(CYGPATH_W) '$(srcdir)/benchmark.c'; fi`
+
+benchmark_https-benchmark_https.o: benchmark_https.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(benchmark_https_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT benchmark_https-benchmark_https.o -MD -MP -MF $(DEPDIR)/benchmark_https-benchmark_https.Tpo -c -o benchmark_https-benchmark_https.o `test -f 'benchmark_https.c' || echo '$(srcdir)/'`benchmark_https.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/benchmark_https-benchmark_https.Tpo $(DEPDIR)/benchmark_https-benchmark_https.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='benchmark_https.c' object='benchmark_https-benchmark_https.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(benchmark_https_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o benchmark_https-benchmark_https.o `test -f 'benchmark_https.c' || echo '$(srcdir)/'`benchmark_https.c
+
+benchmark_https-benchmark_https.obj: benchmark_https.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(benchmark_https_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT benchmark_https-benchmark_https.obj -MD -MP -MF $(DEPDIR)/benchmark_https-benchmark_https.Tpo -c -o benchmark_https-benchmark_https.obj `if test -f 'benchmark_https.c'; then $(CYGPATH_W) 'benchmark_https.c'; else $(CYGPATH_W) '$(srcdir)/benchmark_https.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/benchmark_https-benchmark_https.Tpo $(DEPDIR)/benchmark_https-benchmark_https.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='benchmark_https.c' object='benchmark_https-benchmark_https.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(benchmark_https_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o benchmark_https-benchmark_https.obj `if test -f 'benchmark_https.c'; then $(CYGPATH_W) 'benchmark_https.c'; else $(CYGPATH_W) '$(srcdir)/benchmark_https.c'; fi`
+
+demo-demo.o: demo.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(demo_CPPFLAGS) $(CPPFLAGS) $(demo_CFLAGS) $(CFLAGS) -MT demo-demo.o -MD -MP -MF $(DEPDIR)/demo-demo.Tpo -c -o demo-demo.o `test -f 'demo.c' || echo '$(srcdir)/'`demo.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/demo-demo.Tpo $(DEPDIR)/demo-demo.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='demo.c' object='demo-demo.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(demo_CPPFLAGS) $(CPPFLAGS) $(demo_CFLAGS) $(CFLAGS) -c -o demo-demo.o `test -f 'demo.c' || echo '$(srcdir)/'`demo.c
+
+demo-demo.obj: demo.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(demo_CPPFLAGS) $(CPPFLAGS) $(demo_CFLAGS) $(CFLAGS) -MT demo-demo.obj -MD -MP -MF $(DEPDIR)/demo-demo.Tpo -c -o demo-demo.obj `if test -f 'demo.c'; then $(CYGPATH_W) 'demo.c'; else $(CYGPATH_W) '$(srcdir)/demo.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/demo-demo.Tpo $(DEPDIR)/demo-demo.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='demo.c' object='demo-demo.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(demo_CPPFLAGS) $(CPPFLAGS) $(demo_CFLAGS) $(CFLAGS) -c -o demo-demo.obj `if test -f 'demo.c'; then $(CYGPATH_W) 'demo.c'; else $(CYGPATH_W) '$(srcdir)/demo.c'; fi`
+
+demo_https-demo_https.o: demo_https.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(demo_https_CPPFLAGS) $(CPPFLAGS) $(demo_https_CFLAGS) $(CFLAGS) -MT demo_https-demo_https.o -MD -MP -MF $(DEPDIR)/demo_https-demo_https.Tpo -c -o demo_https-demo_https.o `test -f 'demo_https.c' || echo '$(srcdir)/'`demo_https.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/demo_https-demo_https.Tpo $(DEPDIR)/demo_https-demo_https.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='demo_https.c' object='demo_https-demo_https.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(demo_https_CPPFLAGS) $(CPPFLAGS) $(demo_https_CFLAGS) $(CFLAGS) -c -o demo_https-demo_https.o `test -f 'demo_https.c' || echo '$(srcdir)/'`demo_https.c
+
+demo_https-demo_https.obj: demo_https.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(demo_https_CPPFLAGS) $(CPPFLAGS) $(demo_https_CFLAGS) $(CFLAGS) -MT demo_https-demo_https.obj -MD -MP -MF $(DEPDIR)/demo_https-demo_https.Tpo -c -o demo_https-demo_https.obj `if test -f 'demo_https.c'; then $(CYGPATH_W) 'demo_https.c'; else $(CYGPATH_W) '$(srcdir)/demo_https.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/demo_https-demo_https.Tpo $(DEPDIR)/demo_https-demo_https.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='demo_https.c' object='demo_https-demo_https.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(demo_https_CPPFLAGS) $(CPPFLAGS) $(demo_https_CFLAGS) $(CFLAGS) -c -o demo_https-demo_https.obj `if test -f 'demo_https.c'; then $(CYGPATH_W) 'demo_https.c'; else $(CYGPATH_W) '$(srcdir)/demo_https.c'; fi`
+
+https_fileserver_example-https_fileserver_example.o: https_fileserver_example.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(https_fileserver_example_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT https_fileserver_example-https_fileserver_example.o -MD -MP -MF $(DEPDIR)/https_fileserver_example-https_fileserver_example.Tpo -c -o https_fileserver_example-https_fileserver_example.o `test -f 'https_fileserver_example.c' || echo '$(srcdir)/'`https_fileserver_example.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/https_fileserver_example-https_fileserver_example.Tpo $(DEPDIR)/https_fileserver_example-https_fileserver_example.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='https_fileserver_example.c' object='https_fileserver_example-https_fileserver_example.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(https_fileserver_example_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o https_fileserver_example-https_fileserver_example.o `test -f 'https_fileserver_example.c' || echo '$(srcdir)/'`https_fileserver_example.c
+
+https_fileserver_example-https_fileserver_example.obj: https_fileserver_example.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(https_fileserver_example_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT https_fileserver_example-https_fileserver_example.obj -MD -MP -MF $(DEPDIR)/https_fileserver_example-https_fileserver_example.Tpo -c -o https_fileserver_example-https_fileserver_example.obj `if test -f 'https_fileserver_example.c'; then $(CYGPATH_W) 'https_fileserver_example.c'; else $(CYGPATH_W) '$(srcdir)/https_fileserver_example.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/https_fileserver_example-https_fileserver_example.Tpo $(DEPDIR)/https_fileserver_example-https_fileserver_example.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='https_fileserver_example.c' object='https_fileserver_example-https_fileserver_example.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(https_fileserver_example_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o https_fileserver_example-https_fileserver_example.obj `if test -f 'https_fileserver_example.c'; then $(CYGPATH_W) 'https_fileserver_example.c'; else $(CYGPATH_W) '$(srcdir)/https_fileserver_example.c'; fi`
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+#     (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    $(am__make_dryrun) \
+	      || test -d "$(distdir)/$$subdir" \
+	      || $(MKDIR_P) "$(distdir)/$$subdir" \
+	      || exit 1; \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
+	mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+	check-am clean clean-generic clean-libtool \
+	clean-noinstPROGRAMS 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 \
+	installdirs-am maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+	uninstall-am
+
+
+# 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/src/examples/authorization_example.c b/src/examples/authorization_example.c
new file mode 100644
index 0000000..e6e69ad
--- /dev/null
+++ b/src/examples/authorization_example.c
@@ -0,0 +1,108 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2008 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file authorization_example.c
+ * @brief example for how to use libmicrohttpd with HTTP authentication
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif
+
+#define PAGE "<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>"
+
+#define DENIED "<html><head><title>Access denied</title></head><body>Access denied</body></html>"
+
+
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size, void **ptr)
+{
+  static int aptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+  char *user;
+  char *pass;
+  int fail;
+
+  if (0 != strcmp (method, "GET"))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+    {
+      /* do never respond on first call */
+      *ptr = &aptr;
+      return MHD_YES;
+    }
+  *ptr = NULL;                  /* reset when done */
+
+  /* require: "Aladdin" with password "open sesame" */
+  pass = NULL;
+  user = MHD_basic_auth_get_username_password (connection, &pass);
+  fail = ( (user == NULL) || (0 != strcmp (user, "Aladdin")) || (0 != strcmp (pass, "open sesame") ) );
+  if (fail)
+  {
+      response = MHD_create_response_from_buffer (strlen (DENIED),
+						  (void *) DENIED, 
+						  MHD_RESPMEM_PERSISTENT);
+      ret = MHD_queue_basic_auth_fail_response (connection,"TestRealm",response);
+    }
+  else
+    {
+      response = MHD_create_response_from_buffer (strlen (me),
+						  (void *) me, 
+						  MHD_RESPMEM_PERSISTENT);
+      ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+    }
+
+  MHD_destroy_response (response);
+  return ret;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+
+  if (argc != 3)
+    {
+      printf ("%s PORT SECONDS-TO-RUN\n", argv[0]);
+      return 1;
+    }
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        atoi (argv[1]),
+                        NULL, NULL, &ahc_echo, PAGE, MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  sleep (atoi (argv[2]));
+  MHD_stop_daemon (d);
+  return 0;
+}
diff --git a/src/examples/benchmark.c b/src/examples/benchmark.c
new file mode 100644
index 0000000..d287b2f
--- /dev/null
+++ b/src/examples/benchmark.c
@@ -0,0 +1,159 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2013 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+/**
+ * @file benchmark.c
+ * @brief minimal code to benchmark MHD GET performance
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+#define PAGE "<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>"
+
+
+#define SMALL (1024 * 128)
+
+/**
+ * Number of threads to run in the thread pool.  Should (roughly) match
+ * the number of cores on your system.
+ */
+#define NUMBER_OF_THREADS CPU_COUNT
+
+static unsigned int small_deltas[SMALL];
+
+static struct MHD_Response *response;
+
+
+
+/**
+ * Signature of the callback used by MHD to notify the
+ * application about completed requests.
+ *
+ * @param cls client-defined closure
+ * @param connection connection handle
+ * @param con_cls value as set by the last call to
+ *        the MHD_AccessHandlerCallback
+ * @param toe reason for request termination
+ * @see MHD_OPTION_NOTIFY_COMPLETED
+ */
+static void
+completed_callback (void *cls,
+		    struct MHD_Connection *connection,
+		    void **con_cls,
+		    enum MHD_RequestTerminationCode toe)
+{
+  struct timeval *tv = *con_cls;
+  struct timeval tve;
+  uint64_t delta;
+
+  if (NULL == tv)
+    return;
+  gettimeofday (&tve, NULL);
+
+  delta = 0;
+  if (tve.tv_usec >= tv->tv_usec)
+    delta += (tve.tv_sec - tv->tv_sec) * 1000000LL
+      + (tve.tv_usec - tv->tv_usec);
+  else
+    delta += (tve.tv_sec - tv->tv_sec) * 1000000LL
+      - tv->tv_usec + tve.tv_usec;
+  if (delta < SMALL)
+    small_deltas[delta]++;
+  else
+    fprintf (stdout, "D: %llu 1\n", (unsigned long long) delta);
+  free (tv);
+}
+
+
+static void *
+uri_logger_cb (void *cls,
+	       const char *uri)
+{
+  struct timeval *tv = malloc (sizeof (struct timeval));
+
+  if (NULL != tv)
+    gettimeofday (tv, NULL);
+  return tv;
+}
+
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size, void **ptr)
+{
+  if (0 != strcmp (method, "GET"))
+    return MHD_NO;              /* unexpected method */
+  return MHD_queue_response (connection, MHD_HTTP_OK, response);
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+  unsigned int i;
+
+  if (argc != 2)
+    {
+      printf ("%s PORT\n", argv[0]);
+      return 1;
+    }
+  response = MHD_create_response_from_buffer (strlen (PAGE),
+					      (void *) PAGE,
+					      MHD_RESPMEM_PERSISTENT);
+#if 0
+  (void) MHD_add_response_header (response,
+				  MHD_HTTP_HEADER_CONNECTION,
+				  "close");
+#endif
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_SUPPRESS_DATE_NO_CLOCK
+#if EPOLL_SUPPORT
+			| MHD_USE_EPOLL_LINUX_ONLY | MHD_USE_EPOLL_TURBO
+#endif
+			,
+                        atoi (argv[1]),
+                        NULL, NULL, &ahc_echo, NULL,
+			MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120,
+			MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) NUMBER_OF_THREADS,
+			MHD_OPTION_URI_LOG_CALLBACK, &uri_logger_cb, NULL,
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_callback, NULL,
+			MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 1000,
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  MHD_destroy_response (response);
+  for (i=0;i<SMALL;i++)
+    if (0 != small_deltas[i])
+      fprintf (stdout, "D: %d %u\n", i, small_deltas[i]);
+  return 0;
+}
diff --git a/src/examples/benchmark_https.c b/src/examples/benchmark_https.c
new file mode 100644
index 0000000..735a913
--- /dev/null
+++ b/src/examples/benchmark_https.c
@@ -0,0 +1,207 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2013 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+/**
+ * @file benchmark_https.c
+ * @brief minimal code to benchmark MHD GET performance with HTTPS
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+#define PAGE "<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>"
+
+
+#define SMALL (1024 * 128)
+
+/**
+ * Number of threads to run in the thread pool.  Should (roughly) match
+ * the number of cores on your system.
+ */
+#define NUMBER_OF_THREADS CPU_COUNT
+
+static unsigned int small_deltas[SMALL];
+
+static struct MHD_Response *response;
+
+
+
+/**
+ * Signature of the callback used by MHD to notify the
+ * application about completed requests.
+ *
+ * @param cls client-defined closure
+ * @param connection connection handle
+ * @param con_cls value as set by the last call to
+ *        the MHD_AccessHandlerCallback
+ * @param toe reason for request termination
+ * @see MHD_OPTION_NOTIFY_COMPLETED
+ */
+static void
+completed_callback (void *cls,
+		    struct MHD_Connection *connection,
+		    void **con_cls,
+		    enum MHD_RequestTerminationCode toe)
+{
+  struct timeval *tv = *con_cls;
+  struct timeval tve;
+  uint64_t delta;
+
+  if (NULL == tv)
+    return;
+  gettimeofday (&tve, NULL);
+
+  delta = 0;
+  if (tve.tv_usec >= tv->tv_usec)
+    delta += (tve.tv_sec - tv->tv_sec) * 1000000LL
+      + (tve.tv_usec - tv->tv_usec);
+  else
+    delta += (tve.tv_sec - tv->tv_sec) * 1000000LL
+      - tv->tv_usec + tve.tv_usec;
+  if (delta < SMALL)
+    small_deltas[delta]++;
+  else
+    fprintf (stdout, "D: %llu 1\n", (unsigned long long) delta);
+  free (tv);
+}
+
+
+static void *
+uri_logger_cb (void *cls,
+	       const char *uri)
+{
+  struct timeval *tv = malloc (sizeof (struct timeval));
+
+  if (NULL != tv)
+    gettimeofday (tv, NULL);
+  return tv;
+}
+
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size, void **ptr)
+{
+  if (0 != strcmp (method, "GET"))
+    return MHD_NO;              /* unexpected method */
+  return MHD_queue_response (connection, MHD_HTTP_OK, response);
+}
+
+
+/* test server key */
+const char srv_signed_key_pem[] = "-----BEGIN RSA PRIVATE KEY-----\n"
+  "MIIEowIBAAKCAQEAvfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW\n"
+  "+K03KwEku55QvnUndwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8IL\n"
+  "q4sw32vo0fbMu5BZF49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ0\n"
+  "20Q5EAAEseD1YtWCIpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6\n"
+  "QYGGh1QmHRPAy3CBII6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6x\n"
+  "yoOl204xuekZOaG9RUPId74Rtmwfi1TLbBzo2wIDAQABAoIBADu09WSICNq5cMe4\n"
+  "+NKCLlgAT1NiQpLls1gKRbDhKiHU9j8QWNvWWkJWrCya4QdUfLCfeddCMeiQmv3K\n"
+  "lJMvDs+5OjJSHFoOsGiuW2Ias7IjnIojaJalfBml6frhJ84G27IXmdz6gzOiTIer\n"
+  "DjeAgcwBaKH5WwIay2TxIaScl7AwHBauQkrLcyb4hTmZuQh6ArVIN6+pzoVuORXM\n"
+  "bpeNWl2l/HSN3VtUN6aCAKbN/X3o0GavCCMn5Fa85uJFsab4ss/uP+2PusU71+zP\n"
+  "sBm6p/2IbGvF5k3VPDA7X5YX61sukRjRBihY8xSnNYx1UcoOsX6AiPnbhifD8+xQ\n"
+  "Tlf8oJUCgYEA0BTfzqNpr9Wxw5/QXaSdw7S/0eP5a0C/nwURvmfSzuTD4equzbEN\n"
+  "d+dI/s2JMxrdj/I4uoAfUXRGaabevQIjFzC9uyE3LaOyR2zhuvAzX+vVcs6bSXeU\n"
+  "pKpCAcN+3Z3evMaX2f+z/nfSUAl2i4J2R+/LQAWJW4KwRky/m+cxpfUCgYEA6bN1\n"
+  "b73bMgM8wpNt6+fcmS+5n0iZihygQ2U2DEud8nZJL4Nrm1dwTnfZfJBnkGj6+0Q0\n"
+  "cOwj2KS0/wcEdJBP0jucU4v60VMhp75AQeHqidIde0bTViSRo3HWKXHBIFGYoU3T\n"
+  "LyPyKndbqsOObnsFXHn56Nwhr2HLf6nw4taGQY8CgYBoSW36FLCNbd6QGvLFXBGt\n"
+  "2lMhEM8az/K58kJ4WXSwOLtr6MD/WjNT2tkcy0puEJLm6BFCd6A6pLn9jaKou/92\n"
+  "SfltZjJPb3GUlp9zn5tAAeSSi7YMViBrfuFiHObij5LorefBXISLjuYbMwL03MgH\n"
+  "Ocl2JtA2ywMp2KFXs8GQWQKBgFyIVv5ogQrbZ0pvj31xr9HjqK6d01VxIi+tOmpB\n"
+  "4ocnOLEcaxX12BzprW55ytfOCVpF1jHD/imAhb3YrHXu0fwe6DXYXfZV4SSG2vB7\n"
+  "IB9z14KBN5qLHjNGFpMQXHSMek+b/ftTU0ZnPh9uEM5D3YqRLVd7GcdUhHvG8P8Q\n"
+  "C9aXAoGBAJtID6h8wOGMP0XYX5YYnhlC7dOLfk8UYrzlp3xhqVkzKthTQTj6wx9R\n"
+  "GtC4k7U1ki8oJsfcIlBNXd768fqDVWjYju5rzShMpo8OCTS6ipAblKjCxPPVhIpv\n"
+  "tWPlbSn1qj6wylstJ5/3Z+ZW5H4wIKp5jmLiioDhcP0L/Ex3Zx8O\n"
+  "-----END RSA PRIVATE KEY-----\n";
+
+/* test server CA signed certificates */
+const char srv_signed_cert_pem[] = "-----BEGIN CERTIFICATE-----\n"
+  "MIIDGzCCAgWgAwIBAgIES0KCvTALBgkqhkiG9w0BAQUwFzEVMBMGA1UEAxMMdGVz\n"
+  "dF9jYV9jZXJ0MB4XDTEwMDEwNTAwMDcyNVoXDTQ1MDMxMjAwMDcyNVowFzEVMBMG\n"
+  "A1UEAxMMdGVzdF9jYV9jZXJ0MIIBHzALBgkqhkiG9w0BAQEDggEOADCCAQkCggEA\n"
+  "vfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW+K03KwEku55QvnUn\n"
+  "dwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8ILq4sw32vo0fbMu5BZ\n"
+  "F49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ020Q5EAAEseD1YtWC\n"
+  "IpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6QYGGh1QmHRPAy3CB\n"
+  "II6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6xyoOl204xuekZOaG9\n"
+  "RUPId74Rtmwfi1TLbBzo2wIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQM\n"
+  "MAoGCCsGAQUFBwMBMA8GA1UdDwEB/wQFAwMHIAAwHQYDVR0OBBYEFOFi4ilKOP1d\n"
+  "XHlWCMwmVKr7mgy8MB8GA1UdIwQYMBaAFP2olB4s2T/xuoQ5pT2RKojFwZo2MAsG\n"
+  "CSqGSIb3DQEBBQOCAQEAHVWPxazupbOkG7Did+dY9z2z6RjTzYvurTtEKQgzM2Vz\n"
+  "GQBA+3pZ3c5mS97fPIs9hZXfnQeelMeZ2XP1a+9vp35bJjZBBhVH+pqxjCgiUflg\n"
+  "A3Zqy0XwwVCgQLE2HyaU3DLUD/aeIFK5gJaOSdNTXZLv43K8kl4cqDbMeRpVTbkt\n"
+  "YmG4AyEOYRNKGTqMEJXJoxD5E3rBUNrVI/XyTjYrulxbNPcMWEHKNeeqWpKDYTFo\n"
+  "Bb01PCthGXiq/4A2RLAFosadzRa8SBpoSjPPfZ0b2w4MJpReHqKbR5+T2t6hzml6\n"
+  "4ToyOKPDmamiTuN5KzLN3cw7DQlvWMvqSOChPLnA3Q==\n"
+  "-----END CERTIFICATE-----\n";
+
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+  unsigned int i;
+
+  if (argc != 2)
+    {
+      printf ("%s PORT\n", argv[0]);
+      return 1;
+    }
+  response = MHD_create_response_from_buffer (strlen (PAGE),
+					      (void *) PAGE,
+					      MHD_RESPMEM_PERSISTENT);
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL
+#if EPOLL_SUPPORT
+			| MHD_USE_EPOLL_LINUX_ONLY  | MHD_USE_EPOLL_TURBO
+#endif
+			,
+                        atoi (argv[1]),
+                        NULL, NULL, &ahc_echo, NULL,
+			MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120,
+			MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) NUMBER_OF_THREADS,
+			MHD_OPTION_URI_LOG_CALLBACK, &uri_logger_cb, NULL,
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_callback, NULL,
+			MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 1000,
+                        MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
+                        MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  MHD_destroy_response (response);
+  for (i=0;i<SMALL;i++)
+    if (0 != small_deltas[i])
+      fprintf (stdout, "D: %d %u\n", i, small_deltas[i]);
+  return 0;
+}
diff --git a/src/examples/chunked_example.c b/src/examples/chunked_example.c
new file mode 100644
index 0000000..08bb82d
--- /dev/null
+++ b/src/examples/chunked_example.c
@@ -0,0 +1,94 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2015 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+/**
+ * @file chunked_example.c
+ * @brief example for generating chunked encoding with libmicrohttpd
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+
+
+static ssize_t
+callback (void *cls,
+          uint64_t pos,
+          char *buf,
+          size_t max)
+{
+  return MHD_CONTENT_READER_END_OF_STREAM;
+}
+
+
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size, void **ptr)
+{
+  static int aptr;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp (method, "GET"))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+    {
+      /* do never respond on first call */
+      *ptr = &aptr;
+      return MHD_YES;
+    }
+  *ptr = NULL;                  /* reset when done */
+  response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
+                                                1024,
+                                                &callback,
+                                                NULL,
+                                                NULL);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+
+  if (argc != 2)
+    {
+      printf ("%s PORT\n", argv[0]);
+      return 1;
+    }
+  d = MHD_start_daemon (// MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | MHD_USE_POLL,
+			MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+			// MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG | MHD_USE_POLL,
+			// MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        atoi (argv[1]),
+                        NULL, NULL, &ahc_echo, NULL,
+			MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120,
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  return 0;
+}
diff --git a/src/examples/demo.c b/src/examples/demo.c
new file mode 100644
index 0000000..80ca09c
--- /dev/null
+++ b/src/examples/demo.c
@@ -0,0 +1,907 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2013 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file demo.c
+ * @brief complex demonstration site: create directory index, offer
+ *        upload via form and HTTP POST, download with mime type detection
+ *        and error reporting (403, etc.) --- and all of this with
+ *        high-performance settings (large buffers, thread pool).
+ *        If you want to benchmark MHD, this code should be used to
+ *        run tests against.  Note that the number of threads may need
+ *        to be adjusted depending on the number of available cores.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <microhttpd.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <magic.h>
+#include <limits.h>
+#include <ctype.h>
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+/**
+ * Number of threads to run in the thread pool.  Should (roughly) match
+ * the number of cores on your system.
+ */
+#define NUMBER_OF_THREADS CPU_COUNT
+
+/**
+ * How many bytes of a file do we give to libmagic to determine the mime type?
+ * 16k might be a bit excessive, but ought not hurt performance much anyway,
+ * and should definitively be on the safe side.
+ */
+#define MAGIC_HEADER_SIZE (16 * 1024)
+
+
+/**
+ * Page returned for file-not-found.
+ */
+#define FILE_NOT_FOUND_PAGE "<html><head><title>File not found</title></head><body>File not found</body></html>"
+
+
+/**
+ * Page returned for internal errors.
+ */
+#define INTERNAL_ERROR_PAGE "<html><head><title>Internal error</title></head><body>Internal error</body></html>"
+
+
+/**
+ * Page returned for refused requests.
+ */
+#define REQUEST_REFUSED_PAGE "<html><head><title>Request refused</title></head><body>Request refused (file exists?)</body></html>"
+
+
+/**
+ * Head of index page.
+ */
+#define INDEX_PAGE_HEADER "<html>\n<head><title>Welcome</title></head>\n<body>\n"\
+   "<h1>Upload</h1>\n"\
+   "<form method=\"POST\" enctype=\"multipart/form-data\" action=\"/\">\n"\
+   "<dl><dt>Content type:</dt><dd>"\
+   "<input type=\"radio\" name=\"category\" value=\"books\">Book</input>"\
+   "<input type=\"radio\" name=\"category\" value=\"images\">Image</input>"\
+   "<input type=\"radio\" name=\"category\" value=\"music\">Music</input>"\
+   "<input type=\"radio\" name=\"category\" value=\"software\">Software</input>"\
+   "<input type=\"radio\" name=\"category\" value=\"videos\">Videos</input>\n"\
+   "<input type=\"radio\" name=\"category\" value=\"other\" checked>Other</input></dd>"\
+   "<dt>Language:</dt><dd>"\
+   "<input type=\"radio\" name=\"language\" value=\"no-lang\" checked>none</input>"\
+   "<input type=\"radio\" name=\"language\" value=\"en\">English</input>"\
+   "<input type=\"radio\" name=\"language\" value=\"de\">German</input>"\
+   "<input type=\"radio\" name=\"language\" value=\"fr\">French</input>"\
+   "<input type=\"radio\" name=\"language\" value=\"es\">Spanish</input></dd>\n"\
+   "<dt>File:</dt><dd>"\
+   "<input type=\"file\" name=\"upload\"/></dd></dl>"\
+   "<input type=\"submit\" value=\"Send!\"/>\n"\
+   "</form>\n"\
+   "<h1>Download</h1>\n"\
+   "<ol>\n"
+
+/**
+ * Footer of index page.
+ */
+#define INDEX_PAGE_FOOTER "</ol>\n</body>\n</html>"
+
+
+/**
+ * NULL-terminated array of supported upload categories.  Should match HTML
+ * in the form.
+ */
+static const char * const categories[] =
+  {
+    "books",
+    "images",
+    "music",
+    "software",
+    "videos",
+    "other",
+    NULL,
+  };
+
+
+/**
+ * Specification of a supported language.
+ */
+struct Language
+{
+  /**
+   * Directory name for the language.
+   */
+  const char *dirname;
+
+  /**
+   * Long name for humans.
+   */
+  const char *longname;
+
+};
+
+/**
+ * NULL-terminated array of supported upload categories.  Should match HTML
+ * in the form.
+ */
+static const struct Language languages[] =
+  {
+    { "no-lang", "No language specified" },
+    { "en", "English" },
+    { "de", "German" },
+    { "fr", "French" },
+    { "es", "Spanish" },
+    { NULL, NULL },
+  };
+
+
+/**
+ * Response returned if the requested file does not exist (or is not accessible).
+ */
+static struct MHD_Response *file_not_found_response;
+
+/**
+ * Response returned for internal errors.
+ */
+static struct MHD_Response *internal_error_response;
+
+/**
+ * Response returned for '/' (GET) to list the contents of the directory and allow upload.
+ */
+static struct MHD_Response *cached_directory_response;
+
+/**
+ * Response returned for refused uploads.
+ */
+static struct MHD_Response *request_refused_response;
+
+/**
+ * Mutex used when we update the cached directory response object.
+ */
+static pthread_mutex_t mutex;
+
+/**
+ * Global handle to MAGIC data.
+ */
+static magic_t magic;
+
+
+/**
+ * Mark the given response as HTML for the brower.
+ *
+ * @param response response to mark
+ */
+static void
+mark_as_html (struct MHD_Response *response)
+{
+  (void) MHD_add_response_header (response,
+				  MHD_HTTP_HEADER_CONTENT_TYPE,
+				  "text/html");
+}
+
+
+/**
+ * Replace the existing 'cached_directory_response' with the
+ * given response.
+ *
+ * @param response new directory response
+ */
+static void
+update_cached_response (struct MHD_Response *response)
+{
+  (void) pthread_mutex_lock (&mutex);
+  if (NULL != cached_directory_response)
+    MHD_destroy_response (cached_directory_response);
+  cached_directory_response = response;
+  (void) pthread_mutex_unlock (&mutex);
+}
+
+
+/**
+ * Context keeping the data for the response we're building.
+ */
+struct ResponseDataContext
+{
+  /**
+   * Response data string.
+   */
+  char *buf;
+
+  /**
+   * Number of bytes allocated for 'buf'.
+   */
+  size_t buf_len;
+
+  /**
+   * Current position where we append to 'buf'. Must be smaller or equal to 'buf_len'.
+   */
+  size_t off;
+
+};
+
+
+/**
+ * Create a listing of the files in 'dirname' in HTML.
+ *
+ * @param rdc where to store the list of files
+ * @param dirname name of the directory to list
+ * @return MHD_YES on success, MHD_NO on error
+ */
+static int
+list_directory (struct ResponseDataContext *rdc,
+		const char *dirname)
+{
+  char fullname[PATH_MAX];
+  struct stat sbuf;
+  DIR *dir;
+  struct dirent *de;
+
+  if (NULL == (dir = opendir (dirname)))
+    return MHD_NO;
+  while (NULL != (de = readdir (dir)))
+    {
+      if ('.' == de->d_name[0])
+	continue;
+      if (sizeof (fullname) <= (size_t)
+	  snprintf (fullname, sizeof (fullname),
+		    "%s/%s",
+		    dirname, de->d_name))
+	continue; /* ugh, file too long? how can this be!? */
+      if (0 != stat (fullname, &sbuf))
+	continue; /* ugh, failed to 'stat' */
+      if (! S_ISREG (sbuf.st_mode))
+	continue; /* not a regular file, skip */
+      if (rdc->off + 1024 > rdc->buf_len)
+	{
+	  void *r;
+
+	  if ( (2 * rdc->buf_len + 1024) < rdc->buf_len)
+	    break; /* more than SIZE_T _index_ size? Too big for us */
+	  rdc->buf_len = 2 * rdc->buf_len + 1024;
+	  if (NULL == (r = realloc (rdc->buf, rdc->buf_len)))
+	    break; /* out of memory */
+	  rdc->buf = r;
+	}
+      rdc->off += snprintf (&rdc->buf[rdc->off],
+			    rdc->buf_len - rdc->off,
+			    "<li><a href=\"/%s\">%s</a></li>\n",
+			    fullname,
+			    de->d_name);
+    }
+  (void) closedir (dir);
+  return MHD_YES;
+}
+
+
+/**
+ * Re-scan our local directory and re-build the index.
+ */
+static void
+update_directory ()
+{
+  static size_t initial_allocation = 32 * 1024; /* initial size for response buffer */
+  struct MHD_Response *response;
+  struct ResponseDataContext rdc;
+  unsigned int language_idx;
+  unsigned int category_idx;
+  const struct Language *language;
+  const char *category;
+  char dir_name[128];
+  struct stat sbuf;
+
+  rdc.buf_len = initial_allocation;
+  if (NULL == (rdc.buf = malloc (rdc.buf_len)))
+    {
+      update_cached_response (NULL);
+      return;
+    }
+  rdc.off = snprintf (rdc.buf, rdc.buf_len,
+		      "%s",
+		      INDEX_PAGE_HEADER);
+  for (language_idx = 0; NULL != languages[language_idx].dirname; language_idx++)
+    {
+      language = &languages[language_idx];
+
+      if (0 != stat (language->dirname, &sbuf))
+	continue; /* empty */
+      /* we ensured always +1k room, filenames are ~256 bytes,
+	 so there is always still enough space for the header
+	 without need for an additional reallocation check. */
+      rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
+			   "<h2>%s</h2>\n",
+			   language->longname);
+      for (category_idx = 0; NULL != categories[category_idx]; category_idx++)
+	{
+	  category = categories[category_idx];
+	  snprintf (dir_name, sizeof (dir_name),
+		    "%s/%s",
+		    language->dirname,
+		    category);
+	  if (0 != stat (dir_name, &sbuf))
+	    continue; /* empty */
+
+	  /* we ensured always +1k room, filenames are ~256 bytes,
+	     so there is always still enough space for the header
+	     without need for an additional reallocation check. */
+	  rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
+			       "<h3>%s</h3>\n",
+			       category);
+
+	  if (MHD_NO == list_directory (&rdc, dir_name))
+	    {
+	      free (rdc.buf);
+	      update_cached_response (NULL);
+	      return;
+	    }
+	}
+    }
+  /* we ensured always +1k room, filenames are ~256 bytes,
+     so there is always still enough space for the footer
+     without need for a final reallocation check. */
+  rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
+		       "%s",
+		       INDEX_PAGE_FOOTER);
+  initial_allocation = rdc.buf_len; /* remember for next time */
+  response = MHD_create_response_from_buffer (rdc.off,
+					      rdc.buf,
+					      MHD_RESPMEM_MUST_FREE);
+  mark_as_html (response);
+#if FORCE_CLOSE
+  (void) MHD_add_response_header (response,
+				  MHD_HTTP_HEADER_CONNECTION,
+				  "close");
+#endif
+  update_cached_response (response);
+}
+
+
+/**
+ * Context we keep for an upload.
+ */
+struct UploadContext
+{
+  /**
+   * Handle where we write the uploaded file to.
+   */
+  int fd;
+
+  /**
+   * Name of the file on disk (used to remove on errors).
+   */
+  char *filename;
+
+  /**
+   * Language for the upload.
+   */
+  char *language;
+
+  /**
+   * Category for the upload.
+   */
+  char *category;
+
+  /**
+   * Post processor we're using to process the upload.
+   */
+  struct MHD_PostProcessor *pp;
+
+  /**
+   * Handle to connection that we're processing the upload for.
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * Response to generate, NULL to use directory.
+   */
+  struct MHD_Response *response;
+};
+
+
+/**
+ * Append the 'size' bytes from 'data' to '*ret', adding
+ * 0-termination.  If '*ret' is NULL, allocate an empty string first.
+ *
+ * @param ret string to update, NULL or 0-terminated
+ * @param data data to append
+ * @param size number of bytes in 'data'
+ * @return MHD_NO on allocation failure, MHD_YES on success
+ */
+static int
+do_append (char **ret,
+	   const char *data,
+	   size_t size)
+{
+  char *buf;
+  size_t old_len;
+
+  if (NULL == *ret)
+    old_len = 0;
+  else
+    old_len = strlen (*ret);
+  buf = malloc (old_len + size + 1);
+  if (NULL == buf)
+    return MHD_NO;
+  memcpy (buf, *ret, old_len);
+  if (NULL != *ret)
+    free (*ret);
+  memcpy (&buf[old_len], data, size);
+  buf[old_len + size] = '\0';
+  *ret = buf;
+  return MHD_YES;
+}
+
+
+/**
+ * Iterator over key-value pairs where the value
+ * maybe made available in increments and/or may
+ * not be zero-terminated.  Used for processing
+ * POST data.
+ *
+ * @param cls user-specified closure
+ * @param kind type of the value, always MHD_POSTDATA_KIND when called from MHD
+ * @param key 0-terminated key for the value
+ * @param filename name of the uploaded file, NULL if not known
+ * @param content_type mime-type of the data, NULL if not known
+ * @param transfer_encoding encoding of the data, NULL if not known
+ * @param data pointer to size bytes of data at the
+ *              specified offset
+ * @param off offset of data in the overall value
+ * @param size number of bytes in data available
+ * @return MHD_YES to continue iterating,
+ *         MHD_NO to abort the iteration
+ */
+static int
+process_upload_data (void *cls,
+		     enum MHD_ValueKind kind,
+		     const char *key,
+		     const char *filename,
+		     const char *content_type,
+		     const char *transfer_encoding,
+		     const char *data,
+		     uint64_t off,
+		     size_t size)
+{
+  struct UploadContext *uc = cls;
+  int i;
+
+  if (0 == strcmp (key, "category"))
+    return do_append (&uc->category, data, size);
+  if (0 == strcmp (key, "language"))
+    return do_append (&uc->language, data, size);
+  if (0 != strcmp (key, "upload"))
+    {
+      fprintf (stderr,
+	       "Ignoring unexpected form value `%s'\n",
+	       key);
+      return MHD_YES; /* ignore */
+    }
+  if (NULL == filename)
+    {
+      fprintf (stderr, "No filename, aborting upload\n");
+      return MHD_NO; /* no filename, error */
+    }
+  if ( (NULL == uc->category) ||
+       (NULL == uc->language) )
+    {
+      fprintf (stderr,
+	       "Missing form data for upload `%s'\n",
+	       filename);
+      uc->response = request_refused_response;
+      return MHD_NO;
+    }
+  if (-1 == uc->fd)
+    {
+      char fn[PATH_MAX];
+
+      if ( (NULL != strstr (filename, "..")) ||
+	   (NULL != strchr (filename, '/')) ||
+	   (NULL != strchr (filename, '\\')) )
+	{
+	  uc->response = request_refused_response;
+	  return MHD_NO;
+	}
+      /* create directories -- if they don't exist already */
+#ifdef WINDOWS
+      (void) mkdir (uc->language);
+#else
+      (void) mkdir (uc->language, S_IRWXU);
+#endif
+      snprintf (fn, sizeof (fn),
+		"%s/%s",
+		uc->language,
+		uc->category);
+#ifdef WINDOWS
+      (void) mkdir (fn);
+#else
+      (void) mkdir (fn, S_IRWXU);
+#endif
+      /* open file */
+      snprintf (fn, sizeof (fn),
+		"%s/%s/%s",
+		uc->language,
+		uc->category,
+		filename);
+      for (i=strlen (fn)-1;i>=0;i--)
+	if (! isprint ((int) fn[i]))
+	  fn[i] = '_';
+      uc->fd = open (fn,
+		     O_CREAT | O_EXCL
+#if O_LARGEFILE
+		     | O_LARGEFILE
+#endif
+		     | O_WRONLY,
+		     S_IRUSR | S_IWUSR);
+      if (-1 == uc->fd)
+	{
+	  fprintf (stderr,
+		   "Error opening file `%s' for upload: %s\n",
+		   fn,
+		   strerror (errno));
+	  uc->response = request_refused_response;
+	  return MHD_NO;
+	}
+      uc->filename = strdup (fn);
+    }
+  if ( (0 != size) &&
+       (size != (size_t) write (uc->fd, data, size)) )
+    {
+      /* write failed; likely: disk full */
+      fprintf (stderr,
+	       "Error writing to file `%s': %s\n",
+	       uc->filename,
+	       strerror (errno));
+      uc->response = internal_error_response;
+      close (uc->fd);
+      uc->fd = -1;
+      if (NULL != uc->filename)
+	{
+	  unlink (uc->filename);
+	  free (uc->filename);
+	  uc->filename = NULL;
+	}
+      return MHD_NO;
+    }
+  return MHD_YES;
+}
+
+
+/**
+ * Function called whenever a request was completed.
+ * Used to clean up 'struct UploadContext' objects.
+ *
+ * @param cls client-defined closure, NULL
+ * @param connection connection handle
+ * @param con_cls value as set by the last call to
+ *        the MHD_AccessHandlerCallback, points to NULL if this was
+ *            not an upload
+ * @param toe reason for request termination
+ */
+static void
+response_completed_callback (void *cls,
+			     struct MHD_Connection *connection,
+			     void **con_cls,
+			     enum MHD_RequestTerminationCode toe)
+{
+  struct UploadContext *uc = *con_cls;
+
+  if (NULL == uc)
+    return; /* this request wasn't an upload request */
+  if (NULL != uc->pp)
+    {
+      MHD_destroy_post_processor (uc->pp);
+      uc->pp = NULL;
+    }
+  if (-1 != uc->fd)
+  {
+    (void) close (uc->fd);
+    if (NULL != uc->filename)
+      {
+	fprintf (stderr,
+		 "Upload of file `%s' failed (incomplete or aborted), removing file.\n",
+		 uc->filename);
+	(void) unlink (uc->filename);
+      }
+  }
+  if (NULL != uc->filename)
+    free (uc->filename);
+  free (uc);
+}
+
+
+/**
+ * Return the current directory listing.
+ *
+ * @param connection connection to return the directory for
+ * @return MHD_YES on success, MHD_NO on error
+ */
+static int
+return_directory_response (struct MHD_Connection *connection)
+{
+  int ret;
+
+  (void) pthread_mutex_lock (&mutex);
+  if (NULL == cached_directory_response)
+    ret = MHD_queue_response (connection,
+			      MHD_HTTP_INTERNAL_SERVER_ERROR,
+			      internal_error_response);
+  else
+    ret = MHD_queue_response (connection,
+			      MHD_HTTP_OK,
+			      cached_directory_response);
+  (void) pthread_mutex_unlock (&mutex);
+  return ret;
+}
+
+
+/**
+ * Main callback from MHD, used to generate the page.
+ *
+ * @param cls NULL
+ * @param connection connection handle
+ * @param url requested URL
+ * @param method GET, PUT, POST, etc.
+ * @param version HTTP version
+ * @param upload_data data from upload (PUT/POST)
+ * @param upload_data_size number of bytes in "upload_data"
+ * @param ptr our context
+ * @return MHD_YES on success, MHD_NO to drop connection
+ */
+static int
+generate_page (void *cls,
+	       struct MHD_Connection *connection,
+	       const char *url,
+	       const char *method,
+	       const char *version,
+	       const char *upload_data,
+	       size_t *upload_data_size, void **ptr)
+{
+  struct MHD_Response *response;
+  int ret;
+  int fd;
+  struct stat buf;
+
+  if (0 != strcmp (url, "/"))
+    {
+      /* should be file download */
+      char file_data[MAGIC_HEADER_SIZE];
+      ssize_t got;
+      const char *mime;
+
+      if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
+	return MHD_NO;  /* unexpected method (we're not polite...) */
+      if ( (0 == stat (&url[1], &buf)) &&
+	   (NULL == strstr (&url[1], "..")) &&
+	   ('/' != url[1]))
+	fd = open (&url[1], O_RDONLY);
+      else
+	fd = -1;
+      if (-1 == fd)
+	return MHD_queue_response (connection,
+				   MHD_HTTP_NOT_FOUND,
+				   file_not_found_response);
+      /* read beginning of the file to determine mime type  */
+      got = read (fd, file_data, sizeof (file_data));
+      if (-1 != got)
+	mime = magic_buffer (magic, file_data, got);
+      else
+	mime = NULL;
+      (void) lseek (fd, 0, SEEK_SET);
+
+      if (NULL == (response = MHD_create_response_from_fd (buf.st_size,
+							   fd)))
+	{
+	  /* internal error (i.e. out of memory) */
+	  (void) close (fd);
+	  return MHD_NO;
+	}
+
+      /* add mime type if we had one */
+      if (NULL != mime)
+	(void) MHD_add_response_header (response,
+					MHD_HTTP_HEADER_CONTENT_TYPE,
+					mime);
+      ret = MHD_queue_response (connection,
+				MHD_HTTP_OK,
+				response);
+      MHD_destroy_response (response);
+      return ret;
+    }
+
+  if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
+    {
+      /* upload! */
+      struct UploadContext *uc = *ptr;
+
+      if (NULL == uc)
+	{
+	  if (NULL == (uc = malloc (sizeof (struct UploadContext))))
+	    return MHD_NO; /* out of memory, close connection */
+	  memset (uc, 0, sizeof (struct UploadContext));
+          uc->fd = -1;
+	  uc->connection = connection;
+	  uc->pp = MHD_create_post_processor (connection,
+					      64 * 1024 /* buffer size */,
+					      &process_upload_data, uc);
+	  if (NULL == uc->pp)
+	    {
+	      /* out of memory, close connection */
+	      free (uc);
+	      return MHD_NO;
+	    }
+	  *ptr = uc;
+	  return MHD_YES;
+	}
+      if (0 != *upload_data_size)
+	{
+	  if (NULL == uc->response)
+	    (void) MHD_post_process (uc->pp,
+				     upload_data,
+				     *upload_data_size);
+	  *upload_data_size = 0;
+	  return MHD_YES;
+	}
+      /* end of upload, finish it! */
+      MHD_destroy_post_processor (uc->pp);
+      uc->pp = NULL;
+      if (-1 != uc->fd)
+	{
+	  close (uc->fd);
+	  uc->fd = -1;
+	}
+      if (NULL != uc->response)
+	{
+	  return MHD_queue_response (connection,
+				     MHD_HTTP_FORBIDDEN,
+				     uc->response);
+	}
+      else
+	{
+	  update_directory ();
+	  return return_directory_response (connection);
+	}
+    }
+  if (0 == strcmp (method, MHD_HTTP_METHOD_GET))
+  {
+    return return_directory_response (connection);
+  }
+
+  /* unexpected request, refuse */
+  return MHD_queue_response (connection,
+			     MHD_HTTP_FORBIDDEN,
+			     request_refused_response);
+}
+
+
+/**
+ * Function called if we get a SIGPIPE. Does nothing.
+ *
+ * @param sig will be SIGPIPE (ignored)
+ */
+static void
+catcher (int sig)
+{
+  /* do nothing */
+}
+
+
+/**
+ * setup handlers to ignore SIGPIPE.
+ */
+#ifndef MINGW
+static void
+ignore_sigpipe ()
+{
+  struct sigaction oldsig;
+  struct sigaction sig;
+
+  sig.sa_handler = &catcher;
+  sigemptyset (&sig.sa_mask);
+#ifdef SA_INTERRUPT
+  sig.sa_flags = SA_INTERRUPT;  /* SunOS */
+#else
+  sig.sa_flags = SA_RESTART;
+#endif
+  if (0 != sigaction (SIGPIPE, &sig, &oldsig))
+    fprintf (stderr,
+             "Failed to install SIGPIPE handler: %s\n", strerror (errno));
+}
+#endif
+
+
+/**
+ * Entry point to demo.  Note: this HTTP server will make all
+ * files in the current directory and its subdirectories available
+ * to anyone.  Press ENTER to stop the server once it has started.
+ *
+ * @param argc number of arguments in argv
+ * @param argv first and only argument should be the port number
+ * @return 0 on success
+ */
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+  unsigned int port;
+
+  if ( (argc != 2) ||
+       (1 != sscanf (argv[1], "%u", &port)) ||
+       (UINT16_MAX < port) )
+    {
+      fprintf (stderr,
+	       "%s PORT\n", argv[0]);
+      return 1;
+    }
+  #ifndef MINGW
+  ignore_sigpipe ();
+  #endif
+  magic = magic_open (MAGIC_MIME_TYPE);
+  (void) magic_load (magic, NULL);
+
+  (void) pthread_mutex_init (&mutex, NULL);
+  file_not_found_response = MHD_create_response_from_buffer (strlen (FILE_NOT_FOUND_PAGE),
+							     (void *) FILE_NOT_FOUND_PAGE,
+							     MHD_RESPMEM_PERSISTENT);
+  mark_as_html (file_not_found_response);
+  request_refused_response = MHD_create_response_from_buffer (strlen (REQUEST_REFUSED_PAGE),
+							     (void *) REQUEST_REFUSED_PAGE,
+							     MHD_RESPMEM_PERSISTENT);
+  mark_as_html (request_refused_response);
+  internal_error_response = MHD_create_response_from_buffer (strlen (INTERNAL_ERROR_PAGE),
+							     (void *) INTERNAL_ERROR_PAGE,
+							     MHD_RESPMEM_PERSISTENT);
+  mark_as_html (internal_error_response);
+  update_directory ();
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG
+#if EPOLL_SUPPORT
+			| MHD_USE_EPOLL_LINUX_ONLY
+#endif
+			,
+                        port,
+                        NULL, NULL,
+			&generate_page, NULL,
+			MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (256 * 1024),
+#if PRODUCTION
+			MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) (64),
+#endif
+			MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) (120 /* seconds */),
+			MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) NUMBER_OF_THREADS,
+			MHD_OPTION_NOTIFY_COMPLETED, &response_completed_callback, NULL,
+			MHD_OPTION_END);
+  if (NULL == d)
+    return 1;
+  fprintf (stderr, "HTTP server running. Press ENTER to stop the server\n");
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  MHD_destroy_response (file_not_found_response);
+  MHD_destroy_response (request_refused_response);
+  MHD_destroy_response (internal_error_response);
+  update_cached_response (NULL);
+  (void) pthread_mutex_destroy (&mutex);
+  magic_close (magic);
+  return 0;
+}
+
+/* end of demo.c */
diff --git a/src/examples/demo_https.c b/src/examples/demo_https.c
new file mode 100644
index 0000000..f34a715
--- /dev/null
+++ b/src/examples/demo_https.c
@@ -0,0 +1,960 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2013 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file demo_https.c
+ * @brief complex demonstration site: create directory index, offer
+ *        upload via form and HTTP POST, download with mime type detection
+ *        and error reporting (403, etc.) --- and all of this with
+ *        high-performance settings (large buffers, thread pool).
+ *        If you want to benchmark MHD, this code should be used to
+ *        run tests against.  Note that the number of threads may need
+ *        to be adjusted depending on the number of available cores.
+ *        Logic is identical to demo.c, just adds HTTPS support.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <microhttpd.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <magic.h>
+#include <limits.h>
+#include <ctype.h>
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+/**
+ * Number of threads to run in the thread pool.  Should (roughly) match
+ * the number of cores on your system.
+ */
+#define NUMBER_OF_THREADS CPU_COUNT
+
+/**
+ * How many bytes of a file do we give to libmagic to determine the mime type?
+ * 16k might be a bit excessive, but ought not hurt performance much anyway,
+ * and should definitively be on the safe side.
+ */
+#define MAGIC_HEADER_SIZE (16 * 1024)
+
+
+/**
+ * Page returned for file-not-found.
+ */
+#define FILE_NOT_FOUND_PAGE "<html><head><title>File not found</title></head><body>File not found</body></html>"
+
+
+/**
+ * Page returned for internal errors.
+ */
+#define INTERNAL_ERROR_PAGE "<html><head><title>Internal error</title></head><body>Internal error</body></html>"
+
+
+/**
+ * Page returned for refused requests.
+ */
+#define REQUEST_REFUSED_PAGE "<html><head><title>Request refused</title></head><body>Request refused (file exists?)</body></html>"
+
+
+/**
+ * Head of index page.
+ */
+#define INDEX_PAGE_HEADER "<html>\n<head><title>Welcome</title></head>\n<body>\n"\
+   "<h1>Upload</h1>\n"\
+   "<form method=\"POST\" enctype=\"multipart/form-data\" action=\"/\">\n"\
+   "<dl><dt>Content type:</dt><dd>"\
+   "<input type=\"radio\" name=\"category\" value=\"books\">Book</input>"\
+   "<input type=\"radio\" name=\"category\" value=\"images\">Image</input>"\
+   "<input type=\"radio\" name=\"category\" value=\"music\">Music</input>"\
+   "<input type=\"radio\" name=\"category\" value=\"software\">Software</input>"\
+   "<input type=\"radio\" name=\"category\" value=\"videos\">Videos</input>\n"\
+   "<input type=\"radio\" name=\"category\" value=\"other\" checked>Other</input></dd>"\
+   "<dt>Language:</dt><dd>"\
+   "<input type=\"radio\" name=\"language\" value=\"no-lang\" checked>none</input>"\
+   "<input type=\"radio\" name=\"language\" value=\"en\">English</input>"\
+   "<input type=\"radio\" name=\"language\" value=\"de\">German</input>"\
+   "<input type=\"radio\" name=\"language\" value=\"fr\">French</input>"\
+   "<input type=\"radio\" name=\"language\" value=\"es\">Spanish</input></dd>\n"\
+   "<dt>File:</dt><dd>"\
+   "<input type=\"file\" name=\"upload\"/></dd></dl>"\
+   "<input type=\"submit\" value=\"Send!\"/>\n"\
+   "</form>\n"\
+   "<h1>Download</h1>\n"\
+   "<ol>\n"
+
+/**
+ * Footer of index page.
+ */
+#define INDEX_PAGE_FOOTER "</ol>\n</body>\n</html>"
+
+
+/**
+ * NULL-terminated array of supported upload categories.  Should match HTML
+ * in the form.
+ */
+static const char * const categories[] =
+  {
+    "books",
+    "images",
+    "music",
+    "software",
+    "videos",
+    "other",
+    NULL,
+  };
+
+
+/**
+ * Specification of a supported language.
+ */
+struct Language
+{
+  /**
+   * Directory name for the language.
+   */
+  const char *dirname;
+
+  /**
+   * Long name for humans.
+   */
+  const char *longname;
+
+};
+
+/**
+ * NULL-terminated array of supported upload categories.  Should match HTML
+ * in the form.
+ */
+static const struct Language languages[] =
+  {
+    { "no-lang", "No language specified" },
+    { "en", "English" },
+    { "de", "German" },
+    { "fr", "French" },
+    { "es", "Spanish" },
+    { NULL, NULL },
+  };
+
+
+/**
+ * Response returned if the requested file does not exist (or is not accessible).
+ */
+static struct MHD_Response *file_not_found_response;
+
+/**
+ * Response returned for internal errors.
+ */
+static struct MHD_Response *internal_error_response;
+
+/**
+ * Response returned for '/' (GET) to list the contents of the directory and allow upload.
+ */
+static struct MHD_Response *cached_directory_response;
+
+/**
+ * Response returned for refused uploads.
+ */
+static struct MHD_Response *request_refused_response;
+
+/**
+ * Mutex used when we update the cached directory response object.
+ */
+static pthread_mutex_t mutex;
+
+/**
+ * Global handle to MAGIC data.
+ */
+static magic_t magic;
+
+
+/**
+ * Mark the given response as HTML for the brower.
+ *
+ * @param response response to mark
+ */
+static void
+mark_as_html (struct MHD_Response *response)
+{
+  (void) MHD_add_response_header (response,
+				  MHD_HTTP_HEADER_CONTENT_TYPE,
+				  "text/html");
+}
+
+
+/**
+ * Replace the existing 'cached_directory_response' with the
+ * given response.
+ *
+ * @param response new directory response
+ */
+static void
+update_cached_response (struct MHD_Response *response)
+{
+  (void) pthread_mutex_lock (&mutex);
+  if (NULL != cached_directory_response)
+    MHD_destroy_response (cached_directory_response);
+  cached_directory_response = response;
+  (void) pthread_mutex_unlock (&mutex);
+}
+
+
+/**
+ * Context keeping the data for the response we're building.
+ */
+struct ResponseDataContext
+{
+  /**
+   * Response data string.
+   */
+  char *buf;
+
+  /**
+   * Number of bytes allocated for 'buf'.
+   */
+  size_t buf_len;
+
+  /**
+   * Current position where we append to 'buf'. Must be smaller or equal to 'buf_len'.
+   */
+  size_t off;
+
+};
+
+
+/**
+ * Create a listing of the files in 'dirname' in HTML.
+ *
+ * @param rdc where to store the list of files
+ * @param dirname name of the directory to list
+ * @return MHD_YES on success, MHD_NO on error
+ */
+static int
+list_directory (struct ResponseDataContext *rdc,
+		const char *dirname)
+{
+  char fullname[PATH_MAX];
+  struct stat sbuf;
+  DIR *dir;
+  struct dirent *de;
+
+  if (NULL == (dir = opendir (dirname)))
+    return MHD_NO;
+  while (NULL != (de = readdir (dir)))
+    {
+      if ('.' == de->d_name[0])
+	continue;
+      if (sizeof (fullname) <= (size_t)
+	  snprintf (fullname, sizeof (fullname),
+		    "%s/%s",
+		    dirname, de->d_name))
+	continue; /* ugh, file too long? how can this be!? */
+      if (0 != stat (fullname, &sbuf))
+	continue; /* ugh, failed to 'stat' */
+      if (! S_ISREG (sbuf.st_mode))
+	continue; /* not a regular file, skip */
+      if (rdc->off + 1024 > rdc->buf_len)
+	{
+	  void *r;
+
+	  if ( (2 * rdc->buf_len + 1024) < rdc->buf_len)
+	    break; /* more than SIZE_T _index_ size? Too big for us */
+	  rdc->buf_len = 2 * rdc->buf_len + 1024;
+	  if (NULL == (r = realloc (rdc->buf, rdc->buf_len)))
+	    break; /* out of memory */
+	  rdc->buf = r;
+	}
+      rdc->off += snprintf (&rdc->buf[rdc->off],
+			    rdc->buf_len - rdc->off,
+			    "<li><a href=\"/%s\">%s</a></li>\n",
+			    fullname,
+			    de->d_name);
+    }
+  (void) closedir (dir);
+  return MHD_YES;
+}
+
+
+/**
+ * Re-scan our local directory and re-build the index.
+ */
+static void
+update_directory ()
+{
+  static size_t initial_allocation = 32 * 1024; /* initial size for response buffer */
+  struct MHD_Response *response;
+  struct ResponseDataContext rdc;
+  unsigned int language_idx;
+  unsigned int category_idx;
+  const struct Language *language;
+  const char *category;
+  char dir_name[128];
+  struct stat sbuf;
+
+  rdc.buf_len = initial_allocation;
+  if (NULL == (rdc.buf = malloc (rdc.buf_len)))
+    {
+      update_cached_response (NULL);
+      return;
+    }
+  rdc.off = snprintf (rdc.buf, rdc.buf_len,
+		      "%s",
+		      INDEX_PAGE_HEADER);
+  for (language_idx = 0; NULL != languages[language_idx].dirname; language_idx++)
+    {
+      language = &languages[language_idx];
+
+      if (0 != stat (language->dirname, &sbuf))
+	continue; /* empty */
+      /* we ensured always +1k room, filenames are ~256 bytes,
+	 so there is always still enough space for the header
+	 without need for an additional reallocation check. */
+      rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
+			   "<h2>%s</h2>\n",
+			   language->longname);
+      for (category_idx = 0; NULL != categories[category_idx]; category_idx++)
+	{
+	  category = categories[category_idx];
+	  snprintf (dir_name, sizeof (dir_name),
+		    "%s/%s",
+		    language->dirname,
+		    category);
+	  if (0 != stat (dir_name, &sbuf))
+	    continue; /* empty */
+
+	  /* we ensured always +1k room, filenames are ~256 bytes,
+	     so there is always still enough space for the header
+	     without need for an additional reallocation check. */
+	  rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
+			       "<h3>%s</h3>\n",
+			       category);
+
+	  if (MHD_NO == list_directory (&rdc, dir_name))
+	    {
+	      free (rdc.buf);
+	      update_cached_response (NULL);
+	      return;
+	    }
+	}
+    }
+  /* we ensured always +1k room, filenames are ~256 bytes,
+     so there is always still enough space for the footer
+     without need for a final reallocation check. */
+  rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
+		       "%s",
+		       INDEX_PAGE_FOOTER);
+  initial_allocation = rdc.buf_len; /* remember for next time */
+  response = MHD_create_response_from_buffer (rdc.off,
+					      rdc.buf,
+					      MHD_RESPMEM_MUST_FREE);
+  mark_as_html (response);
+#if FORCE_CLOSE
+  (void) MHD_add_response_header (response,
+				  MHD_HTTP_HEADER_CONNECTION,
+				  "close");
+#endif
+  update_cached_response (response);
+}
+
+
+/**
+ * Context we keep for an upload.
+ */
+struct UploadContext
+{
+  /**
+   * Handle where we write the uploaded file to.
+   */
+  int fd;
+
+  /**
+   * Name of the file on disk (used to remove on errors).
+   */
+  char *filename;
+
+  /**
+   * Language for the upload.
+   */
+  char *language;
+
+  /**
+   * Category for the upload.
+   */
+  char *category;
+
+  /**
+   * Post processor we're using to process the upload.
+   */
+  struct MHD_PostProcessor *pp;
+
+  /**
+   * Handle to connection that we're processing the upload for.
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * Response to generate, NULL to use directory.
+   */
+  struct MHD_Response *response;
+};
+
+
+/**
+ * Append the 'size' bytes from 'data' to '*ret', adding
+ * 0-termination.  If '*ret' is NULL, allocate an empty string first.
+ *
+ * @param ret string to update, NULL or 0-terminated
+ * @param data data to append
+ * @param size number of bytes in 'data'
+ * @return MHD_NO on allocation failure, MHD_YES on success
+ */
+static int
+do_append (char **ret,
+	   const char *data,
+	   size_t size)
+{
+  char *buf;
+  size_t old_len;
+
+  if (NULL == *ret)
+    old_len = 0;
+  else
+    old_len = strlen (*ret);
+  buf = malloc (old_len + size + 1);
+  if (NULL == buf)
+    return MHD_NO;
+  memcpy (buf, *ret, old_len);
+  if (NULL != *ret)
+    free (*ret);
+  memcpy (&buf[old_len], data, size);
+  buf[old_len + size] = '\0';
+  *ret = buf;
+  return MHD_YES;
+}
+
+
+/**
+ * Iterator over key-value pairs where the value
+ * maybe made available in increments and/or may
+ * not be zero-terminated.  Used for processing
+ * POST data.
+ *
+ * @param cls user-specified closure
+ * @param kind type of the value, always MHD_POSTDATA_KIND when called from MHD
+ * @param key 0-terminated key for the value
+ * @param filename name of the uploaded file, NULL if not known
+ * @param content_type mime-type of the data, NULL if not known
+ * @param transfer_encoding encoding of the data, NULL if not known
+ * @param data pointer to size bytes of data at the
+ *              specified offset
+ * @param off offset of data in the overall value
+ * @param size number of bytes in data available
+ * @return MHD_YES to continue iterating,
+ *         MHD_NO to abort the iteration
+ */
+static int
+process_upload_data (void *cls,
+		     enum MHD_ValueKind kind,
+		     const char *key,
+		     const char *filename,
+		     const char *content_type,
+		     const char *transfer_encoding,
+		     const char *data,
+		     uint64_t off,
+		     size_t size)
+{
+  struct UploadContext *uc = cls;
+  int i;
+
+  if (0 == strcmp (key, "category"))
+    return do_append (&uc->category, data, size);
+  if (0 == strcmp (key, "language"))
+    return do_append (&uc->language, data, size);
+  if (0 != strcmp (key, "upload"))
+    {
+      fprintf (stderr,
+	       "Ignoring unexpected form value `%s'\n",
+	       key);
+      return MHD_YES; /* ignore */
+    }
+  if (NULL == filename)
+    {
+      fprintf (stderr, "No filename, aborting upload\n");
+      return MHD_NO; /* no filename, error */
+    }
+  if ( (NULL == uc->category) ||
+       (NULL == uc->language) )
+    {
+      fprintf (stderr,
+	       "Missing form data for upload `%s'\n",
+	       filename);
+      uc->response = request_refused_response;
+      return MHD_NO;
+    }
+  if (-1 == uc->fd)
+    {
+      char fn[PATH_MAX];
+
+      if ( (NULL != strstr (filename, "..")) ||
+	   (NULL != strchr (filename, '/')) ||
+	   (NULL != strchr (filename, '\\')) )
+	{
+	  uc->response = request_refused_response;
+	  return MHD_NO;
+	}
+      /* create directories -- if they don't exist already */
+#ifdef WINDOWS
+      (void) mkdir (uc->language);
+#else
+      (void) mkdir (uc->language, S_IRWXU);
+#endif
+      snprintf (fn, sizeof (fn),
+		"%s/%s",
+		uc->language,
+		uc->category);
+#ifdef WINDOWS
+      (void) mkdir (fn);
+#else
+      (void) mkdir (fn, S_IRWXU);
+#endif
+      /* open file */
+      snprintf (fn, sizeof (fn),
+		"%s/%s/%s",
+		uc->language,
+		uc->category,
+		filename);
+      for (i=strlen (fn)-1;i>=0;i--)
+	if (! isprint ((int) fn[i]))
+	  fn[i] = '_';
+      uc->fd = open (fn,
+		     O_CREAT | O_EXCL
+#if O_LARGEFILE
+		     | O_LARGEFILE
+#endif
+		     | O_WRONLY,
+		     S_IRUSR | S_IWUSR);
+      if (-1 == uc->fd)
+	{
+	  fprintf (stderr,
+		   "Error opening file `%s' for upload: %s\n",
+		   fn,
+		   strerror (errno));
+	  uc->response = request_refused_response;
+	  return MHD_NO;
+	}
+      uc->filename = strdup (fn);
+    }
+  if ( (0 != size) &&
+       (size != (size_t) write (uc->fd, data, size)) )
+    {
+      /* write failed; likely: disk full */
+      fprintf (stderr,
+	       "Error writing to file `%s': %s\n",
+	       uc->filename,
+	       strerror (errno));
+      uc->response = internal_error_response;
+      close (uc->fd);
+      uc->fd = -1;
+      if (NULL != uc->filename)
+	{
+	  unlink (uc->filename);
+	  free (uc->filename);
+	  uc->filename = NULL;
+	}
+      return MHD_NO;
+    }
+  return MHD_YES;
+}
+
+
+/**
+ * Function called whenever a request was completed.
+ * Used to clean up 'struct UploadContext' objects.
+ *
+ * @param cls client-defined closure, NULL
+ * @param connection connection handle
+ * @param con_cls value as set by the last call to
+ *        the MHD_AccessHandlerCallback, points to NULL if this was
+ *            not an upload
+ * @param toe reason for request termination
+ */
+static void
+response_completed_callback (void *cls,
+			     struct MHD_Connection *connection,
+			     void **con_cls,
+			     enum MHD_RequestTerminationCode toe)
+{
+  struct UploadContext *uc = *con_cls;
+
+  if (NULL == uc)
+    return; /* this request wasn't an upload request */
+  if (NULL != uc->pp)
+    {
+      MHD_destroy_post_processor (uc->pp);
+      uc->pp = NULL;
+    }
+  if (-1 != uc->fd)
+  {
+    (void) close (uc->fd);
+    if (NULL != uc->filename)
+      {
+	fprintf (stderr,
+		 "Upload of file `%s' failed (incomplete or aborted), removing file.\n",
+		 uc->filename);
+	(void) unlink (uc->filename);
+      }
+  }
+  if (NULL != uc->filename)
+    free (uc->filename);
+  free (uc);
+}
+
+
+/**
+ * Return the current directory listing.
+ *
+ * @param connection connection to return the directory for
+ * @return MHD_YES on success, MHD_NO on error
+ */
+static int
+return_directory_response (struct MHD_Connection *connection)
+{
+  int ret;
+
+  (void) pthread_mutex_lock (&mutex);
+  if (NULL == cached_directory_response)
+    ret = MHD_queue_response (connection,
+			      MHD_HTTP_INTERNAL_SERVER_ERROR,
+			      internal_error_response);
+  else
+    ret = MHD_queue_response (connection,
+			      MHD_HTTP_OK,
+			      cached_directory_response);
+  (void) pthread_mutex_unlock (&mutex);
+  return ret;
+}
+
+
+/**
+ * Main callback from MHD, used to generate the page.
+ *
+ * @param cls NULL
+ * @param connection connection handle
+ * @param url requested URL
+ * @param method GET, PUT, POST, etc.
+ * @param version HTTP version
+ * @param upload_data data from upload (PUT/POST)
+ * @param upload_data_size number of bytes in "upload_data"
+ * @param ptr our context
+ * @return MHD_YES on success, MHD_NO to drop connection
+ */
+static int
+generate_page (void *cls,
+	       struct MHD_Connection *connection,
+	       const char *url,
+	       const char *method,
+	       const char *version,
+	       const char *upload_data,
+	       size_t *upload_data_size, void **ptr)
+{
+  struct MHD_Response *response;
+  int ret;
+  int fd;
+  struct stat buf;
+
+  if (0 != strcmp (url, "/"))
+    {
+      /* should be file download */
+      char file_data[MAGIC_HEADER_SIZE];
+      ssize_t got;
+      const char *mime;
+
+      if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
+	return MHD_NO;  /* unexpected method (we're not polite...) */
+      if ( (0 == stat (&url[1], &buf)) &&
+	   (NULL == strstr (&url[1], "..")) &&
+	   ('/' != url[1]))
+	fd = open (&url[1], O_RDONLY);
+      else
+	fd = -1;
+      if (-1 == fd)
+	return MHD_queue_response (connection,
+				   MHD_HTTP_NOT_FOUND,
+				   file_not_found_response);
+      /* read beginning of the file to determine mime type  */
+      got = read (fd, file_data, sizeof (file_data));
+      if (-1 != got)
+	mime = magic_buffer (magic, file_data, got);
+      else
+	mime = NULL;
+      (void) lseek (fd, 0, SEEK_SET);
+
+      if (NULL == (response = MHD_create_response_from_fd (buf.st_size,
+							   fd)))
+	{
+	  /* internal error (i.e. out of memory) */
+	  (void) close (fd);
+	  return MHD_NO;
+	}
+
+      /* add mime type if we had one */
+      if (NULL != mime)
+	(void) MHD_add_response_header (response,
+					MHD_HTTP_HEADER_CONTENT_TYPE,
+					mime);
+      ret = MHD_queue_response (connection,
+				MHD_HTTP_OK,
+				response);
+      MHD_destroy_response (response);
+      return ret;
+    }
+
+  if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
+    {
+      /* upload! */
+      struct UploadContext *uc = *ptr;
+
+      if (NULL == uc)
+	{
+	  if (NULL == (uc = malloc (sizeof (struct UploadContext))))
+	    return MHD_NO; /* out of memory, close connection */
+	  memset (uc, 0, sizeof (struct UploadContext));
+          uc->fd = -1;
+	  uc->connection = connection;
+	  uc->pp = MHD_create_post_processor (connection,
+					      64 * 1024 /* buffer size */,
+					      &process_upload_data, uc);
+	  if (NULL == uc->pp)
+	    {
+	      /* out of memory, close connection */
+	      free (uc);
+	      return MHD_NO;
+	    }
+	  *ptr = uc;
+	  return MHD_YES;
+	}
+      if (0 != *upload_data_size)
+	{
+	  if (NULL == uc->response)
+	    (void) MHD_post_process (uc->pp,
+				     upload_data,
+				     *upload_data_size);
+	  *upload_data_size = 0;
+	  return MHD_YES;
+	}
+      /* end of upload, finish it! */
+      MHD_destroy_post_processor (uc->pp);
+      uc->pp = NULL;
+      if (-1 != uc->fd)
+	{
+	  close (uc->fd);
+	  uc->fd = -1;
+	}
+      if (NULL != uc->response)
+	{
+	  return MHD_queue_response (connection,
+				     MHD_HTTP_FORBIDDEN,
+				     uc->response);
+	}
+      else
+	{
+	  update_directory ();
+	  return return_directory_response (connection);
+	}
+    }
+  if (0 == strcmp (method, MHD_HTTP_METHOD_GET))
+  {
+    return return_directory_response (connection);
+  }
+
+  /* unexpected request, refuse */
+  return MHD_queue_response (connection,
+			     MHD_HTTP_FORBIDDEN,
+			     request_refused_response);
+}
+
+
+/**
+ * Function called if we get a SIGPIPE. Does nothing.
+ *
+ * @param sig will be SIGPIPE (ignored)
+ */
+static void
+catcher (int sig)
+{
+  /* do nothing */
+}
+
+
+/**
+ * setup handlers to ignore SIGPIPE.
+ */
+#ifndef MINGW
+static void
+ignore_sigpipe ()
+{
+  struct sigaction oldsig;
+  struct sigaction sig;
+
+  sig.sa_handler = &catcher;
+  sigemptyset (&sig.sa_mask);
+#ifdef SA_INTERRUPT
+  sig.sa_flags = SA_INTERRUPT;  /* SunOS */
+#else
+  sig.sa_flags = SA_RESTART;
+#endif
+  if (0 != sigaction (SIGPIPE, &sig, &oldsig))
+    fprintf (stderr,
+             "Failed to install SIGPIPE handler: %s\n", strerror (errno));
+}
+#endif
+
+/* test server key */
+const char srv_signed_key_pem[] = "-----BEGIN RSA PRIVATE KEY-----\n"
+  "MIIEowIBAAKCAQEAvfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW\n"
+  "+K03KwEku55QvnUndwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8IL\n"
+  "q4sw32vo0fbMu5BZF49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ0\n"
+  "20Q5EAAEseD1YtWCIpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6\n"
+  "QYGGh1QmHRPAy3CBII6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6x\n"
+  "yoOl204xuekZOaG9RUPId74Rtmwfi1TLbBzo2wIDAQABAoIBADu09WSICNq5cMe4\n"
+  "+NKCLlgAT1NiQpLls1gKRbDhKiHU9j8QWNvWWkJWrCya4QdUfLCfeddCMeiQmv3K\n"
+  "lJMvDs+5OjJSHFoOsGiuW2Ias7IjnIojaJalfBml6frhJ84G27IXmdz6gzOiTIer\n"
+  "DjeAgcwBaKH5WwIay2TxIaScl7AwHBauQkrLcyb4hTmZuQh6ArVIN6+pzoVuORXM\n"
+  "bpeNWl2l/HSN3VtUN6aCAKbN/X3o0GavCCMn5Fa85uJFsab4ss/uP+2PusU71+zP\n"
+  "sBm6p/2IbGvF5k3VPDA7X5YX61sukRjRBihY8xSnNYx1UcoOsX6AiPnbhifD8+xQ\n"
+  "Tlf8oJUCgYEA0BTfzqNpr9Wxw5/QXaSdw7S/0eP5a0C/nwURvmfSzuTD4equzbEN\n"
+  "d+dI/s2JMxrdj/I4uoAfUXRGaabevQIjFzC9uyE3LaOyR2zhuvAzX+vVcs6bSXeU\n"
+  "pKpCAcN+3Z3evMaX2f+z/nfSUAl2i4J2R+/LQAWJW4KwRky/m+cxpfUCgYEA6bN1\n"
+  "b73bMgM8wpNt6+fcmS+5n0iZihygQ2U2DEud8nZJL4Nrm1dwTnfZfJBnkGj6+0Q0\n"
+  "cOwj2KS0/wcEdJBP0jucU4v60VMhp75AQeHqidIde0bTViSRo3HWKXHBIFGYoU3T\n"
+  "LyPyKndbqsOObnsFXHn56Nwhr2HLf6nw4taGQY8CgYBoSW36FLCNbd6QGvLFXBGt\n"
+  "2lMhEM8az/K58kJ4WXSwOLtr6MD/WjNT2tkcy0puEJLm6BFCd6A6pLn9jaKou/92\n"
+  "SfltZjJPb3GUlp9zn5tAAeSSi7YMViBrfuFiHObij5LorefBXISLjuYbMwL03MgH\n"
+  "Ocl2JtA2ywMp2KFXs8GQWQKBgFyIVv5ogQrbZ0pvj31xr9HjqK6d01VxIi+tOmpB\n"
+  "4ocnOLEcaxX12BzprW55ytfOCVpF1jHD/imAhb3YrHXu0fwe6DXYXfZV4SSG2vB7\n"
+  "IB9z14KBN5qLHjNGFpMQXHSMek+b/ftTU0ZnPh9uEM5D3YqRLVd7GcdUhHvG8P8Q\n"
+  "C9aXAoGBAJtID6h8wOGMP0XYX5YYnhlC7dOLfk8UYrzlp3xhqVkzKthTQTj6wx9R\n"
+  "GtC4k7U1ki8oJsfcIlBNXd768fqDVWjYju5rzShMpo8OCTS6ipAblKjCxPPVhIpv\n"
+  "tWPlbSn1qj6wylstJ5/3Z+ZW5H4wIKp5jmLiioDhcP0L/Ex3Zx8O\n"
+  "-----END RSA PRIVATE KEY-----\n";
+
+/* test server CA signed certificates */
+const char srv_signed_cert_pem[] = "-----BEGIN CERTIFICATE-----\n"
+  "MIIDGzCCAgWgAwIBAgIES0KCvTALBgkqhkiG9w0BAQUwFzEVMBMGA1UEAxMMdGVz\n"
+  "dF9jYV9jZXJ0MB4XDTEwMDEwNTAwMDcyNVoXDTQ1MDMxMjAwMDcyNVowFzEVMBMG\n"
+  "A1UEAxMMdGVzdF9jYV9jZXJ0MIIBHzALBgkqhkiG9w0BAQEDggEOADCCAQkCggEA\n"
+  "vfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW+K03KwEku55QvnUn\n"
+  "dwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8ILq4sw32vo0fbMu5BZ\n"
+  "F49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ020Q5EAAEseD1YtWC\n"
+  "IpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6QYGGh1QmHRPAy3CB\n"
+  "II6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6xyoOl204xuekZOaG9\n"
+  "RUPId74Rtmwfi1TLbBzo2wIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQM\n"
+  "MAoGCCsGAQUFBwMBMA8GA1UdDwEB/wQFAwMHIAAwHQYDVR0OBBYEFOFi4ilKOP1d\n"
+  "XHlWCMwmVKr7mgy8MB8GA1UdIwQYMBaAFP2olB4s2T/xuoQ5pT2RKojFwZo2MAsG\n"
+  "CSqGSIb3DQEBBQOCAQEAHVWPxazupbOkG7Did+dY9z2z6RjTzYvurTtEKQgzM2Vz\n"
+  "GQBA+3pZ3c5mS97fPIs9hZXfnQeelMeZ2XP1a+9vp35bJjZBBhVH+pqxjCgiUflg\n"
+  "A3Zqy0XwwVCgQLE2HyaU3DLUD/aeIFK5gJaOSdNTXZLv43K8kl4cqDbMeRpVTbkt\n"
+  "YmG4AyEOYRNKGTqMEJXJoxD5E3rBUNrVI/XyTjYrulxbNPcMWEHKNeeqWpKDYTFo\n"
+  "Bb01PCthGXiq/4A2RLAFosadzRa8SBpoSjPPfZ0b2w4MJpReHqKbR5+T2t6hzml6\n"
+  "4ToyOKPDmamiTuN5KzLN3cw7DQlvWMvqSOChPLnA3Q==\n"
+  "-----END CERTIFICATE-----\n";
+
+
+/**
+ * Entry point to demo.  Note: this HTTP server will make all
+ * files in the current directory and its subdirectories available
+ * to anyone.  Press ENTER to stop the server once it has started.
+ *
+ * @param argc number of arguments in argv
+ * @param argv first and only argument should be the port number
+ * @return 0 on success
+ */
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+  unsigned int port;
+
+  if ( (argc != 2) ||
+       (1 != sscanf (argv[1], "%u", &port)) ||
+       (UINT16_MAX < port) )
+    {
+      fprintf (stderr,
+	       "%s PORT\n", argv[0]);
+      return 1;
+    }
+  #ifndef MINGW
+  ignore_sigpipe ();
+  #endif
+  magic = magic_open (MAGIC_MIME_TYPE);
+  (void) magic_load (magic, NULL);
+
+  (void) pthread_mutex_init (&mutex, NULL);
+  file_not_found_response = MHD_create_response_from_buffer (strlen (FILE_NOT_FOUND_PAGE),
+							     (void *) FILE_NOT_FOUND_PAGE,
+							     MHD_RESPMEM_PERSISTENT);
+  mark_as_html (file_not_found_response);
+  request_refused_response = MHD_create_response_from_buffer (strlen (REQUEST_REFUSED_PAGE),
+							     (void *) REQUEST_REFUSED_PAGE,
+							     MHD_RESPMEM_PERSISTENT);
+  mark_as_html (request_refused_response);
+  internal_error_response = MHD_create_response_from_buffer (strlen (INTERNAL_ERROR_PAGE),
+							     (void *) INTERNAL_ERROR_PAGE,
+							     MHD_RESPMEM_PERSISTENT);
+  mark_as_html (internal_error_response);
+  update_directory ();
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | MHD_USE_SSL
+#if EPOLL_SUPPORT
+			| MHD_USE_EPOLL_LINUX_ONLY
+#endif
+			,
+                        port,
+                        NULL, NULL,
+			&generate_page, NULL,
+			MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (256 * 1024),
+#if PRODUCTION
+			MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) (64),
+#endif
+			MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) (120 /* seconds */),
+			MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) NUMBER_OF_THREADS,
+			MHD_OPTION_NOTIFY_COMPLETED, &response_completed_callback, NULL,
+                        MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
+                        MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
+			MHD_OPTION_END);
+  if (NULL == d)
+    return 1;
+  fprintf (stderr, "HTTP server running. Press ENTER to stop the server\n");
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  MHD_destroy_response (file_not_found_response);
+  MHD_destroy_response (request_refused_response);
+  MHD_destroy_response (internal_error_response);
+  update_cached_response (NULL);
+  (void) pthread_mutex_destroy (&mutex);
+  magic_close (magic);
+  return 0;
+}
+
+/* end of demo_https.c */
diff --git a/src/examples/digest_auth_example.c b/src/examples/digest_auth_example.c
new file mode 100644
index 0000000..62c99cf
--- /dev/null
+++ b/src/examples/digest_auth_example.c
@@ -0,0 +1,140 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2010 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+/**
+ * @file digest_auth_example.c
+ * @brief minimal example for how to use digest auth with libmicrohttpd
+ * @author Amr Ali
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+#include <stdlib.h>
+
+#define PAGE "<html><head><title>libmicrohttpd demo</title></head><body>Access granted</body></html>"
+
+#define DENIED "<html><head><title>libmicrohttpd demo</title></head><body>Access denied</body></html>"
+
+#define MY_OPAQUE_STR "11733b200778ce33060f31c9af70a870ba96ddd4"
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size, void **ptr)
+{
+  struct MHD_Response *response;
+  char *username;
+  const char *password = "testpass";
+  const char *realm = "test@example.com";
+  int ret;
+
+  username = MHD_digest_auth_get_username(connection);
+  if (username == NULL)
+    {
+      response = MHD_create_response_from_buffer(strlen (DENIED),
+						 DENIED,
+						 MHD_RESPMEM_PERSISTENT);
+      ret = MHD_queue_auth_fail_response(connection, realm,
+					 MY_OPAQUE_STR,
+					 response,
+					 MHD_NO);
+      MHD_destroy_response(response);
+      return ret;
+    }
+  ret = MHD_digest_auth_check(connection, realm,
+			      username,
+			      password,
+			      300);
+  free(username);
+  if ( (ret == MHD_INVALID_NONCE) ||
+       (ret == MHD_NO) )
+    {
+      response = MHD_create_response_from_buffer(strlen (DENIED),
+						 DENIED,
+						 MHD_RESPMEM_PERSISTENT);
+      if (NULL == response)
+	return MHD_NO;
+      ret = MHD_queue_auth_fail_response(connection, realm,
+					 MY_OPAQUE_STR,
+					 response,
+					 (ret == MHD_INVALID_NONCE) ? MHD_YES : MHD_NO);
+      MHD_destroy_response(response);
+      return ret;
+    }
+  response = MHD_create_response_from_buffer(strlen(PAGE), PAGE,
+					     MHD_RESPMEM_PERSISTENT);
+  ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
+  MHD_destroy_response(response);
+  return ret;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  int fd;
+  char rnd[8];
+  ssize_t len;
+  size_t off;
+  struct MHD_Daemon *d;
+
+  if (argc != 2)
+    {
+      printf ("%s PORT\n", argv[0]);
+      return 1;
+    }
+  fd = open("/dev/urandom", O_RDONLY);
+  if (-1 == fd)
+    {
+      fprintf (stderr, "Failed to open `%s': %s\n",
+	       "/dev/urandom",
+	       strerror (errno));
+      return 1;
+    }
+  off = 0;
+  while (off < 8)
+    {
+      len = read(fd, rnd, 8);
+      if (len == -1)
+	{
+	  fprintf (stderr, "Failed to read `%s': %s\n",
+		   "/dev/urandom",
+		   strerror (errno));
+	  (void) close (fd);
+	  return 1;
+	}
+      off += len;
+    }
+  (void) close(fd);
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        atoi (argv[1]),
+                        NULL, NULL, &ahc_echo, PAGE,
+			MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof(rnd), rnd,
+			MHD_OPTION_NONCE_NC_SIZE, 300,
+			MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120,
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+/* end of digest_auth_example.c */
diff --git a/src/examples/dual_stack_example.c b/src/examples/dual_stack_example.c
new file mode 100644
index 0000000..12d50f4
--- /dev/null
+++ b/src/examples/dual_stack_example.c
@@ -0,0 +1,79 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2012 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+/**
+ * @file dual_stack_example.c
+ * @brief how to use MHD with both IPv4 and IPv6 support (dual-stack)
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+
+#define PAGE "<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>"
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size, void **ptr)
+{
+  static int aptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp (method, "GET"))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+    {
+      /* do never respond on first call */
+      *ptr = &aptr;
+      return MHD_YES;
+    }
+  *ptr = NULL;                  /* reset when done */
+  response = MHD_create_response_from_buffer (strlen (me),
+					      (void *) me,
+					      MHD_RESPMEM_PERSISTENT);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+
+  if (argc != 2)
+    {
+      printf ("%s PORT\n", argv[0]);
+      return 1;
+    }
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | MHD_USE_DUAL_STACK,
+			atoi (argv[1]),
+			NULL, NULL, &ahc_echo, PAGE,
+			MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120,
+			MHD_OPTION_END);
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  return 0;
+}
diff --git a/src/examples/fileserver_example.c b/src/examples/fileserver_example.c
new file mode 100644
index 0000000..8f5223a
--- /dev/null
+++ b/src/examples/fileserver_example.c
@@ -0,0 +1,119 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file fileserver_example.c
+ * @brief minimal example for how to use libmicrohttpd to serve files
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+#include <unistd.h>
+
+#define PAGE "<html><head><title>File not found</title></head><body>File not found</body></html>"
+
+static ssize_t
+file_reader (void *cls, uint64_t pos, char *buf, size_t max)
+{
+  FILE *file = cls;
+
+  (void)  fseek (file, pos, SEEK_SET);
+  return fread (buf, 1, max, file);
+}
+
+static void
+free_callback (void *cls)
+{
+  FILE *file = cls;
+  fclose (file);
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data,
+	  size_t *upload_data_size, void **ptr)
+{
+  static int aptr;
+  struct MHD_Response *response;
+  int ret;
+  FILE *file;
+  struct stat buf;
+
+  if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+    {
+      /* do never respond on first call */
+      *ptr = &aptr;
+      return MHD_YES;
+    }
+  *ptr = NULL;                  /* reset when done */
+  if (0 == stat (&url[1], &buf))
+    file = fopen (&url[1], "rb");
+  else
+    file = NULL;
+  if (file == NULL)
+    {
+      response = MHD_create_response_from_buffer (strlen (PAGE),
+						  (void *) PAGE,
+						  MHD_RESPMEM_PERSISTENT);
+      ret = MHD_queue_response (connection, MHD_HTTP_NOT_FOUND, response);
+      MHD_destroy_response (response);
+    }
+  else
+    {
+      response = MHD_create_response_from_callback (buf.st_size, 32 * 1024,     /* 32k page size */
+                                                    &file_reader,
+                                                    file,
+                                                    &free_callback);
+      if (response == NULL)
+	{
+	  fclose (file);
+	  return MHD_NO;
+	}
+      ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+      MHD_destroy_response (response);
+    }
+  return ret;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+
+  if (argc != 2)
+    {
+      printf ("%s PORT\n", argv[0]);
+      return 1;
+    }
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        atoi (argv[1]),
+                        NULL, NULL, &ahc_echo, PAGE, MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  return 0;
+}
diff --git a/src/examples/fileserver_example_dirs.c b/src/examples/fileserver_example_dirs.c
new file mode 100644
index 0000000..9d4a193
--- /dev/null
+++ b/src/examples/fileserver_example_dirs.c
@@ -0,0 +1,179 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file fileserver_example.c
+ * @brief example for how to use libmicrohttpd to serve files (with directory support)
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include <dirent.h>
+#include <microhttpd.h>
+#include <unistd.h>
+
+#define PAGE "<html><head><title>File not found</title></head><body>File not found</body></html>"
+
+static ssize_t
+file_reader (void *cls, uint64_t pos, char *buf, size_t max)
+{
+  FILE *file = cls;
+
+  (void) fseek (file, pos, SEEK_SET);
+  return fread (buf, 1, max, file);
+}
+
+static void
+file_free_callback (void *cls)
+{
+  FILE *file = cls;
+  fclose (file);
+}
+
+static void
+dir_free_callback (void *cls)
+{
+  DIR *dir = cls;
+  if (dir != NULL)
+    closedir (dir);
+}
+
+static ssize_t
+dir_reader (void *cls, uint64_t pos, char *buf, size_t max)
+{
+  DIR *dir = cls;
+  struct dirent *e;
+
+  if (max < 512)
+    return 0;
+  do
+    {
+      e = readdir (dir);
+      if (e == NULL)
+        return MHD_CONTENT_READER_END_OF_STREAM;
+  } while (e->d_name[0] == '.');
+  return snprintf (buf, max,
+		   "<a href=\"/%s\">%s</a><br>",
+		   e->d_name,
+		   e->d_name);
+}
+
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data,
+	  size_t *upload_data_size, void **ptr)
+{
+  static int aptr;
+  struct MHD_Response *response;
+  int ret;
+  FILE *file;
+  DIR *dir;
+  struct stat buf;
+  char emsg[1024];
+
+  if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+    {
+      /* do never respond on first call */
+      *ptr = &aptr;
+      return MHD_YES;
+    }
+  *ptr = NULL;                  /* reset when done */
+  if ( (0 == stat (&url[1], &buf)) &&
+       (S_ISREG (buf.st_mode)) )
+    file = fopen (&url[1], "rb");
+  else
+    file = NULL;
+  if (file == NULL)
+    {
+      dir = opendir (".");
+      if (dir == NULL)
+	{
+	  /* most likely cause: more concurrent requests than  
+	     available file descriptors / 2 */
+	  snprintf (emsg,
+		    sizeof (emsg),
+		    "Failed to open directory `.': %s\n",
+		    strerror (errno));
+	  response = MHD_create_response_from_buffer (strlen (emsg),
+						      emsg,
+						      MHD_RESPMEM_MUST_COPY);
+	  if (response == NULL)
+	    return MHD_NO;	    
+	  ret = MHD_queue_response (connection, MHD_HTTP_SERVICE_UNAVAILABLE, response);
+	  MHD_destroy_response (response);
+	}
+      else
+	{
+	  response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
+							32 * 1024,
+							&dir_reader,
+							dir,
+							&dir_free_callback);
+	  if (response == NULL)
+	    {
+	      closedir (dir);
+	      return MHD_NO;
+	    }
+	  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+	  MHD_destroy_response (response);
+	}
+    }
+  else
+    {
+      response = MHD_create_response_from_callback (buf.st_size, 32 * 1024,     /* 32k page size */
+                                                    &file_reader,
+                                                    file,
+                                                    &file_free_callback);
+      if (response == NULL)
+	{
+	  fclose (file);
+	  return MHD_NO;
+	}
+      ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+      MHD_destroy_response (response);
+    }
+  return ret;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+
+  if (argc != 2)
+    {
+      printf ("%s PORT\n", argv[0]);
+      return 1;
+    }
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        atoi (argv[1]),
+                        NULL, NULL, &ahc_echo, PAGE, MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  return 0;
+}
diff --git a/src/examples/fileserver_example_external_select.c b/src/examples/fileserver_example_external_select.c
new file mode 100644
index 0000000..7a27061
--- /dev/null
+++ b/src/examples/fileserver_example_external_select.c
@@ -0,0 +1,149 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+/**
+ * @file fileserver_example_external_select.c
+ * @brief minimal example for how to use libmicrohttpd to server files
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define PAGE "<html><head><title>File not found</title></head><body>File not found</body></html>"
+
+static ssize_t
+file_reader (void *cls, uint64_t pos, char *buf, size_t max)
+{
+  FILE *file = cls;
+
+  (void) fseek (file, pos, SEEK_SET);
+  return fread (buf, 1, max, file);
+}
+
+static void
+free_callback (void *cls)
+{
+  FILE *file = cls;
+  fclose (file);
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data,
+	  size_t *upload_data_size, void **ptr)
+{
+  static int aptr;
+  struct MHD_Response *response;
+  int ret;
+  FILE *file;
+  struct stat buf;
+
+  if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+    {
+      /* do never respond on first call */
+      *ptr = &aptr;
+      return MHD_YES;
+    }
+  *ptr = NULL;                  /* reset when done */
+  if ( (0 == stat (&url[1], &buf)) &&
+       (S_ISREG (buf.st_mode)) )
+    file = fopen (&url[1], "rb");
+  else
+    file = NULL;
+  if (file == NULL)
+    {
+      response = MHD_create_response_from_buffer (strlen (PAGE),
+						  (void *) PAGE,
+						  MHD_RESPMEM_PERSISTENT);
+      ret = MHD_queue_response (connection, MHD_HTTP_NOT_FOUND, response);
+      MHD_destroy_response (response);
+    }
+  else
+    {
+      response = MHD_create_response_from_callback (buf.st_size, 32 * 1024,     /* 32k page size */
+                                                    &file_reader,
+                                                    file,
+                                                    &free_callback);
+      if (response == NULL)
+	{
+	  fclose (file);
+	  return MHD_NO;
+	}
+      ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+      MHD_destroy_response (response);
+    }
+  return ret;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+  time_t end;
+  time_t t;
+  struct timeval tv;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  MHD_UNSIGNED_LONG_LONG mhd_timeout;
+
+  if (argc != 3)
+    {
+      printf ("%s PORT SECONDS-TO-RUN\n", argv[0]);
+      return 1;
+    }
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        atoi (argv[1]),
+                        NULL, NULL, &ahc_echo, PAGE, MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  end = time (NULL) + atoi (argv[2]);
+  while ((t = time (NULL)) < end)
+    {
+      tv.tv_sec = end - t;
+      tv.tv_usec = 0;
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+	break; /* fatal internal error */
+      if (MHD_get_timeout (d, &mhd_timeout) == MHD_YES)
+        {
+          if (((MHD_UNSIGNED_LONG_LONG)tv.tv_sec) < mhd_timeout / 1000LL)
+            {
+              tv.tv_sec = mhd_timeout / 1000LL;
+              tv.tv_usec = (mhd_timeout - (tv.tv_sec * 1000LL)) * 1000LL;
+            }
+        }
+      select (max + 1, &rs, &ws, &es, &tv);
+      MHD_run (d);
+    }
+  MHD_stop_daemon (d);
+  return 0;
+}
diff --git a/src/examples/https_fileserver_example.c b/src/examples/https_fileserver_example.c
new file mode 100644
index 0000000..fe0c2de
--- /dev/null
+++ b/src/examples/https_fileserver_example.c
@@ -0,0 +1,207 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+/**
+ * @file https_fileserver_example.c
+ * @brief a simple HTTPS file server using TLS.
+ *
+ * Usage :
+ *
+ *  'http_fileserver_example HTTP-PORT SECONDS-TO-RUN'
+ *
+ * The certificate & key are required by the server to operate,  Omitting the
+ * path arguments will cause the server to use the hard coded example certificate & key.
+ *
+ * 'certtool' may be used to generate these if required.
+ *
+ * @author Sagie Amir
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+#include <sys/stat.h>
+#include <gnutls/gnutls.h>
+#include <gcrypt.h>
+
+#define BUF_SIZE 1024
+#define MAX_URL_LEN 255
+
+// TODO remove if unused
+#define CAFILE "ca.pem"
+#define CRLFILE "crl.pem"
+
+#define EMPTY_PAGE "<html><head><title>File not found</title></head><body>File not found</body></html>"
+
+/* Test Certificate */
+const char cert_pem[] =
+  "-----BEGIN CERTIFICATE-----\n"
+  "MIICpjCCAZCgAwIBAgIESEPtjjALBgkqhkiG9w0BAQUwADAeFw0wODA2MDIxMjU0\n"
+  "MzhaFw0wOTA2MDIxMjU0NDZaMAAwggEfMAsGCSqGSIb3DQEBAQOCAQ4AMIIBCQKC\n"
+  "AQC03TyUvK5HmUAirRp067taIEO4bibh5nqolUoUdo/LeblMQV+qnrv/RNAMTx5X\n"
+  "fNLZ45/kbM9geF8qY0vsPyQvP4jumzK0LOJYuIwmHaUm9vbXnYieILiwCuTgjaud\n"
+  "3VkZDoQ9fteIo+6we9UTpVqZpxpbLulBMh/VsvX0cPJ1VFC7rT59o9hAUlFf9jX/\n"
+  "GmKdYI79MtgVx0OPBjmmSD6kicBBfmfgkO7bIGwlRtsIyMznxbHu6VuoX/eVxrTv\n"
+  "rmCwgEXLWRZ6ru8MQl5YfqeGXXRVwMeXU961KefbuvmEPccgCxm8FZ1C1cnDHFXh\n"
+  "siSgAzMBjC/b6KVhNQ4KnUdZAgMBAAGjLzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0O\n"
+  "BBYEFJcUvpjvE5fF/yzUshkWDpdYiQh/MAsGCSqGSIb3DQEBBQOCAQEARP7eKSB2\n"
+  "RNd6XjEjK0SrxtoTnxS3nw9sfcS7/qD1+XHdObtDFqGNSjGYFB3Gpx8fpQhCXdoN\n"
+  "8QUs3/5ZVa5yjZMQewWBgz8kNbnbH40F2y81MHITxxCe1Y+qqHWwVaYLsiOTqj2/\n"
+  "0S3QjEJ9tvklmg7JX09HC4m5QRYfWBeQLD1u8ZjA1Sf1xJriomFVyRLI2VPO2bNe\n"
+  "JDMXWuP+8kMC7gEvUnJ7A92Y2yrhu3QI3bjPk8uSpHea19Q77tul1UVBJ5g+zpH3\n"
+  "OsF5p0MyaVf09GTzcLds5nE/osTdXGUyHJapWReVmPm3Zn6gqYlnzD99z+DPIgIV\n"
+  "RhZvQx74NQnS6g==\n" "-----END CERTIFICATE-----\n";
+
+const char key_pem[] =
+  "-----BEGIN RSA PRIVATE KEY-----\n"
+  "MIIEowIBAAKCAQEAtN08lLyuR5lAIq0adOu7WiBDuG4m4eZ6qJVKFHaPy3m5TEFf\n"
+  "qp67/0TQDE8eV3zS2eOf5GzPYHhfKmNL7D8kLz+I7psytCziWLiMJh2lJvb2152I\n"
+  "niC4sArk4I2rnd1ZGQ6EPX7XiKPusHvVE6VamacaWy7pQTIf1bL19HDydVRQu60+\n"
+  "faPYQFJRX/Y1/xpinWCO/TLYFcdDjwY5pkg+pInAQX5n4JDu2yBsJUbbCMjM58Wx\n"
+  "7ulbqF/3lca0765gsIBFy1kWeq7vDEJeWH6nhl10VcDHl1PetSnn27r5hD3HIAsZ\n"
+  "vBWdQtXJwxxV4bIkoAMzAYwv2+ilYTUOCp1HWQIDAQABAoIBAArOQv3R7gmqDspj\n"
+  "lDaTFOz0C4e70QfjGMX0sWnakYnDGn6DU19iv3GnX1S072ejtgc9kcJ4e8VUO79R\n"
+  "EmqpdRR7k8dJr3RTUCyjzf/C+qiCzcmhCFYGN3KRHA6MeEnkvRuBogX4i5EG1k5l\n"
+  "/5t+YBTZBnqXKWlzQLKoUAiMLPg0eRWh+6q7H4N7kdWWBmTpako7TEqpIwuEnPGx\n"
+  "u3EPuTR+LN6lF55WBePbCHccUHUQaXuav18NuDkcJmCiMArK9SKb+h0RqLD6oMI/\n"
+  "dKD6n8cZXeMBkK+C8U/K0sN2hFHACsu30b9XfdnljgP9v+BP8GhnB0nCB6tNBCPo\n"
+  "32srOwECgYEAxWh3iBT4lWqL6bZavVbnhmvtif4nHv2t2/hOs/CAq8iLAw0oWGZc\n"
+  "+JEZTUDMvFRlulr0kcaWra+4fN3OmJnjeuFXZq52lfMgXBIKBmoSaZpIh2aDY1Rd\n"
+  "RbEse7nQl9hTEPmYspiXLGtnAXW7HuWqVfFFP3ya8rUS3t4d07Hig8ECgYEA6ou6\n"
+  "OHiBRTbtDqLIv8NghARc/AqwNWgEc9PelCPe5bdCOLBEyFjqKiT2MttnSSUc2Zob\n"
+  "XhYkHC6zN1Mlq30N0e3Q61YK9LxMdU1vsluXxNq2rfK1Scb1oOlOOtlbV3zA3VRF\n"
+  "hV3t1nOA9tFmUrwZi0CUMWJE/zbPAyhwWotKyZkCgYEAh0kFicPdbABdrCglXVae\n"
+  "SnfSjVwYkVuGd5Ze0WADvjYsVkYBHTvhgRNnRJMg+/vWz3Sf4Ps4rgUbqK8Vc20b\n"
+  "AU5G6H6tlCvPRGm0ZxrwTWDHTcuKRVs+pJE8C/qWoklE/AAhjluWVoGwUMbPGuiH\n"
+  "6Gf1bgHF6oj/Sq7rv/VLZ8ECgYBeq7ml05YyLuJutuwa4yzQ/MXfghzv4aVyb0F3\n"
+  "QCdXR6o2IYgR6jnSewrZKlA9aPqFJrwHNR6sNXlnSmt5Fcf/RWO/qgJQGLUv3+rG\n"
+  "7kuLTNDR05azSdiZc7J89ID3Bkb+z2YkV+6JUiPq/Ei1+nDBEXb/m+/HqALU/nyj\n"
+  "P3gXeQKBgBusb8Rbd+KgxSA0hwY6aoRTPRt8LNvXdsB9vRcKKHUFQvxUWiUSS+L9\n"
+  "/Qu1sJbrUquKOHqksV5wCnWnAKyJNJlhHuBToqQTgKXjuNmVdYSe631saiI7PHyC\n"
+  "eRJ6DxULPxABytJrYCRrNqmXi5TCiqR2mtfalEMOPxz8rUU8dYyx\n"
+  "-----END RSA PRIVATE KEY-----\n";
+
+static ssize_t
+file_reader (void *cls, uint64_t pos, char *buf, size_t max)
+{
+  FILE *file = cls;
+
+  (void) fseek (file, pos, SEEK_SET);
+  return fread (buf, 1, max, file);
+}
+
+static void
+file_free_callback (void *cls)
+{
+  FILE *file = cls;
+  fclose (file);
+}
+
+/* HTTP access handler call back */
+static int
+http_ahc (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data,
+	  size_t *upload_data_size, void **ptr)
+{
+  static int aptr;
+  struct MHD_Response *response;
+  int ret;
+  FILE *file;
+  struct stat buf;
+
+  if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+    {
+      /* do never respond on first call */
+      *ptr = &aptr;
+      return MHD_YES;
+    }
+  *ptr = NULL;                  /* reset when done */
+
+  if ( (0 == stat (&url[1], &buf)) &&
+       (S_ISREG (buf.st_mode)) )
+    file = fopen (&url[1], "rb");
+  else
+    file = NULL;
+  if (file == NULL)
+    {
+      response = MHD_create_response_from_buffer (strlen (EMPTY_PAGE),
+						  (void *) EMPTY_PAGE,
+						  MHD_RESPMEM_PERSISTENT);
+      ret = MHD_queue_response (connection, MHD_HTTP_NOT_FOUND, response);
+      MHD_destroy_response (response);
+    }
+  else
+    {
+      response = MHD_create_response_from_callback (buf.st_size, 32 * 1024,     /* 32k PAGE_NOT_FOUND size */
+                                                    &file_reader, file,
+                                                    &file_free_callback);
+      if (response == NULL)
+	{
+	  fclose (file);
+	  return MHD_NO;
+	}
+      ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+      MHD_destroy_response (response);
+    }
+  return ret;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *TLS_daemon;
+
+  if (argc == 2)
+    {
+      /* TODO check if this is truly necessary -  disallow usage of the blocking /dev/random */
+      /* gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0); */
+      TLS_daemon =
+        MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG |
+                          MHD_USE_SSL, atoi (argv[1]), NULL, NULL, &http_ahc,
+                          NULL, MHD_OPTION_CONNECTION_TIMEOUT, 256,
+                          MHD_OPTION_HTTPS_MEM_KEY, key_pem,
+                          MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
+                          MHD_OPTION_END);
+    }
+  else
+    {
+      printf ("Usage: %s HTTP-PORT\n", argv[0]);
+      return 1;
+    }
+
+  if (TLS_daemon == NULL)
+    {
+      fprintf (stderr, "Error: failed to start TLS_daemon\n");
+      return 1;
+    }
+  else
+    {
+      printf ("MHD daemon listening on port %d\n", atoi (argv[1]));
+    }
+
+  (void) getc (stdin);
+
+  MHD_stop_daemon (TLS_daemon);
+
+  return 0;
+}
diff --git a/src/examples/mhd2spdy.c b/src/examples/mhd2spdy.c
new file mode 100644
index 0000000..a227508
--- /dev/null
+++ b/src/examples/mhd2spdy.c
@@ -0,0 +1,322 @@
+/*
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file mhd2spdy.c
+ * @brief  The main file of the HTTP-to-SPDY proxy with the 'main' function
+ *         and event loop. No threads are used.
+ *         Currently only GET is supported.
+ *         TODOs:
+ *         - non blocking SSL connect
+ *         - check certificate
+ *         - on closing spdy session, close sockets for all requests
+ * @author Andrey Uzunov
+ */
+ 
+ 
+#include "mhd2spdy_structures.h"
+#include "mhd2spdy_spdy.h"
+#include "mhd2spdy_http.h"
+
+
+static int run = 1;
+//static int spdy_close = 0;
+
+
+static void
+catch_signal(int signal)
+{
+  (void)signal;
+  //spdy_close = 1;
+  run = 0;
+}
+
+
+void
+print_stat()
+{
+  if(!glob_opt.statistics)
+    return;
+  
+  printf("--------------------------\n");
+  printf("Statistics (TLS overhead is ignored when used):\n");
+  //printf("HTTP bytes received: %lld\n", glob_stat.http_bytes_received);
+  //printf("HTTP bytes sent: %lld\n", glob_stat.http_bytes_sent);
+  printf("SPDY bytes sent: %lld\n", glob_stat.spdy_bytes_sent);
+  printf("SPDY bytes received: %lld\n", glob_stat.spdy_bytes_received);
+  printf("SPDY bytes received and dropped: %lld\n", glob_stat.spdy_bytes_received_and_dropped);
+}
+
+
+int
+run_everything ()
+{	
+  unsigned long long timeoutlong=0;
+  struct timeval timeout;
+  int ret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  int maxfd = -1;
+  int maxfd_s = -1;
+  struct MHD_Daemon *daemon;
+  nfds_t spdy_npollfds = 1;
+  struct URI * spdy2http_uri = NULL;
+  struct SPDY_Connection *connection;
+  struct SPDY_Connection *connections[MAX_SPDY_CONNECTIONS];
+  struct SPDY_Connection *connection_for_delete;
+
+  if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
+    PRINT_INFO("signal failed");
+    
+  if (signal(SIGINT, catch_signal) == SIG_ERR)
+    PRINT_INFO("signal failed");
+
+  glob_opt.streams_opened = 0;
+  glob_opt.responses_pending = 0;
+  //glob_opt.global_memory = 0;
+
+  srand(time(NULL));
+  
+  if(init_parse_uri(&glob_opt.uri_preg))
+    DIE("Regexp compilation failed");
+    
+  if(NULL != glob_opt.spdy2http_str)
+  {
+    ret = parse_uri(&glob_opt.uri_preg, glob_opt.spdy2http_str, &spdy2http_uri);
+    if(ret != 0)
+      DIE("spdy_parse_uri failed");
+  }
+
+  SSL_load_error_strings();
+  SSL_library_init();
+  glob_opt.ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+  if(glob_opt.ssl_ctx == NULL) {
+    PRINT_INFO2("SSL_CTX_new %s", ERR_error_string(ERR_get_error(), NULL));
+    abort();
+  }
+  spdy_ssl_init_ssl_ctx(glob_opt.ssl_ctx, &glob_opt.spdy_proto_version);
+
+  daemon = MHD_start_daemon ( 
+          MHD_SUPPRESS_DATE_NO_CLOCK,
+          glob_opt.listen_port,
+          NULL, NULL, &http_cb_request, NULL,
+          MHD_OPTION_URI_LOG_CALLBACK, &http_cb_log, NULL,
+          MHD_OPTION_NOTIFY_COMPLETED, &http_cb_request_completed, NULL,
+          MHD_OPTION_END);
+  if(NULL==daemon)
+    DIE("MHD_start_daemon failed");
+
+  do
+  {
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 0;
+  
+    if(NULL == glob_opt.spdy_connection && NULL != glob_opt.spdy2http_str)
+    {
+      glob_opt.spdy_connection = spdy_connect(spdy2http_uri, spdy2http_uri->port, strcmp("https", spdy2http_uri->scheme)==0);
+      if(NULL == glob_opt.spdy_connection && glob_opt.only_proxy)
+        PRINT_INFO("cannot connect to the proxy");
+    }
+
+    FD_ZERO(&rs);
+    FD_ZERO(&ws);
+    FD_ZERO(&es);
+
+    ret = MHD_get_timeout(daemon, &timeoutlong);
+    if(MHD_NO == ret || timeoutlong > 5000)
+      timeout.tv_sec = 5;
+    else
+    {
+      timeout.tv_sec = timeoutlong / 1000;
+      timeout.tv_usec = (timeoutlong % 1000) * 1000;
+    }
+    
+    if(MHD_NO == MHD_get_fdset (daemon,
+                                  &rs,
+                                  &ws, 
+                                  &es,
+                                  &maxfd))
+    {
+      PRINT_INFO("MHD_get_fdset error");
+    }
+    assert(-1 != maxfd);
+    
+    maxfd_s = spdy_get_selectfdset(
+                                  &rs,
+                                  &ws, 
+                                  &es,
+                                  connections, MAX_SPDY_CONNECTIONS, &spdy_npollfds);
+    if(maxfd_s > maxfd) 
+      maxfd = maxfd_s;
+ 
+    PRINT_INFO2("MHD timeout %lld %lld", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec);
+
+    glob_opt.spdy_data_received = false;
+      
+    ret = select(maxfd+1, &rs, &ws, &es, &timeout);
+    PRINT_INFO2("timeout now %lld %lld ret is %i", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec, ret);
+
+    switch(ret)
+    {
+      case -1:
+        PRINT_INFO2("select error: %i", errno);
+        break;
+      case 0:
+        //break;
+      default:
+      PRINT_INFO("run");
+        //MHD_run_from_select(daemon,&rs, &ws, &es); //not closing FDs at some time in past
+        MHD_run(daemon);
+        spdy_run_select(&rs, &ws, &es, connections, spdy_npollfds);
+        if(glob_opt.spdy_data_received)
+        {
+          PRINT_INFO("MHD run again");
+          //MHD_run_from_select(daemon,&rs, &ws, &es); //not closing FDs at some time in past
+          MHD_run(daemon);
+        }
+        break;
+    }
+  }
+  while(run);
+
+  MHD_stop_daemon (daemon);
+  
+  //TODO SSL_free brakes
+  spdy_free_connection(glob_opt.spdy_connection);
+  
+  connection = glob_opt.spdy_connections_head;
+  while(NULL != connection)
+  {    
+    connection_for_delete = connection;
+    connection = connection_for_delete->next;
+    glob_opt.streams_opened -= connection_for_delete->streams_opened;
+    DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection_for_delete);
+    spdy_free_connection(connection_for_delete);
+  }
+  
+  free_uri(spdy2http_uri);
+  
+  deinit_parse_uri(&glob_opt.uri_preg);
+  
+  SSL_CTX_free(glob_opt.ssl_ctx);
+  ERR_free_strings();
+  EVP_cleanup();
+    
+  PRINT_INFO2("spdy streams: %i; http requests: %i", glob_opt.streams_opened, glob_opt.responses_pending);
+  //PRINT_INFO2("memory allocated %zu bytes", glob_opt.global_memory);
+  
+  print_stat();
+
+  return 0;
+}
+
+
+void
+display_usage()
+{
+  printf(
+    "Usage: mhd2spdy [-ovs] [-b <SPDY2HTTP-PROXY>] -p <PORT>\n\n"
+    "OPTIONS:\n"
+    "    -p, --port            Listening port.\n"
+    "    -b, --backend-proxy   If set, he proxy will send requests to\n"
+    "                          that SPDY server or proxy. Set the address\n"
+    "                          in the form 'http://host:port'. Use 'https'\n"
+    "                          for SPDY over TLS, or 'http' for plain SPDY\n"
+    "                          communication with the backend.\n"
+    "    -o, --only-proxy      If set, the proxy will always forward the\n"
+    "                          requests to the backend proxy. If not set,\n"
+    "                          the proxy will first try to establsh SPDY\n"
+    "                          connection to the requested server. If the\n"
+    "                          server does not support SPDY and TLS, the\n"
+    "                          backend proxy will be used for the request.\n"
+    "    -v, --verbose         Print debug information.\n"
+    "    -s, --statistics      Print simple statistics on exit.\n\n"
+
+  );
+}
+
+
+int
+main (int argc,
+      char *const *argv)
+{   
+  int getopt_ret;
+  int option_index;
+  struct option long_options[] = {
+    {"port",  required_argument, 0, 'p'},
+    {"backend-proxy",  required_argument, 0, 'b'},
+    {"verbose",  no_argument, 0, 'v'},
+    {"only-proxy",  no_argument, 0, 'o'},
+    {"statistics",  no_argument, 0, 's'},
+    {0, 0, 0, 0}
+  };
+  
+  while (1)
+  {
+    getopt_ret = getopt_long( argc, argv, "p:b:vos", long_options, &option_index);
+    if (getopt_ret == -1)
+      break;
+
+    switch(getopt_ret)
+    {
+      case 'p':
+        glob_opt.listen_port = atoi(optarg);
+        break;
+        
+      case 'b':
+        glob_opt.spdy2http_str = strdup(optarg);
+        if(NULL == glob_opt.spdy2http_str)
+          return 1;
+        break;
+        
+      case 'v':
+        glob_opt.verbose = true;
+        break;
+        
+      case 'o':
+        glob_opt.only_proxy = true;
+        break;
+        
+      case 's':
+        glob_opt.statistics = true;
+        break;
+        
+      case 0:
+        PRINT_INFO("0 from getopt");
+        break;
+        
+      case '?':
+        display_usage();
+        return 1;
+        
+      default:
+        DIE("default from getopt");
+    }
+  }
+  
+  if(
+    0 == glob_opt.listen_port
+    || (glob_opt.only_proxy && NULL == glob_opt.spdy2http_str)
+    )
+  {
+    display_usage();
+    return 1;
+  }
+    
+  return run_everything();
+}
diff --git a/src/examples/mhd2spdy_http.c b/src/examples/mhd2spdy_http.c
new file mode 100644
index 0000000..895f07f
--- /dev/null
+++ b/src/examples/mhd2spdy_http.c
@@ -0,0 +1,422 @@
+/*
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file mhd2spdy_http.c
+ * @brief  HTTP part of the proxy. libmicrohttpd is used for the server side.
+ * @author Andrey Uzunov
+ */
+ 
+#include "mhd2spdy_structures.h"
+#include "mhd2spdy_http.h"
+#include "mhd2spdy_spdy.h"
+
+
+void *
+http_cb_log(void * cls,
+const char * uri)
+{
+  (void)cls;
+  
+  struct HTTP_URI * http_uri;
+  
+  PRINT_INFO2("log uri '%s'\n", uri);
+  
+  //TODO not freed once in a while
+  if(NULL == (http_uri = au_malloc(sizeof(struct HTTP_URI ))))
+    return NULL;
+  http_uri->uri = strdup(uri);
+  return http_uri;
+}
+
+
+static int
+http_cb_iterate(void *cls,
+                 enum MHD_ValueKind kind,
+                 const char *name,
+                 const char *value)
+{
+  (void)kind;
+  
+  static char * const forbidden[] = {"Transfer-Encoding",
+    "Proxy-Connection",
+    "Keep-Alive",
+    "Connection"};
+  static int forbidden_size = 4;
+  int i;
+	struct SPDY_Headers *spdy_headers = (struct SPDY_Headers *)cls;
+	
+	if(0 == strcasecmp(name, "Host"))
+    spdy_headers->nv[9] = (char *)value;
+  else
+  {
+    for(i=0; i<forbidden_size; ++i)
+      if(0 == strcasecmp(forbidden[i], name))
+        return MHD_YES;
+    spdy_headers->nv[spdy_headers->cnt++] = (char *)name;
+    spdy_headers->nv[spdy_headers->cnt++] = (char *)value;
+  }
+	
+	return MHD_YES;
+}
+
+
+static ssize_t
+http_cb_response (void *cls,
+                        uint64_t pos,
+                        char *buffer,
+                        size_t max)
+{
+  (void)pos;
+  
+	int ret;
+	struct Proxy *proxy = (struct Proxy *)cls;
+	void *newbody;
+  const union MHD_ConnectionInfo *info;
+  int val = 1;
+  
+  PRINT_INFO2("http_cb_response for %s", proxy->url);
+  
+  if(proxy->spdy_error)
+    return MHD_CONTENT_READER_END_WITH_ERROR;
+  
+	if(0 == proxy->http_body_size && (proxy->done || !proxy->spdy_active))
+  {
+    PRINT_INFO("sent end of stream");
+    return MHD_CONTENT_READER_END_OF_STREAM;
+  }
+	
+	if(!proxy->http_body_size)//nothing to write now
+  {
+    //flush data
+    info = MHD_get_connection_info (proxy->http_connection,
+         MHD_CONNECTION_INFO_CONNECTION_FD);
+    ret = setsockopt(info->connect_fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
+    if(ret == -1) {
+      DIE("setsockopt");
+    }
+    
+    PRINT_INFO("FLUSH data");
+		return 0;
+  }
+	
+	if(max >= proxy->http_body_size)
+	{
+		ret = proxy->http_body_size;
+		newbody = NULL;
+	}
+	else
+	{
+		ret = max;
+		if(NULL == (newbody = au_malloc(proxy->http_body_size - max)))
+		{
+			PRINT_INFO("no memory");
+			return MHD_CONTENT_READER_END_WITH_ERROR;
+		}
+		memcpy(newbody, proxy->http_body + max, proxy->http_body_size - max);
+	}
+	memcpy(buffer, proxy->http_body, ret);
+	free(proxy->http_body);
+	proxy->http_body = newbody;
+	proxy->http_body_size -= ret;
+	
+	if(proxy->length >= 0)
+	{
+		proxy->length -= ret;
+	}
+	
+	PRINT_INFO2("response_callback, size: %i",ret);
+	
+	return ret;
+}
+
+
+static void
+http_cb_response_done(void *cls)
+{
+  (void)cls;
+  //TODO remove
+}
+
+int
+http_cb_request (void *cls,
+                struct MHD_Connection *connection,
+                const char *url,
+                const char *method,
+                const char *version,
+                const char *upload_data,
+                size_t *upload_data_size,
+                void **ptr)
+{
+  (void)cls;
+  (void)url;
+  (void)upload_data;
+  (void)upload_data_size;
+  
+  int ret;
+  struct Proxy *proxy;
+  struct SPDY_Headers spdy_headers;
+  bool with_body = false;
+  struct HTTP_URI *http_uri;
+  const char *header_value;
+
+  if (NULL == ptr || NULL == *ptr)
+    return MHD_NO;
+    
+  http_uri = (struct HTTP_URI *)*ptr;
+    
+  if(NULL == http_uri->proxy)
+  {
+    //first call for this request
+    if (0 != strcmp (method, MHD_HTTP_METHOD_GET) && 0 != strcmp (method, MHD_HTTP_METHOD_POST))
+    {
+      free(http_uri->uri);
+      free(http_uri);
+      PRINT_INFO2("unexpected method %s", method);
+      return MHD_NO;
+    }
+    
+    if(NULL == (proxy = au_malloc(sizeof(struct Proxy))))
+    {
+      free(http_uri->uri);
+      free(http_uri);
+      PRINT_INFO("No memory");
+      return MHD_NO; 
+    }
+    
+    ++glob_opt.responses_pending;
+    proxy->id = rand();
+    proxy->http_active = true;
+    proxy->http_connection = connection;
+    http_uri->proxy = proxy;
+    return MHD_YES;
+  }
+  
+  proxy = http_uri->proxy;
+  
+  if(proxy->spdy_error || proxy->http_error)
+    return MHD_NO; // handled at different place TODO? leaks?
+
+  if(proxy->spdy_active)
+  {
+    if(0 == strcmp (method, MHD_HTTP_METHOD_POST))
+    {
+      PRINT_INFO("POST processing");
+        
+      int rc= spdylay_session_resume_data(proxy->spdy_connection->session, proxy->stream_id);
+      PRINT_INFO2("rc is %i stream is %i", rc, proxy->stream_id);
+      proxy->spdy_connection->want_io |= WANT_WRITE;
+      
+      if(0 == *upload_data_size)
+      {
+      PRINT_INFO("POST http EOF");
+        proxy->receiving_done = true;
+        return MHD_YES;
+      }
+      
+      if(!copy_buffer(upload_data, *upload_data_size, &proxy->received_body, &proxy->received_body_size))
+      {
+        //TODO handle it better?
+        PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
+        return MHD_NO;
+      }
+      
+      *upload_data_size = 0;
+                               
+      return MHD_YES;
+    }
+  
+    //already handled
+    PRINT_INFO("unnecessary call to http_cb_request");
+    return MHD_YES;
+  }
+  
+  //second call for this request
+
+  PRINT_INFO2("received request for '%s %s %s'", method, http_uri->uri, version);
+
+  proxy->url = http_uri->uri;
+  
+  header_value = MHD_lookup_connection_value(connection,
+    MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH);
+  
+  with_body = 0 == strcmp (method, MHD_HTTP_METHOD_POST)
+    && (NULL == header_value || 0 != strcmp ("0", header_value));
+    
+  PRINT_INFO2("body will be sent %i", with_body);
+    
+  ret = parse_uri(&glob_opt.uri_preg, proxy->url, &proxy->uri);
+  if(ret != 0)
+    DIE("parse_uri failed");
+  proxy->http_uri = http_uri;
+
+  spdy_headers.num = MHD_get_connection_values (connection,
+                       MHD_HEADER_KIND,
+                       NULL,
+                       NULL);
+  if(NULL == (spdy_headers.nv = au_malloc(((spdy_headers.num + 5) * 2 + 1) * sizeof(char *))))
+    DIE("no memory");
+  spdy_headers.nv[0] = ":method";     spdy_headers.nv[1] = method;
+  spdy_headers.nv[2] = ":path";       spdy_headers.nv[3] = proxy->uri->path_and_more;
+  spdy_headers.nv[4] = ":version";    spdy_headers.nv[5] = (char *)version;
+  spdy_headers.nv[6] = ":scheme";     spdy_headers.nv[7] = proxy->uri->scheme;
+  spdy_headers.nv[8] = ":host";       spdy_headers.nv[9] = NULL;
+  //nv[14] = NULL;
+  spdy_headers.cnt = 10;
+  MHD_get_connection_values (connection,
+                       MHD_HEADER_KIND,
+                       &http_cb_iterate,
+                       &spdy_headers);
+                       
+  spdy_headers.nv[spdy_headers.cnt] = NULL;
+  if(NULL == spdy_headers.nv[9])
+    spdy_headers.nv[9] = proxy->uri->host_and_port;
+
+  if(0 != spdy_request(spdy_headers.nv, proxy, with_body))
+  {
+    free(spdy_headers.nv);
+    //free_proxy(proxy);
+    
+    return MHD_NO;
+  }
+  free(spdy_headers.nv);
+  
+  proxy->http_response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
+                         4096,
+                         &http_cb_response,
+                         proxy,
+                         &http_cb_response_done);
+
+  if (NULL == proxy->http_response)
+    DIE("no response");
+  
+  if(MHD_NO == MHD_add_response_header (proxy->http_response,
+                 "Proxy-Connection", "keep-alive"))
+    PRINT_INFO("SPDY_name_value_add failed: ");
+  if(MHD_NO == MHD_add_response_header (proxy->http_response,
+                 "Connection", "Keep-Alive"))
+    PRINT_INFO("SPDY_name_value_add failed: ");
+  if(MHD_NO == MHD_add_response_header (proxy->http_response,
+                 "Keep-Alive", "timeout=5, max=100"))
+    PRINT_INFO("SPDY_name_value_add failed: ");
+    
+  proxy->spdy_active = true;
+  
+  return MHD_YES;
+}
+
+
+void
+http_create_response(struct Proxy* proxy,
+                     char **nv)
+{
+  size_t i;
+  
+  if(!proxy->http_active)
+    return;
+  
+  for(i = 0; nv[i]; i += 2) {
+    if(0 == strcmp(":status", nv[i]))
+    {
+      char tmp[4];
+      memcpy(&tmp,nv[i+1],3);
+      tmp[3]=0;
+      proxy->status = atoi(tmp);
+      continue;
+    }
+    else if(0 == strcmp(":version", nv[i]))
+    {
+      proxy->version = nv[i+1];
+      continue;
+    }
+    else if(0 == strcmp("content-length", nv[i]))
+    {
+      continue;
+    }
+
+    char *header = *(nv+i);
+    if(MHD_NO == MHD_add_response_header (proxy->http_response,
+                   header, nv[i+1]))
+    {
+      PRINT_INFO2("SPDY_name_value_add failed: '%s' '%s'", header, nv[i+1]);
+    }
+    PRINT_INFO2("adding '%s: %s'",header, nv[i+1]);
+  }
+  
+  if(MHD_NO == MHD_queue_response (proxy->http_connection, proxy->status, proxy->http_response)){
+    PRINT_INFO("No queue");
+    //TODO
+    //abort();
+    proxy->http_error = true;
+  }
+  
+  MHD_destroy_response (proxy->http_response);
+  proxy->http_response = NULL;
+}
+
+void
+http_cb_request_completed (void *cls,
+                                   struct MHD_Connection *connection,
+                                   void **con_cls,
+                                   enum MHD_RequestTerminationCode toe)
+{
+  (void)cls;
+  (void)connection;
+  struct HTTP_URI *http_uri;
+  struct Proxy *proxy;
+  
+  http_uri = (struct HTTP_URI *)*con_cls;
+  if(NULL == http_uri)
+    return;
+  proxy = (struct Proxy *)http_uri->proxy;
+  assert(NULL != proxy);
+  
+  PRINT_INFO2("http_cb_request_completed %i for %s; id %i",toe, http_uri->uri, proxy->id);
+  
+  if(NULL != proxy->http_response)
+  {
+    MHD_destroy_response (proxy->http_response);
+    proxy->http_response = NULL;
+  }
+  
+  if(proxy->spdy_active)
+  {
+    proxy->http_active = false;
+    if(MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
+    {
+      proxy->http_error = true;
+      if(proxy->stream_id > 0 /*&& NULL != proxy->spdy_connection->session*/)
+      {
+        //send RST_STREAM_STATUS_CANCEL
+        PRINT_INFO2("send rst_stream %i %i",proxy->spdy_active, proxy->stream_id );
+        spdylay_submit_rst_stream(proxy->spdy_connection->session, proxy->stream_id, 5);
+      }
+      /*else
+      {
+        DLL_remove(proxy->spdy_connection->proxies_head, proxy->spdy_connection->proxies_tail, proxy); 
+        free_proxy(proxy);
+      }*/
+    }
+  }
+  else
+  {
+    PRINT_INFO2("proxy free http id %i ", proxy->id);
+    free_proxy(proxy);
+  }
+    
+  --glob_opt.responses_pending;
+}
diff --git a/src/examples/mhd2spdy_http.h b/src/examples/mhd2spdy_http.h
new file mode 100644
index 0000000..89d3889
--- /dev/null
+++ b/src/examples/mhd2spdy_http.h
@@ -0,0 +1,54 @@
+/*
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file mhd2spdy_http.h
+ * @brief  HTTP part of the proxy. libmicrohttpd is used for the server side.
+ * @author Andrey Uzunov
+ */
+ 
+#ifndef HTTP_H
+#define HTTP_H
+
+#include "mhd2spdy_structures.h"
+
+
+int
+http_cb_request (void *cls,
+                struct MHD_Connection *connection,
+                const char *url,
+                const char *method,
+                const char *version,
+                const char *upload_data,
+                size_t *upload_data_size,
+                void **ptr);
+
+
+void * http_cb_log(void * cls, const char * uri);
+
+
+void
+http_create_response(struct Proxy* proxy, char **nv);
+
+
+void
+http_cb_request_completed (void *cls,
+                                   struct MHD_Connection *connection,
+                                   void **con_cls,
+                                   enum MHD_RequestTerminationCode toe);
+
+#endif
diff --git a/src/examples/mhd2spdy_spdy.c b/src/examples/mhd2spdy_spdy.c
new file mode 100644
index 0000000..95ec43c
--- /dev/null
+++ b/src/examples/mhd2spdy_spdy.c
@@ -0,0 +1,1150 @@
+/*
+ *
+ * Copyright (c) 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @file mhd2spdy_spdy.c
+ * @brief  SPDY part of the proxy. libspdylay is used for the client side.
+ *         The example spdycli.c from spdylay was used as basis;
+ *         however, multiple changes were made.
+ * @author Tatsuhiro Tsujikawa
+ * @author Andrey Uzunov
+ */
+
+#include "mhd2spdy_structures.h"
+#include "mhd2spdy_spdy.h"
+#include "mhd2spdy_http.h"
+
+
+/*
+ * Prints error containing the function name |func| and message |msg|
+ * and exit.
+ */
+static void
+spdy_dief(const char *func,
+          const char *msg)
+{
+  fprintf(stderr, "FATAL: %s: %s\n", func, msg);
+  exit(EXIT_FAILURE);
+}
+
+
+/*
+ * Prints error containing the function name |func| and error code
+ * |error_code| and exit.
+ */
+void
+spdy_diec(const char *func,
+          int error_code)
+{
+  fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
+          spdylay_strerror(error_code));
+  exit(EXIT_FAILURE);
+}
+
+
+static ssize_t
+spdy_cb_data_source_read(spdylay_session *session, int32_t stream_id, uint8_t *buf, size_t length, int *eof, spdylay_data_source *source, void *user_data)
+{
+  (void)session;
+  (void)stream_id;
+  (void)user_data;
+  
+  ssize_t ret;
+  assert(NULL != source);
+  assert(NULL != source->ptr);
+	struct Proxy *proxy = (struct Proxy *)(source->ptr);
+	void *newbody;
+  
+ 
+  if(length < 1)
+  {
+    PRINT_INFO("spdy_cb_data_source_read: length is 0");
+    return 0;
+	}
+  
+	if(!proxy->received_body_size)//nothing to write now
+  {
+    if(proxy->receiving_done)
+    {
+      PRINT_INFO("POST spdy EOF");
+      *eof = 1;
+    }
+      PRINT_INFO("POST SPDYLAY_ERR_DEFERRED");
+		return SPDYLAY_ERR_DEFERRED;//TODO SPDYLAY_ERR_DEFERRED should be used
+  }
+	
+	if(length >= proxy->received_body_size)
+	{
+		ret = proxy->received_body_size;
+		newbody = NULL;
+	}
+	else
+	{
+		ret = length;
+		if(NULL == (newbody = malloc(proxy->received_body_size - length)))
+		{
+			PRINT_INFO("no memory");
+			return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE;
+		}
+		memcpy(newbody, proxy->received_body + length, proxy->received_body_size - length);
+	}
+	memcpy(buf, proxy->received_body, ret);
+	free(proxy->received_body);
+	proxy->received_body = newbody;
+	proxy->received_body_size -= ret;
+  
+  if(0 == proxy->received_body_size && proxy->receiving_done)
+    {
+      PRINT_INFO("POST spdy EOF");
+    *eof = 1;
+  }
+  
+  PRINT_INFO2("given POST bytes to spdylay: %zd", ret);
+  
+  return ret;
+}
+
+
+/*
+ * The implementation of spdylay_send_callback type. Here we write
+ * |data| with size |length| to the network and return the number of
+ * bytes actually written. See the documentation of
+ * spdylay_send_callback for the details.
+ */
+static ssize_t
+spdy_cb_send(spdylay_session *session,
+             const uint8_t *data,
+             size_t length,
+             int flags,
+             void *user_data)
+{
+  (void)session;
+  (void)flags;
+  
+  //PRINT_INFO("spdy_cb_send called");
+  struct SPDY_Connection *connection;
+  ssize_t rv;
+  connection = (struct SPDY_Connection*)user_data;
+  connection->want_io = IO_NONE;
+  
+  if(glob_opt.ignore_rst_stream
+    && 16 == length
+    && 0x80 == data[0]
+    && 0x00 == data[2]
+    && 0x03 == data[3]
+    )
+  {
+    PRINT_INFO2("ignoring RST_STREAM for stream_id %i %i %i %i", data[8], data[9], data[10], data[11]);
+    glob_opt.ignore_rst_stream = false;
+    return 16;
+  }
+  glob_opt.ignore_rst_stream = false;
+  
+  if(connection->is_tls)
+  {
+    ERR_clear_error();
+    rv = SSL_write(connection->ssl, data, length);
+    if(rv < 0) {
+      int err = SSL_get_error(connection->ssl, rv);
+      if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
+        connection->want_io |= (err == SSL_ERROR_WANT_READ ?
+                               WANT_READ : WANT_WRITE);
+        rv = SPDYLAY_ERR_WOULDBLOCK;
+      } else {
+        rv = SPDYLAY_ERR_CALLBACK_FAILURE;
+      }
+    }
+  }
+  else
+  {
+    rv = write(connection->fd, 
+            data,
+            length);
+            
+    if (rv < 0)
+    {
+      switch(errno)
+      {				
+        case EAGAIN:
+  #if EAGAIN != EWOULDBLOCK
+        case EWOULDBLOCK:
+  #endif
+          connection->want_io |= WANT_WRITE;
+          rv = SPDYLAY_ERR_WOULDBLOCK;
+          break;
+          
+        default:
+          rv = SPDYLAY_ERR_CALLBACK_FAILURE;
+      }
+    }
+  }
+  
+  PRINT_INFO2("%zd bytes written by spdy", rv);
+  
+  if(rv > 0)
+    UPDATE_STAT(glob_stat.spdy_bytes_sent, rv);
+  
+  return rv;
+}
+
+
+/*
+ * The implementation of spdylay_recv_callback type. Here we read data
+ * from the network and write them in |buf|. The capacity of |buf| is
+ * |length| bytes. Returns the number of bytes stored in |buf|. See
+ * the documentation of spdylay_recv_callback for the details.
+ */
+static ssize_t
+spdy_cb_recv(spdylay_session *session,
+             uint8_t *buf,
+             size_t length, 
+             int flags,
+             void *user_data)
+{
+  (void)session;
+  (void)flags;
+  
+  struct SPDY_Connection *connection;
+  ssize_t rv;
+  
+  connection = (struct SPDY_Connection*)user_data;
+  //prevent monopolizing everything
+  if(!(++connection->counter % 10)) return SPDYLAY_ERR_WOULDBLOCK;
+  connection->want_io = IO_NONE;
+  if(connection->is_tls)
+  {
+    ERR_clear_error();
+    rv = SSL_read(connection->ssl, buf, length);
+    if(rv < 0) {
+      int err = SSL_get_error(connection->ssl, rv);
+      if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
+        connection->want_io |= (err == SSL_ERROR_WANT_READ ?
+                               WANT_READ : WANT_WRITE);
+        rv = SPDYLAY_ERR_WOULDBLOCK;
+      } else {
+        rv = SPDYLAY_ERR_CALLBACK_FAILURE;
+      }
+    } else if(rv == 0) {
+      rv = SPDYLAY_ERR_EOF;
+    }
+  }
+  else
+  {
+    rv = read(connection->fd, 
+            buf,
+            length);
+            
+    if (rv < 0)
+    {
+      switch(errno)
+      {				
+        case EAGAIN:
+  #if EAGAIN != EWOULDBLOCK
+        case EWOULDBLOCK:
+  #endif
+          connection->want_io |= WANT_READ;
+          rv = SPDYLAY_ERR_WOULDBLOCK;
+          break;
+          
+        default:
+          rv = SPDYLAY_ERR_CALLBACK_FAILURE;
+      }
+    }
+    else if(rv == 0)
+      rv = SPDYLAY_ERR_EOF;
+  }
+  
+  if(rv > 0)
+    UPDATE_STAT(glob_stat.spdy_bytes_received, rv);
+  
+  return rv;
+}
+
+
+static void
+spdy_cb_before_ctrl_send(spdylay_session *session,
+                    spdylay_frame_type type,
+                    spdylay_frame *frame,
+                    void *user_data)
+{
+  (void)user_data;
+  
+  int32_t stream_id;
+  struct Proxy *proxy;
+  
+  switch(type) {
+    case SPDYLAY_SYN_STREAM:
+      stream_id = frame->syn_stream.stream_id;
+      proxy = spdylay_session_get_stream_user_data(session, stream_id);
+      proxy->stream_id = stream_id;
+      ++glob_opt.streams_opened;
+      ++proxy->spdy_connection->streams_opened;
+      PRINT_INFO2("opening stream: str open %i; %s", glob_opt.streams_opened, proxy->url);
+      break;
+    case SPDYLAY_RST_STREAM:
+      //try to ignore duplicate RST_STREAMs
+      //TODO this will ignore RST_STREAMs also for bogus data
+      glob_opt.ignore_rst_stream = NULL==spdylay_session_get_stream_user_data(session, frame->rst_stream.stream_id);
+      PRINT_INFO2("sending RST_STREAM for %i; ignore %i; status %i",
+        frame->rst_stream.stream_id,
+        glob_opt.ignore_rst_stream,
+        frame->rst_stream.status_code);
+    break;
+    default:
+      break;
+  }
+}
+
+
+void
+spdy_cb_on_ctrl_recv(spdylay_session *session,
+                    spdylay_frame_type type,
+                    spdylay_frame *frame,
+                    void *user_data)
+{
+  (void)user_data;
+  
+  char **nv;
+  int32_t stream_id;
+  struct Proxy * proxy;
+
+  switch(type) {
+    case SPDYLAY_SYN_REPLY:
+      nv = frame->syn_reply.nv;
+      stream_id = frame->syn_reply.stream_id;
+    break;
+    case SPDYLAY_RST_STREAM:
+      stream_id = frame->rst_stream.stream_id;
+    break;
+    case SPDYLAY_HEADERS:
+      nv = frame->headers.nv;
+      stream_id = frame->headers.stream_id;
+    break;
+    default:
+      return;
+    break;
+  }
+
+  proxy = spdylay_session_get_stream_user_data(session, stream_id);
+  if(NULL == proxy)
+  {
+    PRINT_INFO2("received frame type %i for unkonwn stream id %i", type, stream_id);
+    return;
+    //DIE("no proxy obj");
+  }
+
+  switch(type) {
+    case SPDYLAY_SYN_REPLY:
+      PRINT_INFO2("received headers for %s", proxy->url);
+      http_create_response(proxy, nv);
+    break;
+    case SPDYLAY_RST_STREAM:
+      PRINT_INFO2("received reset stream for %s", proxy->url);
+      proxy->spdy_error = true;
+    break;
+    case SPDYLAY_HEADERS:
+      PRINT_INFO2("received headers for %s", proxy->url);
+      http_create_response(proxy, nv);
+    break;
+    default:
+      return;
+    break;
+  }
+  
+  glob_opt.spdy_data_received = true;
+}
+
+
+/*
+ * The implementation of spdylay_on_stream_close_callback type. We use
+ * this function to know the response is fully received. Since we just
+ * fetch 1 resource in this program, after reception of the response,
+ * we submit GOAWAY and close the session.
+ */
+static void
+spdy_cb_on_stream_close(spdylay_session *session,
+                       int32_t stream_id,
+                       spdylay_status_code status_code,
+                       void *user_data)
+{
+  (void)status_code;
+  (void)user_data;
+  
+  struct Proxy * proxy = spdylay_session_get_stream_user_data(session, stream_id);
+  
+  assert(NULL != proxy);
+  
+  --glob_opt.streams_opened;
+  --proxy->spdy_connection->streams_opened;
+  PRINT_INFO2("closing stream: str opened %i; remove proxy %i", glob_opt.streams_opened, proxy->id);
+   
+  DLL_remove(proxy->spdy_connection->proxies_head, proxy->spdy_connection->proxies_tail, proxy); 
+  if(proxy->http_active)
+  {
+    proxy->spdy_active = false;
+  }
+  else
+  {
+    free_proxy(proxy);
+  }
+}
+
+
+/*
+ * The implementation of spdylay_on_data_chunk_recv_callback type. We
+ * use this function to print the received response body.
+ */
+static void
+spdy_cb_on_data_chunk_recv(spdylay_session *session,
+                          uint8_t flags,
+                          int32_t stream_id,
+                          const uint8_t *data,
+                          size_t len,
+                          void *user_data)
+{
+  (void)flags;
+  (void)user_data;
+  
+  struct Proxy *proxy;
+  proxy = spdylay_session_get_stream_user_data(session, stream_id);
+  
+  if(NULL == proxy)
+  {
+    PRINT_INFO("proxy in spdy_cb_on_data_chunk_recv is NULL)");
+    return;
+	}
+  
+  if(!copy_buffer(data, len, &proxy->http_body, &proxy->http_body_size))
+  {
+    //TODO handle it better?
+    PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
+    return;
+  }
+  /*
+	if(NULL == proxy->http_body)
+		proxy->http_body = au_malloc(len);
+  else
+		proxy->http_body = realloc(proxy->http_body, proxy->http_body_size + len);
+	if(NULL == proxy->http_body)
+	{
+		PRINT_INFO("not enough memory (realloc returned NULL)");
+		return ;
+	}
+
+	memcpy(proxy->http_body + proxy->http_body_size, data, len);
+	proxy->http_body_size += len;
+  */
+  PRINT_INFO2("received data for %s; %zu bytes", proxy->url, len);
+  glob_opt.spdy_data_received = true;
+}
+
+
+static void
+spdy_cb_on_data_recv(spdylay_session *session,
+		                 uint8_t flags,
+                     int32_t stream_id,
+                     int32_t length,
+                     void *user_data)
+{
+  (void)length;
+  (void)user_data;
+  
+	if(flags & SPDYLAY_DATA_FLAG_FIN)
+	{
+    struct Proxy *proxy;
+    proxy = spdylay_session_get_stream_user_data(session, stream_id);
+    proxy->done = true;
+    PRINT_INFO2("last data frame received for %s", proxy->url);
+	}
+}
+
+
+/*
+ * Setup callback functions. Spdylay API offers many callback
+ * functions, but most of them are optional. The send_callback is
+ * always required. Since we use spdylay_session_recv(), the
+ * recv_callback is also required.
+ */
+static void
+spdy_setup_spdylay_callbacks(spdylay_session_callbacks *callbacks)
+{
+  memset(callbacks, 0, sizeof(spdylay_session_callbacks));
+  callbacks->send_callback = spdy_cb_send;
+  callbacks->recv_callback = spdy_cb_recv;
+  callbacks->before_ctrl_send_callback = spdy_cb_before_ctrl_send;
+  callbacks->on_ctrl_recv_callback = spdy_cb_on_ctrl_recv;
+  callbacks->on_stream_close_callback = spdy_cb_on_stream_close;
+  callbacks->on_data_chunk_recv_callback = spdy_cb_on_data_chunk_recv;
+  callbacks->on_data_recv_callback = spdy_cb_on_data_recv;
+}
+
+
+/*
+ * Callback function for SSL/TLS NPN. Since this program only supports
+ * SPDY protocol, if server does not offer SPDY protocol the Spdylay
+ * library supports, we terminate program.
+ */
+static int
+spdy_cb_ssl_select_next_proto(SSL* ssl,
+                                unsigned char **out,
+                                unsigned char *outlen,
+                                const unsigned char *in,
+                                unsigned int inlen,
+                                void *arg)
+{
+  (void)ssl;
+  
+  int rv;
+  uint16_t *spdy_proto_version;
+  
+  /* spdylay_select_next_protocol() selects SPDY protocol version the
+     Spdylay library supports. */
+  rv = spdylay_select_next_protocol(out, outlen, in, inlen);
+  if(rv <= 0) {
+    PRINT_INFO("Server did not advertise spdy/2 or spdy/3 protocol.");
+    return rv;
+  }
+  spdy_proto_version = (uint16_t*)arg;
+  *spdy_proto_version = rv;
+  return SSL_TLSEXT_ERR_OK;
+}
+
+
+/*
+ * Setup SSL context. We pass |spdy_proto_version| to get negotiated
+ * SPDY protocol version in NPN callback.
+ */
+void
+spdy_ssl_init_ssl_ctx(SSL_CTX *ssl_ctx,
+                      uint16_t *spdy_proto_version)
+{
+  /* Disable SSLv2 and enable all workarounds for buggy servers */
+  SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION);
+  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
+  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
+  /* Set NPN callback */
+  SSL_CTX_set_next_proto_select_cb(ssl_ctx, spdy_cb_ssl_select_next_proto,
+                                   spdy_proto_version);
+}
+
+
+static int
+spdy_ssl_handshake(SSL *ssl,
+                   int fd)
+{
+  int rv;
+  
+  if(SSL_set_fd(ssl, fd) == 0)
+    spdy_dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
+
+  ERR_clear_error();
+  rv = SSL_connect(ssl);
+  if(rv <= 0)
+    PRINT_INFO2("SSL_connect %s", ERR_error_string(ERR_get_error(), NULL));
+  
+  return rv;
+}
+
+
+/*
+ * Connects to the host |host| and port |port|.  This function returns
+ * the file descriptor of the client socket.
+ */
+static int
+spdy_socket_connect_to(const char *host,
+                       uint16_t port)
+{
+  struct addrinfo hints;
+  int fd = -1;
+  int rv;
+  char service[NI_MAXSERV];
+  struct addrinfo *res, *rp;
+  
+  //TODO checks
+  snprintf(service, sizeof(service), "%u", port);
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  rv = getaddrinfo(host, service, &hints, &res);
+  if(rv != 0)
+  {
+	  printf("%s\n",host);
+    spdy_dief("getaddrinfo", gai_strerror(rv));
+  }
+  for(rp = res; rp; rp = rp->ai_next)
+  {
+    fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+    if(fd == -1)
+      continue;
+    while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
+          errno == EINTR);
+    if(rv == 0)
+      break;
+    close(fd);
+    fd = -1;
+  }
+  freeaddrinfo(res);
+  
+  return fd;
+}
+
+
+static void
+spdy_socket_make_non_block(int fd)
+{
+  int flags;
+  int rv;
+  
+  while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
+  
+  if(flags == -1)
+    spdy_dief("fcntl", strerror(errno));
+    
+  while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
+ 
+  if(rv == -1)
+    spdy_dief("fcntl", strerror(errno));
+}
+
+
+/*
+ * Setting TCP_NODELAY is not mandatory for the SPDY protocol.
+ */
+static void
+spdy_socket_set_tcp_nodelay(int fd)
+{
+  int val = 1;
+  int rv;
+  
+  rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
+  if(rv == -1)
+    spdy_dief("setsockopt", strerror(errno));
+}
+
+/*
+ * Update |pollfd| based on the state of |connection|.
+ */
+ /*
+void
+spdy_ctl_poll(struct pollfd *pollfd,
+              struct SPDY_Connection *connection)
+{
+  pollfd->events = 0;
+  if(spdylay_session_want_read(connection->session) ||
+     connection->want_io & WANT_READ)
+  {
+    pollfd->events |= POLLIN;
+  }
+  if(spdylay_session_want_write(connection->session) ||
+     connection->want_io & WANT_WRITE)
+  {
+    pollfd->events |= POLLOUT;
+  }
+}*/
+
+
+/*
+ * Update |selectfd| based on the state of |connection|.
+ */
+bool
+spdy_ctl_select(fd_set * read_fd_set,
+                fd_set * write_fd_set, 
+                fd_set * except_fd_set,
+                struct SPDY_Connection *connection)
+{
+  (void)except_fd_set;
+  
+  bool ret = false;
+  
+  if(spdylay_session_want_read(connection->session) ||
+     connection->want_io & WANT_READ)
+  {
+    FD_SET(connection->fd, read_fd_set);
+    ret = true;
+  }
+  if(spdylay_session_want_write(connection->session) ||
+     connection->want_io & WANT_WRITE)
+  {
+    FD_SET(connection->fd, write_fd_set);
+    ret = true;
+  }
+  
+  return ret;
+}
+
+
+/*
+ * Performs the network I/O.
+ */
+int
+spdy_exec_io(struct SPDY_Connection *connection)
+{
+  int rv;
+  
+  rv = spdylay_session_recv(connection->session);
+  if(rv != 0)
+  {
+    PRINT_INFO2("spdylay_session_recv %i", rv);
+    return rv;
+  }
+  rv = spdylay_session_send(connection->session);
+  if(rv != 0)
+    PRINT_INFO2("spdylay_session_send %i", rv);
+    
+  return rv;
+}
+
+
+/*
+ * Fetches the resource denoted by |uri|.
+ */
+struct SPDY_Connection *
+spdy_connect(const struct URI *uri,
+             uint16_t port,
+             bool is_tls)
+{
+  spdylay_session_callbacks callbacks;
+  int fd;
+  SSL *ssl=NULL;
+  struct SPDY_Connection * connection = NULL;
+  int rv;
+
+  spdy_setup_spdylay_callbacks(&callbacks);
+
+  /* Establish connection and setup SSL */
+  PRINT_INFO2("connecting to %s:%i", uri->host, port);
+  fd = spdy_socket_connect_to(uri->host, port);
+  if(fd == -1)
+  {
+    PRINT_INFO("Could not open file descriptor");
+    return NULL;
+  }
+  
+  if(is_tls)
+  {
+    ssl = SSL_new(glob_opt.ssl_ctx);
+    if(ssl == NULL) {
+      spdy_dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
+    }
+    
+    //TODO non-blocking
+    /* To simplify the program, we perform SSL/TLS handshake in blocking
+       I/O. */
+    glob_opt.spdy_proto_version = 0;
+    rv = spdy_ssl_handshake(ssl, fd);
+    if(rv <= 0 || (glob_opt.spdy_proto_version != 3 && glob_opt.spdy_proto_version != 2))
+    {
+      PRINT_INFO("Closing SSL");
+      //no spdy on the other side
+      goto free_and_fail;
+    }
+  }
+  else
+  {
+    glob_opt.spdy_proto_version = 3;
+  }
+
+  if(NULL == (connection = au_malloc(sizeof(struct SPDY_Connection))))
+    goto free_and_fail;
+  
+  connection->is_tls = is_tls;
+  connection->ssl = ssl;
+  connection->want_io = IO_NONE;
+  if(NULL == (connection->host = strdup(uri->host)))
+    goto free_and_fail;
+
+  /* Here make file descriptor non-block */
+  spdy_socket_make_non_block(fd);
+  spdy_socket_set_tcp_nodelay(fd);
+
+  PRINT_INFO2("[INFO] SPDY protocol version = %d\n", glob_opt.spdy_proto_version);
+  rv = spdylay_session_client_new(&(connection->session), glob_opt.spdy_proto_version,
+                                  &callbacks, connection);
+  if(rv != 0) {
+    spdy_diec("spdylay_session_client_new", rv);
+  }
+  
+  connection->fd = fd;
+
+	return connection;
+  
+	//for GOTO
+	free_and_fail:
+  if(NULL != connection)
+  {
+    free(connection->host);
+    free(connection);
+  }
+  
+  if(is_tls)
+    SSL_shutdown(ssl);
+    
+  close(fd);
+  
+  if(is_tls)
+    SSL_free(ssl);
+  
+  return NULL;
+}
+
+
+void
+spdy_free_connection(struct SPDY_Connection * connection)
+{
+  struct Proxy *proxy;
+  struct Proxy *proxy_next;
+  
+  if(NULL != connection)
+  {
+    for(proxy = connection->proxies_head; NULL != proxy; proxy=proxy_next)
+    {
+      proxy_next = proxy->next;
+      DLL_remove(connection->proxies_head, connection->proxies_tail, proxy);
+      proxy->spdy_active = false;
+      proxy->spdy_error = true;
+      PRINT_INFO2("spdy_free_connection for id %i", proxy->id);
+      if(!proxy->http_active)
+      {
+        free_proxy(proxy);
+      }
+    }
+    spdylay_session_del(connection->session);
+    SSL_free(connection->ssl);
+    free(connection->host);
+    free(connection);
+    //connection->session = NULL;
+  }
+}
+
+
+int
+spdy_request(const char **nv,
+             struct Proxy *proxy,
+             bool with_body)
+{
+  int ret;
+  uint16_t port;
+  struct SPDY_Connection *connection;
+  spdylay_data_provider post_data;
+  
+  if(glob_opt.only_proxy)
+  {
+    connection = glob_opt.spdy_connection;
+  }
+  else
+  {
+    connection = glob_opt.spdy_connections_head;
+    while(NULL != connection)
+    {
+      if(0 == strcasecmp(proxy->uri->host, connection->host))
+        break;
+      connection = connection->next;
+    }
+  
+    if(NULL == connection)
+    {
+      //connect to host
+      port = proxy->uri->port;
+      if(0 == port) port = 443;
+      connection = spdy_connect(proxy->uri, port, true);
+      if(NULL != connection)
+      {
+        DLL_insert(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection);
+        glob_opt.total_spdy_connections++;
+      }
+      else
+        connection = glob_opt.spdy_connection;
+    }
+  }
+  
+  if(NULL == connection)
+  {
+    PRINT_INFO("there is no proxy!");
+    return -1;
+  }
+  
+  proxy->spdy_connection = connection;
+  if(with_body)
+  {
+    post_data.source.ptr = proxy;
+    post_data.read_callback = &spdy_cb_data_source_read;
+    ret = spdylay_submit_request(connection->session, 0, nv, &post_data, proxy);
+  }
+  else
+    ret = spdylay_submit_request(connection->session, 0, nv, NULL, proxy);
+  
+  if(ret != 0) {
+    spdy_diec("spdylay_spdy_submit_request", ret);
+  }
+  PRINT_INFO2("adding proxy %i", proxy->id);
+  if(NULL != connection->proxies_head)
+    PRINT_INFO2("before proxy %i", connection->proxies_head->id);
+  DLL_insert(connection->proxies_head, connection->proxies_tail, proxy);
+  
+  return ret;
+}
+
+/*
+void
+spdy_get_pollfdset(struct pollfd fds[],
+                   struct SPDY_Connection *connections[],
+                   unsigned int max_size,
+                   nfds_t *real_size)
+{
+  struct SPDY_Connection *connection;
+  struct Proxy *proxy;
+  
+  *real_size = 0;
+  if(max_size<1)
+    return;
+    
+  if(NULL != glob_opt.spdy_connection)
+  {
+    spdy_ctl_poll(&(fds[*real_size]), glob_opt.spdy_connection);
+    if(!fds[*real_size].events)
+    {
+      //PRINT_INFO("TODO drop connection");
+      glob_opt.streams_opened -= glob_opt.spdy_connection->streams_opened;
+      
+      for(proxy = glob_opt.spdy_connection->proxies_head; NULL != proxy; proxy=proxy->next)
+      {
+        abort();
+        DLL_remove(glob_opt.spdy_connection->proxies_head, glob_opt.spdy_connection->proxies_tail, proxy);
+        proxy->spdy_active = false;
+      }
+      spdy_free_connection(glob_opt.spdy_connection);
+      glob_opt.spdy_connection = NULL;
+    }
+    else
+    {
+      fds[*real_size].fd = glob_opt.spdy_connection->fd;
+      connections[*real_size] = glob_opt.spdy_connection;
+      ++(*real_size);
+    }
+  }
+  
+  connection = glob_opt.spdy_connections_head;
+  
+  while(NULL != connection && *real_size < max_size)
+  {
+    assert(!glob_opt.only_proxy);
+    spdy_ctl_poll(&(fds[*real_size]), connection);
+    if(!fds[*real_size].events)
+    {
+      //PRINT_INFO("TODO drop connection");
+      glob_opt.streams_opened -= connection->streams_opened;
+      DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection);
+      glob_opt.total_spdy_connections--;
+      
+      for(proxy = connection->proxies_head; NULL != proxy; proxy=proxy->next)
+      {
+        abort();
+        DLL_remove(connection->proxies_head, connection->proxies_tail, proxy);
+        proxy->spdy_active = false;
+      }
+      spdy_free_connection(connection);
+    }
+    else
+    {
+      fds[*real_size].fd = connection->fd;
+      connections[*real_size] = connection;
+      ++(*real_size);
+    }
+    connection = connection->next;
+  }
+  
+  //, "TODO max num of conn reached; close something"
+  assert(NULL == connection);
+}
+*/
+
+int
+spdy_get_selectfdset(fd_set * read_fd_set,
+                      fd_set * write_fd_set, 
+                      fd_set * except_fd_set,
+                      struct SPDY_Connection *connections[],
+                      unsigned int max_size,
+                      nfds_t *real_size)
+{
+  struct SPDY_Connection *connection;
+  struct SPDY_Connection *next_connection;
+  bool ret;
+  int maxfd = 0;
+  
+  *real_size = 0;
+  if(max_size<1)
+    return 0;
+    
+  if(NULL != glob_opt.spdy_connection)
+  {
+    ret = spdy_ctl_select(read_fd_set,
+				 write_fd_set, 
+				 except_fd_set, glob_opt.spdy_connection);
+    if(!ret)
+    {
+      glob_opt.streams_opened -= glob_opt.spdy_connection->streams_opened;
+      
+      PRINT_INFO("spdy_free_connection in spdy_get_selectfdset");
+      spdy_free_connection(glob_opt.spdy_connection);
+      glob_opt.spdy_connection = NULL;
+    }
+    else
+    {
+      connections[*real_size] = glob_opt.spdy_connection;
+      ++(*real_size);
+      if(maxfd < glob_opt.spdy_connection->fd) maxfd = glob_opt.spdy_connection->fd;
+    }
+  }
+  
+  connection = glob_opt.spdy_connections_head;
+  
+  while(NULL != connection && *real_size < max_size)
+  {
+    assert(!glob_opt.only_proxy);
+    ret = spdy_ctl_select(read_fd_set,
+				 write_fd_set, 
+				 except_fd_set, connection);
+         
+    next_connection = connection->next;
+    if(!ret)
+    {
+      glob_opt.streams_opened -= connection->streams_opened;
+      DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection);
+      glob_opt.total_spdy_connections--;
+      
+      PRINT_INFO("spdy_free_connection in spdy_get_selectfdset");
+      spdy_free_connection(connection);
+    }
+    else
+    {
+      connections[*real_size] = connection;
+      ++(*real_size);
+      if(maxfd < connection->fd) maxfd = connection->fd;
+    }
+    connection = next_connection;
+  }
+  
+  //, "TODO max num of conn reached; close something"
+  assert(NULL == connection);
+  
+  return maxfd;
+}
+
+/*
+void
+spdy_run(struct pollfd fds[],
+         struct SPDY_Connection *connections[],
+         int size)
+{
+  int i;
+  int ret;
+  struct Proxy *proxy;
+  
+  for(i=0; i<size; ++i)
+  {
+    //  PRINT_INFO2("exec about to be called for %s", connections[i]->host);
+    if(fds[i].revents & (POLLIN | POLLOUT))
+    {
+      ret = spdy_exec_io(connections[i]);
+      //PRINT_INFO2("%i",ret);
+      //if((spdy_pollfds[i].revents & POLLHUP) || (spdy_pollfds[0].revents & POLLERR))
+      //  PRINT_INFO("SPDY SPDY_Connection error");
+      
+      //TODO POLLRDHUP
+      // always close on ret != 0?
+        
+      if(0 != ret)
+      {
+        glob_opt.streams_opened -= connections[i]->streams_opened;
+        if(connections[i] == glob_opt.spdy_connection)
+        {
+          glob_opt.spdy_connection = NULL;
+        }
+        else
+        {
+          DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connections[i]);
+          glob_opt.total_spdy_connections--;
+        }
+        for(proxy = connections[i]->proxies_head; NULL != proxy; proxy=proxy->next)
+        {
+        abort();
+          DLL_remove(connections[i]->proxies_head, connections[i]->proxies_tail, proxy);
+          proxy->spdy_active = false;
+          proxy->spdy_error = true;
+          PRINT_INFO2("spdy_free_connection for id %i", proxy->id);
+        }
+        PRINT_INFO("spdy_free_connection in loop");
+        spdy_free_connection(connections[i]);
+      }
+    }
+    else
+      PRINT_INFO("not called");
+  }
+}
+*/
+
+void
+spdy_run_select(fd_set * read_fd_set,
+                fd_set * write_fd_set, 
+                fd_set * except_fd_set,
+                struct SPDY_Connection *connections[],
+                int size)
+{
+  int i;
+  int ret;
+  
+  for(i=0; i<size; ++i)
+  {
+    //  PRINT_INFO2("exec about to be called for %s", connections[i]->host);
+    if(FD_ISSET(connections[i]->fd, read_fd_set) || FD_ISSET(connections[i]->fd, write_fd_set) || FD_ISSET(connections[i]->fd, except_fd_set))
+    {
+      //raise(SIGINT);
+      ret = spdy_exec_io(connections[i]);
+        
+      if(0 != ret)
+      {
+        glob_opt.streams_opened -= connections[i]->streams_opened;
+        if(connections[i] == glob_opt.spdy_connection)
+        {
+          glob_opt.spdy_connection = NULL;
+        }
+        else
+        {
+          DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connections[i]);
+          glob_opt.total_spdy_connections--;
+        }
+        PRINT_INFO("in spdy_run_select");
+        spdy_free_connection(connections[i]);
+      }
+    }
+    else
+    {
+      PRINT_INFO("not called");
+      //PRINT_INFO2("connection->want_io %i",connections[i]->want_io);
+      //PRINT_INFO2("read %i",spdylay_session_want_read(connections[i]->session));
+      //PRINT_INFO2("write %i",spdylay_session_want_write(connections[i]->session));
+      //raise(SIGINT);
+    }
+  }
+}
diff --git a/src/examples/mhd2spdy_spdy.h b/src/examples/mhd2spdy_spdy.h
new file mode 100644
index 0000000..4207c62
--- /dev/null
+++ b/src/examples/mhd2spdy_spdy.h
@@ -0,0 +1,102 @@
+/*
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file mhd2spdy_spdy.h
+ * @brief  SPDY part of the proxy. libspdylay is used for the client side.
+ * @author Andrey Uzunov
+ */
+ 
+#ifndef SPDY_H
+#define SPDY_H
+
+#include "mhd2spdy_structures.h"
+
+
+struct SPDY_Connection *
+spdy_connect(const struct URI *uri,
+             uint16_t port,
+             bool is_tls);
+
+
+void
+spdy_ctl_poll(struct pollfd *pollfd,
+              struct SPDY_Connection *connection);
+
+
+bool
+spdy_ctl_select(fd_set * read_fd_set,
+                fd_set * write_fd_set, 
+                fd_set * except_fd_set,
+                struct SPDY_Connection *connection);
+
+
+int
+spdy_exec_io(struct SPDY_Connection *connection);
+
+
+void
+spdy_diec(const char *func,
+          int error_code);
+
+
+int 
+spdy_request(const char **nv,
+             struct Proxy *proxy,
+             bool with_body);
+
+
+void
+spdy_ssl_init_ssl_ctx(SSL_CTX *ssl_ctx,
+                      uint16_t *spdy_proto_version);
+
+
+void
+spdy_free_connection(struct SPDY_Connection * connection);
+
+
+void
+spdy_get_pollfdset(struct pollfd fds[],
+                   struct SPDY_Connection *connections[],
+                   unsigned int max_size,
+                   nfds_t *real_size);
+
+
+int
+spdy_get_selectfdset(fd_set * read_fd_set,
+                    fd_set * write_fd_set, 
+                    fd_set * except_fd_set,
+                    struct SPDY_Connection *connections[],
+                    unsigned int max_size,
+                    nfds_t *real_size);
+
+
+void
+spdy_run(struct pollfd fds[],
+        struct SPDY_Connection *connections[],
+        int size);
+
+
+void
+spdy_run_select(fd_set * read_fd_set,
+                fd_set * write_fd_set, 
+                fd_set * except_fd_set,
+                struct SPDY_Connection *connections[],
+                int size);
+
+
+#endif
diff --git a/src/examples/mhd2spdy_structures.c b/src/examples/mhd2spdy_structures.c
new file mode 100644
index 0000000..6d4a407
--- /dev/null
+++ b/src/examples/mhd2spdy_structures.c
@@ -0,0 +1,162 @@
+/*
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file mhd2spdy_structures.h
+ * @brief  Common functions, macros.
+ * @author Andrey Uzunov
+ */
+ 
+#include "mhd2spdy_structures.h"
+
+
+void
+free_uri(struct URI * uri)
+{
+  if(NULL != uri)
+  {
+    free(uri->full_uri);
+    free(uri->scheme);
+    free(uri->host_and_port);
+    free(uri->host);
+    free(uri->path);
+    free(uri->path_and_more);
+    free(uri->query);
+    free(uri->fragment);
+    uri->port = 0;
+    free(uri);
+  }
+}
+
+
+int
+init_parse_uri(regex_t * preg)
+{
+  // RFC 2396
+  // ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
+      /*
+        scheme    = $2
+      authority = $4
+      path      = $5
+      query     = $7
+      fragment  = $9
+      */
+  
+  return regcomp(preg, "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?", REG_EXTENDED);
+}
+
+void
+deinit_parse_uri(regex_t * preg)
+{
+  regfree(preg);
+}
+  
+int
+parse_uri(regex_t * preg,
+          char * full_uri,
+          struct URI ** uri)
+{
+  int ret;
+  char *colon;
+  long long port;
+  size_t nmatch = 10;
+  regmatch_t pmatch[10];
+
+  if (0 != (ret = regexec(preg, full_uri, nmatch, pmatch, 0)))
+    return ret;
+    
+  *uri = au_malloc(sizeof(struct URI));
+  if(NULL == *uri)
+    return -200;
+    
+  (*uri)->full_uri = strdup(full_uri);
+  
+  asprintf(&((*uri)->scheme), "%.*s",pmatch[2].rm_eo - pmatch[2].rm_so, &full_uri[pmatch[2].rm_so]);
+  asprintf(&((*uri)->host_and_port), "%.*s",pmatch[4].rm_eo - pmatch[4].rm_so, &full_uri[pmatch[4].rm_so]);
+  asprintf(&((*uri)->path), "%.*s",pmatch[5].rm_eo - pmatch[5].rm_so, &full_uri[pmatch[5].rm_so]);
+  asprintf(&((*uri)->path_and_more), "%.*s",pmatch[9].rm_eo - pmatch[5].rm_so, &full_uri[pmatch[5].rm_so]);
+  asprintf(&((*uri)->query), "%.*s",pmatch[7].rm_eo - pmatch[7].rm_so, &full_uri[pmatch[7].rm_so]);
+  asprintf(&((*uri)->fragment), "%.*s",pmatch[9].rm_eo - pmatch[9].rm_so, &full_uri[pmatch[9].rm_so]);
+  
+  colon = strrchr((*uri)->host_and_port, ':');
+  if(NULL == colon)
+  {
+    (*uri)->host = strdup((*uri)->host_and_port);
+    (*uri)->port = 0;
+   
+    return 0;
+  }
+  
+  port = atoi(colon  + 1);
+  if(port<1 || port >= 256 * 256)
+  {
+    free_uri(*uri);
+    return -100;
+  }
+  (*uri)->port = port;
+  asprintf(&((*uri)->host), "%.*s", (int)(colon - (*uri)->host_and_port), (*uri)->host_and_port);
+  
+  return 0;
+}
+
+
+void
+free_proxy(struct Proxy *proxy)
+{
+  PRINT_INFO2("free proxy called for '%s'", proxy->url);
+  if(NULL != proxy->http_body && proxy->http_body_size > 0)
+    UPDATE_STAT(glob_stat.spdy_bytes_received_and_dropped, proxy->http_body_size);
+  free(proxy->http_body);
+  free_uri(proxy->uri);
+	free(proxy->url);
+	free(proxy->http_uri);
+	free(proxy);
+}
+
+
+void *au_malloc(size_t size)
+{
+  void *new_memory;
+  
+  new_memory = malloc(size);
+  if(NULL != new_memory)
+  {
+    glob_opt.global_memory += size;
+    memset(new_memory, 0, size);
+  }
+  return new_memory;
+}
+
+
+bool
+copy_buffer(const void *src, size_t src_size, void **dst, size_t *dst_size)
+{
+  if(0 == src_size)
+    return true;
+  
+  if(NULL == *dst)
+		*dst = malloc(src_size);
+	else
+		*dst = realloc(*dst, src_size + *dst_size);
+	if(NULL == *dst)
+		return false;
+
+	memcpy(*dst + *dst_size, src, src_size);
+	*dst_size += src_size;
+  
+  return true;
+}
diff --git a/src/examples/mhd2spdy_structures.h b/src/examples/mhd2spdy_structures.h
new file mode 100644
index 0000000..f567934
--- /dev/null
+++ b/src/examples/mhd2spdy_structures.h
@@ -0,0 +1,296 @@
+/*
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file mhd2spdy_structures.h
+ * @brief  Common structures, functions, macros and global variables.
+ * @author Andrey Uzunov
+ */
+#ifndef STRUCTURES_H
+#define STRUCTURES_H
+
+#define _GNU_SOURCE
+ 
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <assert.h>
+#include <microhttpd.h>
+#include <signal.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <spdylay/spdylay.h>
+#include <getopt.h>
+
+
+/* WANT_READ if SSL connection needs more input; or WANT_WRITE if it
+   needs more output; or IO_NONE. This is necessary because SSL/TLS
+   re-negotiation is possible at any time. Spdylay API offers
+   similar functions like spdylay_session_want_read() and
+   spdylay_session_want_write() but they do not take into account
+   SSL connection. */
+enum
+{
+  IO_NONE,
+  WANT_READ,
+  WANT_WRITE
+};
+
+
+struct Proxy;
+
+
+struct SPDY_Connection {
+  SSL *ssl;
+  spdylay_session *session;
+  struct SPDY_Connection *prev;
+  struct SPDY_Connection *next;
+  struct Proxy *proxies_head;
+  struct Proxy *proxies_tail;
+  char *host;
+  int fd;
+  int want_io;
+  uint counter;
+  uint streams_opened;
+  bool is_tls;
+};
+
+
+struct URI
+{
+  char * full_uri;
+  char * scheme;
+  char * host_and_port;
+  char * host;
+  char * path;
+  char * path_and_more;
+  char * query;
+  char * fragment;
+  uint16_t port;
+};
+
+
+struct HTTP_URI;
+
+
+struct Proxy
+{
+	struct MHD_Connection *http_connection;
+	struct MHD_Response *http_response;
+	struct URI *uri;
+  struct HTTP_URI *http_uri;
+  struct SPDY_Connection *spdy_connection;
+  struct Proxy *next;
+  struct Proxy *prev;
+	char *url;
+	char *version;
+	void *http_body;
+	void *received_body;
+	size_t http_body_size;
+	size_t received_body_size;
+	ssize_t length;
+	int status;
+	int id;
+  int32_t stream_id;
+	bool done;
+	bool http_error;
+	bool spdy_error;
+  bool http_active;
+  bool spdy_active;
+  bool receiving_done;
+};
+
+
+struct HTTP_URI
+{
+  char * uri;
+  struct Proxy * proxy;
+};
+
+
+struct SPDY_Headers
+{
+  const char **nv;
+  int num;
+  int cnt;
+};
+
+
+struct global_options
+{
+  char *spdy2http_str;
+  struct SPDY_Connection *spdy_connection;
+  struct SPDY_Connection *spdy_connections_head;
+  struct SPDY_Connection *spdy_connections_tail;
+  int streams_opened;
+  int responses_pending;
+  regex_t uri_preg;
+  size_t global_memory;
+  SSL_CTX *ssl_ctx;
+  uint32_t total_spdy_connections;
+  uint16_t spdy_proto_version;
+  uint16_t listen_port;
+  bool verbose;
+  bool only_proxy;
+  bool spdy_data_received;
+  bool statistics;
+  bool ignore_rst_stream;
+}
+glob_opt;
+
+
+struct global_statistics
+{
+  //unsigned long long http_bytes_sent;
+  //unsigned long long http_bytes_received;
+  unsigned long long spdy_bytes_sent;
+  unsigned long long spdy_bytes_received;
+  unsigned long long spdy_bytes_received_and_dropped;
+}
+glob_stat;
+
+
+//forbidden headers
+#define SPDY_HTTP_HEADER_TRANSFER_ENCODING "transfer-encoding"
+#define SPDY_HTTP_HEADER_PROXY_CONNECTION "proxy-connection"
+#define SPDY_HTTP_HEADER_KEEP_ALIVE "keep-alive"
+#define SPDY_HTTP_HEADER_CONNECTION "connection"
+
+#define MAX_SPDY_CONNECTIONS 100
+
+#define SPDY_MAX_OUTLEN 4096
+
+/**
+ * Insert an element at the head of a DLL. Assumes that head, tail and
+ * element are structs with prev and next fields.
+ *
+ * @param head pointer to the head of the DLL (struct ? *)
+ * @param tail pointer to the tail of the DLL (struct ? *)
+ * @param element element to insert (struct ? *)
+ */
+#define DLL_insert(head,tail,element) do { \
+	(element)->next = (head); \
+	(element)->prev = NULL; \
+	if ((tail) == NULL) \
+		(tail) = element; \
+	else \
+		(head)->prev = element; \
+	(head) = (element); } while (0)
+
+
+/**
+ * Remove an element from a DLL. Assumes
+ * that head, tail and element are structs
+ * with prev and next fields.
+ *
+ * @param head pointer to the head of the DLL (struct ? *)
+ * @param tail pointer to the tail of the DLL (struct ? *)
+ * @param element element to remove (struct ? *)
+ */
+#define DLL_remove(head,tail,element) do { \
+	if ((element)->prev == NULL) \
+		(head) = (element)->next;  \
+	else \
+		(element)->prev->next = (element)->next; \
+	if ((element)->next == NULL) \
+		(tail) = (element)->prev;  \
+	else \
+		(element)->next->prev = (element)->prev; \
+	(element)->next = NULL; \
+	(element)->prev = NULL; } while (0)
+
+
+#define PRINT_INFO(msg) do{\
+  if(glob_opt.verbose){\
+	printf("%i:%s\n", __LINE__, msg);\
+	fflush(stdout);\
+	}\
+  }\
+	while(0)
+
+
+#define PRINT_INFO2(fmt, ...) do{\
+  if(glob_opt.verbose){\
+	printf("%i\n", __LINE__);\
+	printf(fmt,##__VA_ARGS__);\
+	printf("\n");\
+	fflush(stdout);\
+	}\
+	}\
+	while(0)
+  
+
+#define DIE(msg) do{\
+	printf("FATAL ERROR (line %i): %s\n", __LINE__, msg);\
+	fflush(stdout);\
+  exit(EXIT_FAILURE);\
+	}\
+	while(0)
+  
+  
+#define UPDATE_STAT(stat, value) do{\
+  if(glob_opt.statistics)\
+  {\
+    stat += value;\
+  }\
+  }\
+  while(0)
+
+
+void
+free_uri(struct URI * uri);
+
+
+int
+init_parse_uri(regex_t * preg);
+
+
+void
+deinit_parse_uri(regex_t * preg);
+
+
+int
+parse_uri(regex_t * preg,
+          char * full_uri,
+          struct URI ** uri);
+
+
+void
+free_proxy(struct Proxy *proxy);
+
+
+void *
+au_malloc(size_t size);
+
+
+bool
+copy_buffer(const void *src, size_t src_size, void **dst, size_t *dst_size);
+
+#endif
diff --git a/src/examples/minimal_example.c b/src/examples/minimal_example.c
new file mode 100644
index 0000000..313651c
--- /dev/null
+++ b/src/examples/minimal_example.c
@@ -0,0 +1,83 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+/**
+ * @file minimal_example.c
+ * @brief minimal example for how to use libmicrohttpd
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+
+#define PAGE "<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>"
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size, void **ptr)
+{
+  static int aptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp (method, "GET"))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+    {
+      /* do never respond on first call */
+      *ptr = &aptr;
+      return MHD_YES;
+    }
+  *ptr = NULL;                  /* reset when done */
+  response = MHD_create_response_from_buffer (strlen (me),
+					      (void *) me,
+					      MHD_RESPMEM_PERSISTENT);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+
+  if (argc != 2)
+    {
+      printf ("%s PORT\n", argv[0]);
+      return 1;
+    }
+  d = MHD_start_daemon (// MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | MHD_USE_POLL,
+			MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+			// MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG | MHD_USE_POLL,
+			// MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        atoi (argv[1]),
+                        NULL, NULL, &ahc_echo, PAGE,
+			MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120,
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  return 0;
+}
diff --git a/src/examples/minimal_example_comet.c b/src/examples/minimal_example_comet.c
new file mode 100644
index 0000000..0c9d264
--- /dev/null
+++ b/src/examples/minimal_example_comet.c
@@ -0,0 +1,85 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+/**
+ * @file minimal_example.c
+ * @brief minimal example for how to generate an infinite stream with libmicrohttpd
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+
+static ssize_t
+data_generator (void *cls, uint64_t pos, char *buf, size_t max)
+{
+  if (max < 80)
+    return 0;
+  memset (buf, 'A', max - 1);
+  buf[79] = '\n';
+  return 80;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size, void **ptr)
+{
+  static int aptr;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp (method, "GET"))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+    {
+      /* do never respond on first call */
+      *ptr = &aptr;
+      return MHD_YES;
+    }
+  *ptr = NULL;                  /* reset when done */
+  response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
+                                                80,
+                                                &data_generator, NULL, NULL);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+
+  if (argc != 2)
+    {
+      printf ("%s PORT\n", argv[0]);
+      return 1;
+    }
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        atoi (argv[1]),
+                        NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  return 0;
+}
diff --git a/src/examples/post_example.c b/src/examples/post_example.c
new file mode 100644
index 0000000..d8d13f9
--- /dev/null
+++ b/src/examples/post_example.c
@@ -0,0 +1,750 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2011 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+/**
+ * @file post_example.c
+ * @brief example for processing POST requests using libmicrohttpd
+ * @author Christian Grothoff
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <time.h>
+#include <microhttpd.h>
+
+/**
+ * Invalid method page.
+ */
+#define METHOD_ERROR "<html><head><title>Illegal request</title></head><body>Go away.</body></html>"
+
+/**
+ * Invalid URL page.
+ */
+#define NOT_FOUND_ERROR "<html><head><title>Not found</title></head><body>Go away.</body></html>"
+
+/**
+ * Front page. (/)
+ */
+#define MAIN_PAGE "<html><head><title>Welcome</title></head><body><form action=\"/2\" method=\"post\">What is your name? <input type=\"text\" name=\"v1\" value=\"%s\" /><input type=\"submit\" value=\"Next\" /></body></html>"
+
+/**
+ * Second page. (/2)
+ */
+#define SECOND_PAGE "<html><head><title>Tell me more</title></head><body><a href=\"/\">previous</a> <form action=\"/S\" method=\"post\">%s, what is your job? <input type=\"text\" name=\"v2\" value=\"%s\" /><input type=\"submit\" value=\"Next\" /></body></html>"
+
+/**
+ * Second page (/S)
+ */
+#define SUBMIT_PAGE "<html><head><title>Ready to submit?</title></head><body><form action=\"/F\" method=\"post\"><a href=\"/2\">previous </a> <input type=\"hidden\" name=\"DONE\" value=\"yes\" /><input type=\"submit\" value=\"Submit\" /></body></html>"
+
+/**
+ * Last page.
+ */
+#define LAST_PAGE "<html><head><title>Thank you</title></head><body>Thank you.</body></html>"
+
+/**
+ * Name of our cookie.
+ */
+#define COOKIE_NAME "session"
+
+
+/**
+ * State we keep for each user/session/browser.
+ */
+struct Session
+{
+  /**
+   * We keep all sessions in a linked list.
+   */
+  struct Session *next;
+
+  /**
+   * Unique ID for this session.
+   */
+  char sid[33];
+
+  /**
+   * Reference counter giving the number of connections
+   * currently using this session.
+   */
+  unsigned int rc;
+
+  /**
+   * Time when this session was last active.
+   */
+  time_t start;
+
+  /**
+   * String submitted via form.
+   */
+  char value_1[64];
+
+  /**
+   * Another value submitted via form.
+   */
+  char value_2[64];
+
+};
+
+
+/**
+ * Data kept per request.
+ */
+struct Request
+{
+
+  /**
+   * Associated session.
+   */
+  struct Session *session;
+
+  /**
+   * Post processor handling form data (IF this is
+   * a POST request).
+   */
+  struct MHD_PostProcessor *pp;
+
+  /**
+   * URL to serve in response to this POST (if this request
+   * was a 'POST')
+   */
+  const char *post_url;
+
+};
+
+
+/**
+ * Linked list of all active sessions.  Yes, O(n) but a
+ * hash table would be overkill for a simple example...
+ */
+static struct Session *sessions;
+
+
+
+
+/**
+ * Return the session handle for this connection, or
+ * create one if this is a new user.
+ */
+static struct Session *
+get_session (struct MHD_Connection *connection)
+{
+  struct Session *ret;
+  const char *cookie;
+
+  cookie = MHD_lookup_connection_value (connection,
+					MHD_COOKIE_KIND,
+					COOKIE_NAME);
+  if (cookie != NULL)
+    {
+      /* find existing session */
+      ret = sessions;
+      while (NULL != ret)
+	{
+	  if (0 == strcmp (cookie, ret->sid))
+	    break;
+	  ret = ret->next;
+	}
+      if (NULL != ret)
+	{
+	  ret->rc++;
+	  return ret;
+	}
+    }
+  /* create fresh session */
+  ret = calloc (1, sizeof (struct Session));
+  if (NULL == ret)
+    {
+      fprintf (stderr, "calloc error: %s\n", strerror (errno));
+      return NULL;
+    }
+  /* not a super-secure way to generate a random session ID,
+     but should do for a simple example... */
+  snprintf (ret->sid,
+	    sizeof (ret->sid),
+	    "%X%X%X%X",
+	    (unsigned int) rand (),
+	    (unsigned int) rand (),
+	    (unsigned int) rand (),
+	    (unsigned int) rand ());
+  ret->rc++;
+  ret->start = time (NULL);
+  ret->next = sessions;
+  sessions = ret;
+  return ret;
+}
+
+
+/**
+ * Type of handler that generates a reply.
+ *
+ * @param cls content for the page (handler-specific)
+ * @param mime mime type to use
+ * @param session session information
+ * @param connection connection to process
+ * @param MHD_YES on success, MHD_NO on failure
+ */
+typedef int (*PageHandler)(const void *cls,
+			   const char *mime,
+			   struct Session *session,
+			   struct MHD_Connection *connection);
+
+
+/**
+ * Entry we generate for each page served.
+ */
+struct Page
+{
+  /**
+   * Acceptable URL for this page.
+   */
+  const char *url;
+
+  /**
+   * Mime type to set for the page.
+   */
+  const char *mime;
+
+  /**
+   * Handler to call to generate response.
+   */
+  PageHandler handler;
+
+  /**
+   * Extra argument to handler.
+   */
+  const void *handler_cls;
+};
+
+
+/**
+ * Add header to response to set a session cookie.
+ *
+ * @param session session to use
+ * @param response response to modify
+ */
+static void
+add_session_cookie (struct Session *session,
+		    struct MHD_Response *response)
+{
+  char cstr[256];
+  snprintf (cstr,
+	    sizeof (cstr),
+	    "%s=%s",
+	    COOKIE_NAME,
+	    session->sid);
+  if (MHD_NO ==
+      MHD_add_response_header (response,
+			       MHD_HTTP_HEADER_SET_COOKIE,
+			       cstr))
+    {
+      fprintf (stderr,
+	       "Failed to set session cookie header!\n");
+    }
+}
+
+
+/**
+ * Handler that returns a simple static HTTP page that
+ * is passed in via 'cls'.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ */
+static int
+serve_simple_form (const void *cls,
+		   const char *mime,
+		   struct Session *session,
+		   struct MHD_Connection *connection)
+{
+  int ret;
+  const char *form = cls;
+  struct MHD_Response *response;
+
+  /* return static form */
+  response = MHD_create_response_from_buffer (strlen (form),
+					      (void *) form,
+					      MHD_RESPMEM_PERSISTENT);
+  if (NULL == response)
+    return MHD_NO;
+  add_session_cookie (session, response);
+  MHD_add_response_header (response,
+			   MHD_HTTP_HEADER_CONTENT_ENCODING,
+			   mime);
+  ret = MHD_queue_response (connection,
+			    MHD_HTTP_OK,
+			    response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+/**
+ * Handler that adds the 'v1' value to the given HTML code.
+ *
+ * @param cls unused
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ */
+static int
+fill_v1_form (const void *cls,
+	      const char *mime,
+	      struct Session *session,
+	      struct MHD_Connection *connection)
+{
+  int ret;
+  char *reply;
+  struct MHD_Response *response;
+
+  reply = malloc (strlen (MAIN_PAGE) + strlen (session->value_1) + 1);
+  if (NULL == reply)
+    return MHD_NO;
+  snprintf (reply,
+	    strlen (MAIN_PAGE) + strlen (session->value_1) + 1,
+	    MAIN_PAGE,
+	    session->value_1);
+  /* return static form */
+  response = MHD_create_response_from_buffer (strlen (reply),
+					      (void *) reply,
+					      MHD_RESPMEM_MUST_FREE);
+  if (NULL == response)
+    return MHD_NO;
+  add_session_cookie (session, response);
+  MHD_add_response_header (response,
+			   MHD_HTTP_HEADER_CONTENT_ENCODING,
+			   mime);
+  ret = MHD_queue_response (connection,
+			    MHD_HTTP_OK,
+			    response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+/**
+ * Handler that adds the 'v1' and 'v2' values to the given HTML code.
+ *
+ * @param cls unused
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ */
+static int
+fill_v1_v2_form (const void *cls,
+		 const char *mime,
+		 struct Session *session,
+		 struct MHD_Connection *connection)
+{
+  int ret;
+  char *reply;
+  struct MHD_Response *response;
+
+  reply = malloc (strlen (SECOND_PAGE) + strlen (session->value_1) + strlen (session->value_2) + 1);
+  if (NULL == reply)
+    return MHD_NO;
+  snprintf (reply,
+	    strlen (SECOND_PAGE) + strlen (session->value_1) + strlen (session->value_2) + 1,
+	    SECOND_PAGE,
+	    session->value_1,
+            session->value_2);
+  /* return static form */
+  response = MHD_create_response_from_buffer (strlen (reply),
+					      (void *) reply,
+					      MHD_RESPMEM_MUST_FREE);
+  if (NULL == response)
+    return MHD_NO;
+  add_session_cookie (session, response);
+  MHD_add_response_header (response,
+			   MHD_HTTP_HEADER_CONTENT_ENCODING,
+			   mime);
+  ret = MHD_queue_response (connection,
+			    MHD_HTTP_OK,
+			    response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+/**
+ * Handler used to generate a 404 reply.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ */
+static int
+not_found_page (const void *cls,
+		const char *mime,
+		struct Session *session,
+		struct MHD_Connection *connection)
+{
+  int ret;
+  struct MHD_Response *response;
+
+  /* unsupported HTTP method */
+  response = MHD_create_response_from_buffer (strlen (NOT_FOUND_ERROR),
+					      (void *) NOT_FOUND_ERROR,
+					      MHD_RESPMEM_PERSISTENT);
+  if (NULL == response)
+    return MHD_NO;
+  ret = MHD_queue_response (connection,
+			    MHD_HTTP_NOT_FOUND,
+			    response);
+  MHD_add_response_header (response,
+			   MHD_HTTP_HEADER_CONTENT_ENCODING,
+			   mime);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+/**
+ * List of all pages served by this HTTP server.
+ */
+static struct Page pages[] =
+  {
+    { "/", "text/html",  &fill_v1_form, NULL },
+    { "/2", "text/html", &fill_v1_v2_form, NULL },
+    { "/S", "text/html", &serve_simple_form, SUBMIT_PAGE },
+    { "/F", "text/html", &serve_simple_form, LAST_PAGE },
+    { NULL, NULL, &not_found_page, NULL } /* 404 */
+  };
+
+
+
+/**
+ * Iterator over key-value pairs where the value
+ * maybe made available in increments and/or may
+ * not be zero-terminated.  Used for processing
+ * POST data.
+ *
+ * @param cls user-specified closure
+ * @param kind type of the value
+ * @param key 0-terminated key for the value
+ * @param filename name of the uploaded file, NULL if not known
+ * @param content_type mime-type of the data, NULL if not known
+ * @param transfer_encoding encoding of the data, NULL if not known
+ * @param data pointer to size bytes of data at the
+ *              specified offset
+ * @param off offset of data in the overall value
+ * @param size number of bytes in data available
+ * @return MHD_YES to continue iterating,
+ *         MHD_NO to abort the iteration
+ */
+static int
+post_iterator (void *cls,
+	       enum MHD_ValueKind kind,
+	       const char *key,
+	       const char *filename,
+	       const char *content_type,
+	       const char *transfer_encoding,
+	       const char *data, uint64_t off, size_t size)
+{
+  struct Request *request = cls;
+  struct Session *session = request->session;
+
+  if (0 == strcmp ("DONE", key))
+    {
+      fprintf (stdout,
+	       "Session `%s' submitted `%s', `%s'\n",
+	       session->sid,
+	       session->value_1,
+	       session->value_2);
+      return MHD_YES;
+    }
+  if (0 == strcmp ("v1", key))
+    {
+      if (size + off >= sizeof(session->value_1))
+	size = sizeof (session->value_1) - off - 1;
+      memcpy (&session->value_1[off],
+	      data,
+	      size);
+      session->value_1[size+off] = '\0';
+      return MHD_YES;
+    }
+  if (0 == strcmp ("v2", key))
+    {
+      if (size + off >= sizeof(session->value_2))
+	size = sizeof (session->value_2) - off - 1;
+      memcpy (&session->value_2[off],
+	      data,
+	      size);
+      session->value_2[size+off] = '\0';
+      return MHD_YES;
+    }
+  fprintf (stderr,
+           "Unsupported form value `%s'\n",
+           key);
+  return MHD_YES;
+}
+
+
+/**
+ * Main MHD callback for handling requests.
+ *
+ * @param cls argument given together with the function
+ *        pointer when the handler was registered with MHD
+ * @param connection handle identifying the incoming connection
+ * @param url the requested url
+ * @param method the HTTP method used ("GET", "PUT", etc.)
+ * @param version the HTTP version string (i.e. "HTTP/1.1")
+ * @param upload_data the data being uploaded (excluding HEADERS,
+ *        for a POST that fits into memory and that is encoded
+ *        with a supported encoding, the POST data will NOT be
+ *        given in upload_data and is instead available as
+ *        part of MHD_get_connection_values; very large POST
+ *        data *will* be made available incrementally in
+ *        upload_data)
+ * @param upload_data_size set initially to the size of the
+ *        upload_data provided; the method must update this
+ *        value to the number of bytes NOT processed;
+ * @param ptr pointer that the callback can set to some
+ *        address and that will be preserved by MHD for future
+ *        calls for this request; since the access handler may
+ *        be called many times (i.e., for a PUT/POST operation
+ *        with plenty of upload data) this allows the application
+ *        to easily associate some request-specific state.
+ *        If necessary, this state can be cleaned up in the
+ *        global "MHD_RequestCompleted" callback (which
+ *        can be set with the MHD_OPTION_NOTIFY_COMPLETED).
+ *        Initially, <tt>*con_cls</tt> will be NULL.
+ * @return MHS_YES if the connection was handled successfully,
+ *         MHS_NO if the socket must be closed due to a serios
+ *         error while handling the request
+ */
+static int
+create_response (void *cls,
+		 struct MHD_Connection *connection,
+		 const char *url,
+		 const char *method,
+		 const char *version,
+		 const char *upload_data,
+		 size_t *upload_data_size,
+		 void **ptr)
+{
+  struct MHD_Response *response;
+  struct Request *request;
+  struct Session *session;
+  int ret;
+  unsigned int i;
+
+  request = *ptr;
+  if (NULL == request)
+    {
+      request = calloc (1, sizeof (struct Request));
+      if (NULL == request)
+	{
+	  fprintf (stderr, "calloc error: %s\n", strerror (errno));
+	  return MHD_NO;
+	}
+      *ptr = request;
+      if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
+	{
+	  request->pp = MHD_create_post_processor (connection, 1024,
+						   &post_iterator, request);
+	  if (NULL == request->pp)
+	    {
+	      fprintf (stderr, "Failed to setup post processor for `%s'\n",
+		       url);
+	      return MHD_NO; /* internal error */
+	    }
+	}
+      return MHD_YES;
+    }
+  if (NULL == request->session)
+    {
+      request->session = get_session (connection);
+      if (NULL == request->session)
+	{
+	  fprintf (stderr, "Failed to setup session for `%s'\n",
+		   url);
+	  return MHD_NO; /* internal error */
+	}
+    }
+  session = request->session;
+  session->start = time (NULL);
+  if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
+    {
+      /* evaluate POST data */
+      MHD_post_process (request->pp,
+			upload_data,
+			*upload_data_size);
+      if (0 != *upload_data_size)
+	{
+	  *upload_data_size = 0;
+	  return MHD_YES;
+	}
+      /* done with POST data, serve response */
+      MHD_destroy_post_processor (request->pp);
+      request->pp = NULL;
+      method = MHD_HTTP_METHOD_GET; /* fake 'GET' */
+      if (NULL != request->post_url)
+	url = request->post_url;
+    }
+
+  if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
+       (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
+    {
+      /* find out which page to serve */
+      i=0;
+      while ( (pages[i].url != NULL) &&
+	      (0 != strcmp (pages[i].url, url)) )
+	i++;
+      ret = pages[i].handler (pages[i].handler_cls,
+			      pages[i].mime,
+			      session, connection);
+      if (ret != MHD_YES)
+	fprintf (stderr, "Failed to create page for `%s'\n",
+		 url);
+      return ret;
+    }
+  /* unsupported HTTP method */
+  response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
+					      (void *) METHOD_ERROR,
+					      MHD_RESPMEM_PERSISTENT);
+  ret = MHD_queue_response (connection,
+			    MHD_HTTP_METHOD_NOT_ACCEPTABLE,
+			    response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+/**
+ * Callback called upon completion of a request.
+ * Decrements session reference counter.
+ *
+ * @param cls not used
+ * @param connection connection that completed
+ * @param con_cls session handle
+ * @param toe status code
+ */
+static void
+request_completed_callback (void *cls,
+			    struct MHD_Connection *connection,
+			    void **con_cls,
+			    enum MHD_RequestTerminationCode toe)
+{
+  struct Request *request = *con_cls;
+
+  if (NULL == request)
+    return;
+  if (NULL != request->session)
+    request->session->rc--;
+  if (NULL != request->pp)
+    MHD_destroy_post_processor (request->pp);
+  free (request);
+}
+
+
+/**
+ * Clean up handles of sessions that have been idle for
+ * too long.
+ */
+static void
+expire_sessions ()
+{
+  struct Session *pos;
+  struct Session *prev;
+  struct Session *next;
+  time_t now;
+
+  now = time (NULL);
+  prev = NULL;
+  pos = sessions;
+  while (NULL != pos)
+    {
+      next = pos->next;
+      if (now - pos->start > 60 * 60)
+	{
+	  /* expire sessions after 1h */
+	  if (NULL == prev)
+	    sessions = pos->next;
+	  else
+	    prev->next = next;
+	  free (pos);
+	}
+      else
+        prev = pos;
+      pos = next;
+    }
+}
+
+
+/**
+ * Call with the port number as the only argument.
+ * Never terminates (other than by signals, such as CTRL-C).
+ */
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+  struct timeval tv;
+  struct timeval *tvp;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  MHD_UNSIGNED_LONG_LONG mhd_timeout;
+
+  if (argc != 2)
+    {
+      printf ("%s PORT\n", argv[0]);
+      return 1;
+    }
+  /* initialize PRNG */
+  srand ((unsigned int) time (NULL));
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        atoi (argv[1]),
+                        NULL, NULL,
+			&create_response, NULL,
+			MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 15,
+			MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL,
+			MHD_OPTION_END);
+  if (NULL == d)
+    return 1;
+  while (1)
+    {
+      expire_sessions ();
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+	break; /* fatal internal error */
+      if (MHD_get_timeout (d, &mhd_timeout) == MHD_YES)
+	{
+	  tv.tv_sec = mhd_timeout / 1000;
+	  tv.tv_usec = (mhd_timeout - (tv.tv_sec * 1000)) * 1000;
+	  tvp = &tv;
+	}
+      else
+	tvp = NULL;
+      select (max + 1, &rs, &ws, &es, tvp);
+      MHD_run (d);
+    }
+  MHD_stop_daemon (d);
+  return 0;
+}
+
diff --git a/src/examples/querystring_example.c b/src/examples/querystring_example.c
new file mode 100644
index 0000000..24f8ae4
--- /dev/null
+++ b/src/examples/querystring_example.c
@@ -0,0 +1,90 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+/**
+ * @file querystring_example.c
+ * @brief example for how to get the query string from libmicrohttpd
+ *        Call with an URI ending with something like "?q=QUERY"
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+
+#define PAGE "<html><head><title>libmicrohttpd demo</title></head><body>Query string for &quot;%s&quot; was &quot;%s&quot;</body></html>"
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size, void **ptr)
+{
+  static int aptr;
+  const char *fmt = cls;
+  const char *val;
+  char *me;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp (method, "GET"))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+    {
+      /* do never respond on first call */
+      *ptr = &aptr;
+      return MHD_YES;
+    }
+  *ptr = NULL;                  /* reset when done */
+  val = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "q");
+  me = malloc (snprintf (NULL, 0, fmt, "q", val) + 1);
+  if (me == NULL)
+    return MHD_NO;
+  sprintf (me, fmt, "q", val);
+  response = MHD_create_response_from_buffer (strlen (me), me,
+					      MHD_RESPMEM_MUST_FREE);
+  if (response == NULL)
+    {
+      free (me);
+      return MHD_NO;
+    }
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+
+  if (argc != 2)
+    {
+      printf ("%s PORT\n", argv[0]);
+      return 1;
+    }
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        atoi (argv[1]),
+                        NULL, NULL, &ahc_echo, PAGE, MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  return 0;
+}
diff --git a/src/examples/refuse_post_example.c b/src/examples/refuse_post_example.c
new file mode 100644
index 0000000..846546c
--- /dev/null
+++ b/src/examples/refuse_post_example.c
@@ -0,0 +1,100 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+/**
+ * @file refuse_post_example.c
+ * @brief example for how to refuse a POST request properly
+ * @author Christian Grothoff and Sebastian Gerhardt
+ */
+#include "platform.h"
+#include <microhttpd.h>
+
+const char *askpage = "<html><body>\n\
+                       Upload a file, please!<br>\n\
+                       <form action=\"/filepost\" method=\"post\" enctype=\"multipart/form-data\">\n\
+                       <input name=\"file\" type=\"file\">\n\
+                       <input type=\"submit\" value=\" Send \"></form>\n\
+                       </body></html>";
+
+#define BUSYPAGE "<html><head><title>Webserver busy</title></head><body>We are too busy to process POSTs right now.</body></html>"
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size, void **ptr)
+{
+  static int aptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if ((0 != strcmp (method, "GET")) && (0 != strcmp (method, "POST")))
+    return MHD_NO;              /* unexpected method */
+
+  if (&aptr != *ptr)
+    {
+      *ptr = &aptr;
+
+      /* always to busy for POST requests */
+      if (0 == strcmp (method, "POST"))
+        {
+          response = MHD_create_response_from_buffer (strlen (BUSYPAGE),
+						      (void *) BUSYPAGE, 
+						      MHD_RESPMEM_PERSISTENT);
+          ret =
+            MHD_queue_response (connection, MHD_HTTP_SERVICE_UNAVAILABLE,
+                                response);
+          MHD_destroy_response (response);
+          return ret;
+        }
+    }
+
+  *ptr = NULL;                  /* reset when done */
+  response = MHD_create_response_from_buffer (strlen (me),
+					      (void *) me,
+					      MHD_RESPMEM_PERSISTENT);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+
+  if (argc != 2)
+    {
+      printf ("%s PORT\n", argv[0]);
+      return 1;
+    }
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        atoi (argv[1]),
+                        NULL, NULL, &ahc_echo, (void *) askpage,
+                        MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+/* end of refuse_post_example.c */
diff --git a/src/examples/spdy_event_loop.c b/src/examples/spdy_event_loop.c
new file mode 100644
index 0000000..6b7192c
--- /dev/null
+++ b/src/examples/spdy_event_loop.c
@@ -0,0 +1,487 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file event_loop.c
+ * @brief  shows how to use the daemon. THIS IS MAINLY A TEST AND DEBUG
+ * 		 PROGRAM
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include "microspdy.h"
+#include <sys/time.h>
+#include <time.h>
+#ifndef MINGW
+#include <arpa/inet.h>
+#endif
+//#include "../framinglayer/structures.h"
+//#include "../applicationlayer/alstructures.h"
+
+static int run = 1;
+
+static int run2 = 1;
+
+	
+static uint64_t loops;
+
+static time_t start;
+
+
+static void
+new_session_callback (void *cls,
+						struct SPDY_Session * session)
+{
+  (void)cls;
+  
+	char ipstr[1024];
+		
+	struct sockaddr *addr;
+	socklen_t addr_len = SPDY_get_remote_addr(session, &addr);	
+	
+	if(!addr_len)
+	{
+		printf("SPDY_get_remote_addr");
+		abort();
+	}
+	
+	if(AF_INET == addr->sa_family)
+	{
+		struct sockaddr_in * addr4 = (struct sockaddr_in *) addr;
+		if(NULL == inet_ntop(AF_INET, &(addr4->sin_addr), ipstr, sizeof(ipstr)))
+		{
+			printf("inet_ntop");
+			abort();
+		}
+		printf("New connection from: %s:%i\n", ipstr, ntohs(addr4->sin_port));
+		
+	}
+	else if(AF_INET6 == addr->sa_family)
+	{
+		struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *) addr;
+		if(NULL == inet_ntop(AF_INET6, &(addr6->sin6_addr), ipstr, sizeof(ipstr)))
+		{
+			printf("inet_ntop");
+			abort();
+		}
+		printf("New connection from: %s:%i\n", ipstr, ntohs(addr6->sin6_port));
+		
+	}
+}
+
+
+static void
+session_closed_handler (void *cls,
+						struct SPDY_Session * session,
+						int by_client)
+{
+  (void)cls;
+  (void)session;
+  
+	//printf("session_closed_handler called\n");
+	
+	if(SPDY_YES != by_client)
+	{
+		//killchild(child,"wrong by_client");
+		printf("session closed by server\n");
+	}
+	else
+	{
+		printf("session closed by client\n");
+	}
+	
+	//session_closed_called = 1;
+}
+
+
+static void
+response_done_callback(void *cls,
+						struct SPDY_Response *response,
+						struct SPDY_Request *request,
+						enum SPDY_RESPONSE_RESULT status,
+						bool streamopened)
+{
+	(void)streamopened;
+	if(strcmp(cls, "/close (daemon1)") == 0)
+		run = 0;
+	else {
+		if(strcmp(cls, "/close (daemon2)") == 0) run2 = 0;
+		loops = 0;
+		start = time(NULL);
+	}
+	if(SPDY_RESPONSE_RESULT_SUCCESS != status)
+	{
+		printf("not sent frame cause %i", status);
+	}
+	printf("answer for %s was sent\n", (char*)cls);
+	//printf("raw sent headers %s\n", (char *)(response->headers)+8);
+	
+	SPDY_destroy_request(request);
+	SPDY_destroy_response(response);
+	free(cls);
+}
+
+/*
+static int
+print_headers (void *cls,
+                           const char *name, const char *value)
+{
+	(void)cls;
+	printf("%s: %s\n",name,value);
+	return SPDY_YES;
+}
+ */
+ 
+ 
+/*       
+void
+new_request_cb (void *cls,
+						struct SPDY_Request * request,
+						uint8_t priority,
+                        const char *method,
+                        const char *path,
+                        const char *version,
+                        const char *host,
+                        const char *scheme,
+						struct SPDY_NameValue * headers)
+{
+	(void)cls;
+	(void)request;
+	printf("Priority: %i\nHTTP headers, scheme: %s\n\n%s %s %s\nHost: %s\n", priority,scheme,method,path,version,host);
+	SPDY_name_value_iterate(headers, &print_headers, NULL);
+}
+*/
+
+
+static int
+append_headers_to_data (void *cls,
+                           const char *name, const char * const *value, int num_values)
+{
+	char **data = cls;
+	void *tofree = *data;
+	int i;
+	
+	if(num_values)
+	for(i=0;i<num_values;++i)
+	{
+	asprintf(data,"%s%s: %s\n", *data,name,value[i]);
+	}
+	else
+	asprintf(data,"%s%s: \n", *data,name);
+	
+	free(tofree);
+	return SPDY_YES;
+}  
+
+
+static void
+standard_request_handler(void *cls,
+						struct SPDY_Request * request,
+						uint8_t priority,
+                        const char *method,
+                        const char *path,
+                        const char *version,
+                        const char *host,
+                        const char *scheme,
+						struct SPDY_NameValue * headers,
+            bool more)
+{
+  (void)more;
+  
+	char *html;
+	char *data;
+	struct SPDY_Response *response=NULL;
+	
+	printf("received request for '%s %s %s'\n", method, path, version);
+	if(strcmp(path,"/main.css")==0)
+	{
+		if(NULL != cls)
+			asprintf(&html,"body{color:green;}");
+		else
+			asprintf(&html,"body{color:red;}");
+				
+		//struct SPDY_NameValue *headers=SPDY_name_value_create();
+		//SPDY_name_value_add(headers,"content-type","text/css");
+		
+		response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,html,strlen(html));
+		free(html);
+	}
+	else
+	{
+		asprintf(&data,"Priority: %i\nHTTP headers, scheme: %s\n\n%s %s %s\nHost: %s\n", priority,scheme,method,path,version,host);
+		
+		SPDY_name_value_iterate(headers, &append_headers_to_data, &data);
+		
+		if(strcmp(path,"/close")==0)
+		{
+			asprintf(&html,"<html>"
+		"<body><b>Closing now!<br>This is an answer to the following "
+		"request:</b><br><br><pre>%s</pre></body></html>",data);
+		}
+		else
+		{
+			asprintf(&html,"<html><link href=\"main.css\" rel=\"stylesheet\" type=\"text/css\" />"
+		"<body><b>This is an answer to the following "
+		"request:</b><br><br><pre>%s</pre></body></html>",data);
+		}
+		
+		free(data);
+		
+		response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,html,strlen(html));
+		free(html);
+	}
+	
+	if(NULL==response){
+		fprintf(stdout,"no response obj\n");
+		abort();
+	}
+	
+	char *pathcls;
+	asprintf(&pathcls, "%s (daemon%i)",path,NULL==cls ? 1 : 2);
+	if(SPDY_queue_response(request,response,true,false,&response_done_callback,pathcls)!=SPDY_YES)
+	{
+		fprintf(stdout,"queue\n");
+		abort();
+	}
+}
+
+
+static int
+new_post_data_cb (void * cls,
+					 struct SPDY_Request *request,
+					 const void * buf,
+					 size_t size,
+					 bool more)
+{
+  (void)cls;
+  (void)request;
+  (void)more;
+  
+	printf("DATA:\n===============================\n");
+  write(0, buf, size);
+	printf("\n===============================\n");
+  return SPDY_YES;
+}
+
+
+static void 
+sig_handler(int signo)
+{
+  (void)signo;
+  
+  printf("received signal\n");
+}
+
+
+int
+main (int argc, char *const *argv)
+{	
+	if(argc != 2) return 1;
+	
+	#ifndef MINGW
+	if (signal(SIGPIPE, sig_handler) == SIG_ERR)
+		printf("\ncan't catch SIGPIPE\n");
+	#endif
+	
+	SPDY_init();
+	
+	/*
+  struct sockaddr_in addr4;
+	struct in_addr inaddr4;
+	inaddr4.s_addr = htonl(INADDR_ANY);
+	addr4.sin_family = AF_INET;
+	addr4.sin_addr = inaddr4;
+	addr4.sin_port = htons(atoi(argv[1]));
+	*/
+  
+	struct SPDY_Daemon *daemon = SPDY_start_daemon(atoi(argv[1]),
+	 DATA_DIR "cert-and-key.pem",
+	 DATA_DIR "cert-and-key.pem",
+	&new_session_callback,&session_closed_handler,&standard_request_handler,&new_post_data_cb,NULL,
+	SPDY_DAEMON_OPTION_SESSION_TIMEOUT, 10,
+	//SPDY_DAEMON_OPTION_SOCK_ADDR,  (struct sockaddr *)&addr4,
+	SPDY_DAEMON_OPTION_END);
+	
+	if(NULL==daemon){
+		printf("no daemon\n");
+		return 1;
+	}
+	
+  /*
+	struct sockaddr_in6 addr6;
+	addr6.sin6_family = AF_INET6;
+	addr6.sin6_addr = in6addr_any;
+	addr6.sin6_port = htons(atoi(argv[1]) + 1);
+	*/
+  
+	struct SPDY_Daemon *daemon2 = SPDY_start_daemon(atoi(argv[1]) + 1,
+	 DATA_DIR "cert-and-key.pem",
+	 DATA_DIR "cert-and-key.pem",
+	&new_session_callback,NULL,&standard_request_handler,&new_post_data_cb,&main,
+	//SPDY_DAEMON_OPTION_SESSION_TIMEOUT, 0,
+	//SPDY_DAEMON_OPTION_SOCK_ADDR,  (struct sockaddr *)&addr6,
+	//SPDY_DAEMON_OPTION_FLAGS, SPDY_DAEMON_FLAG_ONLY_IPV6,
+	SPDY_DAEMON_OPTION_END);
+	
+	if(NULL==daemon2){
+		printf("no daemon\n");
+		return 1;
+	}
+	
+	do
+	{
+	unsigned long long timeoutlong=0;
+	struct timeval timeout;
+	volatile int rc; /* select() return code */ 
+	volatile int ret;
+
+	fd_set read_fd_set;
+	fd_set write_fd_set;
+	fd_set except_fd_set;
+	int maxfd = -1;
+
+	if(run && daemon != NULL)
+	{
+		loops++;
+		FD_ZERO(&read_fd_set);
+		FD_ZERO(&write_fd_set);
+		FD_ZERO(&except_fd_set);
+
+		ret = SPDY_get_timeout(daemon, &timeoutlong);
+		if(SPDY_NO == ret || timeoutlong > 1000)
+		{
+			timeout.tv_sec = 1;
+      timeout.tv_usec = 0;
+		}
+		else
+		{
+			timeout.tv_sec = timeoutlong / 1000;
+			timeout.tv_usec = (timeoutlong % 1000) * 1000;
+		}
+    
+		printf("ret=%i; timeoutlong=%llu; sec=%llu; usec=%llu\n", ret, timeoutlong, (long long unsigned)timeout.tv_sec, (long long unsigned)timeout.tv_usec);
+		//raise(SIGINT);
+
+		/* get file descriptors from the transfers */ 
+		maxfd = SPDY_get_fdset (daemon,
+		&read_fd_set,
+		&write_fd_set, 
+		&except_fd_set);
+
+//struct timeval ts1,ts2;
+    //gettimeofday(&ts1, NULL);
+		rc = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
+    //gettimeofday(&ts2, NULL);
+    printf("rc %i\n",rc);
+   // printf("time for select %i\n",ts2.tv_usec - ts1.tv_usec);
+   // printf("%i %i %i %i\n",ts1.tv_sec, ts1.tv_usec,ts2.tv_sec, ts2.tv_usec);
+
+		switch(rc) {
+			case -1:
+				/* select error */ 
+				break;
+			case 0:
+
+				break;
+			default:
+				SPDY_run(daemon);
+
+			break;
+		}
+	}
+	else if(daemon != NULL){
+	
+	printf("%lu loops in %llu secs\n", loops, (long long unsigned)(time(NULL) - start));
+		SPDY_stop_daemon(daemon);
+		daemon=NULL;
+	}
+
+	if(run2)
+	{
+		FD_ZERO(&read_fd_set);
+		FD_ZERO(&write_fd_set);
+		FD_ZERO(&except_fd_set);
+
+		ret = SPDY_get_timeout(daemon2, &timeoutlong);
+		//printf("tout %i\n",timeoutlong);
+		if(SPDY_NO == ret || timeoutlong > 1)
+		{ 
+			//do sth else
+			//sleep(1);
+
+			//try new connection
+			timeout.tv_sec = 1;
+			timeout.tv_usec = 0;
+		}
+		else
+		{
+			timeout.tv_sec = timeoutlong;
+			timeout.tv_usec = 0;//(timeoutlong % 1000) * 1000;
+		}
+
+		//printf("ret=%i; timeoutlong=%i; sec=%i; usec=%i\n", ret, timeoutlong, timeout.tv_sec, timeout.tv_usec);
+		//raise(SIGINT);
+
+		/* get file descriptors from the transfers */ 
+		maxfd = SPDY_get_fdset (daemon2,
+		&read_fd_set,
+		&write_fd_set, 
+		&except_fd_set);
+
+		rc = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
+
+		switch(rc) {
+			case -1:
+				/* select error */ 
+				break;
+			case 0:
+
+				break;
+			default:
+				SPDY_run(daemon2);
+
+				break;
+		}
+	}
+	else if(daemon2 != NULL){
+		SPDY_stop_daemon(daemon2);
+		daemon2=NULL;
+	}
+	}
+	while(run || run2);
+
+	if(daemon != NULL){
+		SPDY_stop_daemon(daemon);
+	}
+	if(daemon2 != NULL){
+		SPDY_stop_daemon(daemon2);
+	}
+	
+	SPDY_deinit();
+	
+	return 0;
+}
+
diff --git a/src/examples/spdy_fileserver.c b/src/examples/spdy_fileserver.c
new file mode 100644
index 0000000..0a0254f
--- /dev/null
+++ b/src/examples/spdy_fileserver.c
@@ -0,0 +1,353 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file fileserver.c
+ * @brief   Simple example how the lib can be used for serving
+ * 			files directly read from the system
+ * @author Andrey Uzunov
+ */
+
+//for asprintf
+#define _GNU_SOURCE
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include "microspdy.h"
+#include "time.h"
+
+
+int run = 1;
+char* basedir;
+
+
+#define GET_MIME_TYPE(fname, mime)	do {\
+		unsigned int __len = strlen(fname);\
+		if (__len < 4 || '.' != (fname)[__len - 4]) \
+		{	\
+			(mime) = strdup("application/octet-stream");\
+			printf("MIME for %s is applic...\n", (fname));\
+		}\
+    else {\
+      const char * __ext = &(fname)[__len - 3];\
+      if(0 == strcmp(__ext, "jpg")) (mime) = strdup("image/jpeg");\
+      else if(0 == strcmp(__ext, "png")) (mime) = strdup("image/png");\
+      else if(0 == strcmp(__ext, "css")) (mime) = strdup("text/css");\
+      else if(0 == strcmp(__ext, "gif")) (mime) = strdup("image/gif");\
+      else if(0 == strcmp(__ext, "htm")) (mime) = strdup("text/html");\
+      else \
+      {	\
+        (mime) = strdup("application/octet-stream");\
+        printf("MIME for %s is applic...\n", (fname));\
+      }\
+    }\
+		if(NULL == (mime))\
+		{\
+			printf("no memory\n");\
+			abort();\
+		}\
+	} while (0)
+
+
+static const char *DAY_NAMES[] =
+  { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+
+static const char *MONTH_NAMES[] =
+  { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+//taken from http://stackoverflow.com/questions/2726975/how-can-i-generate-an-rfc1123-date-string-from-c-code-win32
+//and modified for linux
+char *Rfc1123_DateTimeNow()
+{
+    const int RFC1123_TIME_LEN = 29;
+    time_t t;
+    struct tm tm;
+    char * buf = malloc(RFC1123_TIME_LEN+1);
+
+    if (NULL == buf)
+      return NULL;
+    time(&t);
+    gmtime_r( &t, &tm);
+
+    strftime(buf, RFC1123_TIME_LEN+1, "---, %d --- %Y %H:%M:%S GMT", &tm);
+    memcpy(buf, DAY_NAMES[tm.tm_wday], 3);
+    memcpy(buf+8, MONTH_NAMES[tm.tm_mon], 3);
+
+    return buf;
+}
+
+
+ssize_t
+response_callback (void *cls,
+						void *buffer,
+						size_t max,
+						bool *more)
+{
+	FILE *fd =(FILE*)cls;
+
+	int ret = fread(buffer,1,max,fd);
+	*more = feof(fd) == 0;
+
+	//if(!(*more))
+	//	fclose(fd);
+
+	return ret;
+}
+
+
+void
+response_done_callback(void *cls,
+						struct SPDY_Response *response,
+						struct SPDY_Request *request,
+						enum SPDY_RESPONSE_RESULT status,
+						bool streamopened)
+{
+	(void)streamopened;
+	(void)status;
+	//printf("answer for %s was sent\n", (char *)cls);
+
+	/*if(SPDY_RESPONSE_RESULT_SUCCESS != status)
+	{
+		printf("answer for %s was NOT sent, %i\n", (char *)cls,status);
+	}*/
+
+	SPDY_destroy_request(request);
+	SPDY_destroy_response(response);
+	if(NULL!=cls)fclose(cls);
+}
+
+void
+standard_request_handler(void *cls,
+						struct SPDY_Request * request,
+						uint8_t priority,
+                        const char *method,
+                        const char *path,
+                        const char *version,
+                        const char *host,
+                        const char *scheme,
+						struct SPDY_NameValue * headers,
+            bool more)
+{
+	(void)cls;
+	(void)request;
+	(void)priority;
+	(void)host;
+	(void)scheme;
+	(void)headers;
+	(void)method;
+	(void)version;
+	(void)more;
+
+	struct SPDY_Response *response=NULL;
+	struct SPDY_NameValue *resp_headers;
+	char *fname;
+	char *fsize;
+	char *mime=NULL;
+	char *date=NULL;
+	ssize_t filesize = -666;
+	FILE *fd = NULL;
+	int ret = -666;
+
+	//printf("received request for '%s %s %s'\n", method, path, version);
+	if(strlen(path) > 1 && NULL == strstr(path, "..") && '/' == path[0])
+	{
+		asprintf(&fname,"%s%s",basedir,path);
+		if(0 == access(fname, R_OK))
+		{
+			if(NULL == (fd = fopen(fname,"r"))
+				|| 0 != (ret = fseek(fd, 0L, SEEK_END))
+				|| -1 == (filesize = ftell(fd))
+				|| 0 != (ret = fseek(fd, 0L, SEEK_SET)))
+			{
+				printf("Error on opening %s\n%p %i %zd\n",fname, fd, ret, filesize);
+				response = SPDY_build_response(SPDY_HTTP_INTERNAL_SERVER_ERROR,NULL,SPDY_HTTP_VERSION_1_1,NULL,NULL,0);
+			}
+			else
+			{
+				if(NULL == (resp_headers = SPDY_name_value_create()))
+				{
+					printf("SPDY_name_value_create failed\n");
+					abort();
+				}
+
+				date = Rfc1123_DateTimeNow();
+				if(NULL == date
+					|| SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_DATE,date))
+				{
+					printf("SPDY_name_value_add or Rfc1123_DateTimeNow failed\n");
+					abort();
+				}
+				free(date);
+
+				if(-1 == asprintf(&fsize, "%zd", filesize)
+					|| SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_LENGTH,fsize))
+				{
+					printf("SPDY_name_value_add or asprintf failed\n");
+					abort();
+				}
+				free(fsize);
+
+				GET_MIME_TYPE(path,mime);
+				if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_TYPE,mime))
+				{
+					printf("SPDY_name_value_add failed\n");
+					abort();
+				}
+				free(mime);
+
+				if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_SERVER,"libmicrospdy/fileserver"))
+				{
+					printf("SPDY_name_value_add failed\n");
+					abort();
+				}
+
+				response = SPDY_build_response_with_callback(200,NULL,
+					SPDY_HTTP_VERSION_1_1,resp_headers,&response_callback,fd,SPDY_MAX_SUPPORTED_FRAME_SIZE);
+				SPDY_name_value_destroy(resp_headers);
+			}
+
+			if(NULL==response){
+				printf("no response obj\n");
+				abort();
+			}
+
+			if(SPDY_queue_response(request,response,true,false,&response_done_callback,fd)!=SPDY_YES)
+			{
+				printf("queue\n");
+				abort();
+			}
+
+			free(fname);
+			return;
+		}
+		free(fname);
+	}
+
+	if(strcmp(path,"/close")==0)
+	{
+		run = 0;
+	}
+
+	response = SPDY_build_response(SPDY_HTTP_NOT_FOUND,NULL,SPDY_HTTP_VERSION_1_1,NULL,NULL,0);
+	printf("Not found %s\n",path);
+
+	if(NULL==response){
+		printf("no response obj\n");
+		abort();
+	}
+
+	if(SPDY_queue_response(request,response,true,false,&response_done_callback,NULL)!=SPDY_YES)
+	{
+		printf("queue\n");
+		abort();
+	}
+}
+
+int
+main (int argc, char *const *argv)
+{
+	unsigned long long timeoutlong=0;
+	struct timeval timeout;
+	int ret;
+	fd_set read_fd_set;
+	fd_set write_fd_set;
+	fd_set except_fd_set;
+	int maxfd = -1;
+	struct SPDY_Daemon *daemon;
+
+	if(argc != 5)
+	{
+		printf("Usage: %s cert-file key-file base-dir port\n", argv[0]);
+		return 1;
+	}
+
+	SPDY_init();
+
+	daemon = SPDY_start_daemon(atoi(argv[4]),
+								argv[1],
+								argv[2],
+								NULL,
+								NULL,
+								&standard_request_handler,
+								NULL,
+								NULL,
+								SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
+								1800,
+								SPDY_DAEMON_OPTION_END);
+
+	if(NULL==daemon){
+		printf("no daemon\n");
+		return 1;
+	}
+
+	basedir = argv[3];
+
+	do
+	{
+		FD_ZERO(&read_fd_set);
+		FD_ZERO(&write_fd_set);
+		FD_ZERO(&except_fd_set);
+
+		ret = SPDY_get_timeout(daemon, &timeoutlong);
+		if(SPDY_NO == ret || timeoutlong > 1000)
+		{
+			timeout.tv_sec = 1;
+      timeout.tv_usec = 0;
+		}
+		else
+		{
+			timeout.tv_sec = timeoutlong / 1000;
+			timeout.tv_usec = (timeoutlong % 1000) * 1000;
+		}
+
+		maxfd = SPDY_get_fdset (daemon,
+								&read_fd_set,
+								&write_fd_set,
+								&except_fd_set);
+
+		ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
+
+		switch(ret) {
+			case -1:
+				printf("select error: %i\n", errno);
+				break;
+			case 0:
+
+				break;
+			default:
+				SPDY_run(daemon);
+
+			break;
+		}
+	}
+	while(run);
+
+	SPDY_stop_daemon(daemon);
+
+	SPDY_deinit();
+
+	return 0;
+}
+
diff --git a/src/examples/spdy_response_with_callback.c b/src/examples/spdy_response_with_callback.c
new file mode 100644
index 0000000..5bd452d
--- /dev/null
+++ b/src/examples/spdy_response_with_callback.c
@@ -0,0 +1,236 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file response_with_callback.c
+ * @brief  shows how to create responses with callbacks
+ * @author Andrey Uzunov
+ */
+ 
+//for asprintf
+#define _GNU_SOURCE 
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include "microspdy.h"
+
+static int run = 1;
+
+
+static ssize_t
+response_callback (void *cls,
+						void *buffer,
+						size_t max,
+						bool *more)
+{
+	FILE *fd =(FILE*)cls;
+	
+	int ret = fread(buffer,1,max,fd);
+	*more = feof(fd) == 0;
+	
+	if(!(*more))
+		fclose(fd);
+	
+	return ret;
+}
+
+
+static void
+response_done_callback(void *cls,
+		       struct SPDY_Response *response,
+		       struct SPDY_Request *request,
+		       enum SPDY_RESPONSE_RESULT status,
+		       bool streamopened)
+{
+	(void)streamopened;
+	(void)status;
+  
+	printf("answer for %s was sent\n", (char *)cls);
+	
+	SPDY_destroy_request(request);
+	SPDY_destroy_response(response);
+	free(cls);
+}
+
+
+static void
+standard_request_handler(void *cls,
+						struct SPDY_Request * request,
+						uint8_t priority,
+                        const char *method,
+                        const char *path,
+                        const char *version,
+                        const char *host,
+                        const char *scheme,
+						struct SPDY_NameValue * headers,
+            bool more)
+{
+	(void)cls;
+	(void)request;
+	(void)priority;
+	(void)host;
+	(void)scheme;
+	(void)headers;
+	(void)more;
+	
+	char *html;
+	struct SPDY_Response *response=NULL;
+	struct SPDY_NameValue *resp_headers;
+	
+	printf("received request for '%s %s %s'\n", method, path, version);
+	if(strcmp(path,"/spdy-draft.txt")==0)
+	{
+		FILE *fd = fopen(DATA_DIR "spdy-draft.txt","r");
+		
+		if(NULL == (resp_headers = SPDY_name_value_create()))
+		{
+			fprintf(stdout,"SPDY_name_value_create failed\n");
+			abort();
+		}
+		if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_TYPE,"text/plain"))
+		{
+			fprintf(stdout,"SPDY_name_value_add failed\n");
+			abort();
+		}
+		
+		response = SPDY_build_response_with_callback(200,NULL,
+			SPDY_HTTP_VERSION_1_1,resp_headers,&response_callback,fd,SPDY_MAX_SUPPORTED_FRAME_SIZE);
+		SPDY_name_value_destroy(resp_headers);
+	}
+	else
+	{
+		if(strcmp(path,"/close")==0)
+		{
+			asprintf(&html,"<html>"
+		"<body><b>Closing now!</body></html>");
+			run = 0;
+		}
+		else
+		{
+			asprintf(&html,"<html>"
+		"<body><a href=\"/spdy-draft.txt\">/spdy-draft.txt</a><br></body></html>");
+		}
+		
+		response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,html,strlen(html));
+		free(html);
+	}
+	
+	if(NULL==response){
+		fprintf(stdout,"no response obj\n");
+		abort();
+	}
+	
+	void *clspath = strdup(path);
+	
+	if(SPDY_queue_response(request,response,true,false,&response_done_callback,clspath)!=SPDY_YES)
+	{
+		fprintf(stdout,"queue\n");
+		abort();
+	}
+}
+
+
+int
+main (int argc, char *const *argv)
+{	
+	unsigned long long timeoutlong=0;
+	struct timeval timeout;
+	int ret;
+	fd_set read_fd_set;
+	fd_set write_fd_set;
+	fd_set except_fd_set;
+	int maxfd = -1;
+	struct SPDY_Daemon *daemon;
+	
+	if(argc != 2)
+	{
+		return 1;
+	}
+
+	SPDY_init();
+	
+	daemon = SPDY_start_daemon(atoi(argv[1]),
+								DATA_DIR "cert-and-key.pem",
+								DATA_DIR "cert-and-key.pem",
+								NULL,
+								NULL,
+								&standard_request_handler,
+								NULL,
+								NULL,
+								SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
+								1800,
+								SPDY_DAEMON_OPTION_END);
+	
+	if(NULL==daemon){
+		printf("no daemon\n");
+		return 1;
+	}
+	
+	do
+	{
+		FD_ZERO(&read_fd_set);
+		FD_ZERO(&write_fd_set);
+		FD_ZERO(&except_fd_set);
+
+		ret = SPDY_get_timeout(daemon, &timeoutlong);
+		if(SPDY_NO == ret || timeoutlong > 1000)
+		{
+			timeout.tv_sec = 1;
+      timeout.tv_usec = 0;
+		}
+		else
+		{
+			timeout.tv_sec = timeoutlong / 1000;
+			timeout.tv_usec = (timeoutlong % 1000) * 1000;
+		}
+		
+		maxfd = SPDY_get_fdset (daemon,
+								&read_fd_set,
+								&write_fd_set, 
+								&except_fd_set);
+								
+		ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
+		
+		switch(ret) {
+			case -1:
+				printf("select error: %i\n", errno);
+				break;
+			case 0:
+
+				break;
+			default:
+				SPDY_run(daemon);
+
+			break;
+		}
+	}
+	while(run);
+
+	SPDY_stop_daemon(daemon);
+	
+	SPDY_deinit();
+	
+	return 0;
+}
+
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
new file mode 100644
index 0000000..437008f
--- /dev/null
+++ b/src/include/Makefile.am
@@ -0,0 +1,10 @@
+# This Makefile.am is in the public domain
+SUBDIRS = .
+
+if ENABLE_SPDY
+microspdy = microspdy.h
+endif
+
+include_HEADERS = microhttpd.h $(microspdy)
+
+EXTRA_DIST = platform.h platform_interface.h w32functions.h autoinit_funcs.h
diff --git a/src/include/Makefile.in b/src/include/Makefile.in
new file mode 100644
index 0000000..151ac48
--- /dev/null
+++ b/src/include/Makefile.in
@@ -0,0 +1,723 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 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 = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+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 = src/include
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(am__include_HEADERS_DIST)
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_compile_flags.m4 \
+	$(top_srcdir)/m4/ax_append_flag.m4 \
+	$(top_srcdir)/m4/ax_check_compile_flag.m4 \
+	$(top_srcdir)/m4/ax_check_link_flag.m4 \
+	$(top_srcdir)/m4/ax_check_openssl.m4 \
+	$(top_srcdir)/m4/ax_count_cpus.m4 \
+	$(top_srcdir)/m4/ax_have_epoll.m4 \
+	$(top_srcdir)/m4/ax_pthread.m4 \
+	$(top_srcdir)/m4/ax_require_defined.m4 \
+	$(top_srcdir)/m4/libcurl.m4 $(top_srcdir)/m4/libgcrypt.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)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/MHD_config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-recursive dvi-recursive html-recursive info-recursive \
+	install-data-recursive install-dvi-recursive \
+	install-exec-recursive install-html-recursive \
+	install-info-recursive install-pdf-recursive \
+	install-ps-recursive install-recursive installcheck-recursive \
+	installdirs-recursive pdf-recursive ps-recursive \
+	tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__include_HEADERS_DIST = microhttpd.h microspdy.h
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(includedir)"
+HEADERS = $(include_HEADERS)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	distdir
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPU_COUNT = @CPU_COUNT@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_CPPFLAGS = @GNUTLS_CPPFLAGS@
+GNUTLS_LDFLAGS = @GNUTLS_LDFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+HAVE_CURL_BINARY = @HAVE_CURL_BINARY@
+HAVE_MAKEINFO_BINARY = @HAVE_MAKEINFO_BINARY@
+HIDDEN_VISIBILITY_CFLAGS = @HIDDEN_VISIBILITY_CFLAGS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSPDY_VERSION_AGE = @LIBSPDY_VERSION_AGE@
+LIBSPDY_VERSION_CURRENT = @LIBSPDY_VERSION_CURRENT@
+LIBSPDY_VERSION_REVISION = @LIBSPDY_VERSION_REVISION@
+LIBTOOL = @LIBTOOL@
+LIB_VERSION_AGE = @LIB_VERSION_AGE@
+LIB_VERSION_CURRENT = @LIB_VERSION_CURRENT@
+LIB_VERSION_REVISION = @LIB_VERSION_REVISION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MHD_LIBDEPS = @MHD_LIBDEPS@
+MHD_LIBDEPS_PKGCFG = @MHD_LIBDEPS_PKGCFG@
+MHD_LIB_CFLAGS = @MHD_LIB_CFLAGS@
+MHD_LIB_CPPFLAGS = @MHD_LIB_CPPFLAGS@
+MHD_LIB_LDFLAGS = @MHD_LIB_LDFLAGS@
+MHD_REQ_PRIVATE = @MHD_REQ_PRIVATE@
+MKDIR_P = @MKDIR_P@
+MS_LIB_TOOL = @MS_LIB_TOOL@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_INCLUDES = @OPENSSL_INCLUDES@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@
+PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@
+PACKAGE_VERSION_SUBMINOR = @PACKAGE_VERSION_SUBMINOR@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPDY_LIBDEPS = @SPDY_LIBDEPS@
+SPDY_LIB_CFLAGS = @SPDY_LIB_CFLAGS@
+SPDY_LIB_CPPFLAGS = @SPDY_LIB_CPPFLAGS@
+SPDY_LIB_LDFLAGS = @SPDY_LIB_LDFLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+_libcurl_config = @_libcurl_config@
+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_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@
+ax_pthread_config = @ax_pthread_config@
+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@
+have_socat = @have_socat@
+have_zzuf = @have_zzuf@
+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@
+lt_cv_objdir = @lt_cv_objdir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# This Makefile.am is in the public domain
+SUBDIRS = .
+@ENABLE_SPDY_TRUE@microspdy = microspdy.h
+include_HEADERS = microhttpd.h $(microspdy)
+EXTRA_DIST = platform.h platform_interface.h w32functions.h autoinit_funcs.h
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/include/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/include/Makefile
+.PRECIOUS: 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__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+install-includeHEADERS: $(include_HEADERS)
+	@$(NORMAL_INSTALL)
+	@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+	  $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+	done
+
+uninstall-includeHEADERS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+#     (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    $(am__make_dryrun) \
+	      || test -d "$(distdir)/$$subdir" \
+	      || $(MKDIR_P) "$(distdir)/$$subdir" \
+	      || exit 1; \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(HEADERS)
+installdirs: installdirs-recursive
+installdirs-am:
+	for dir in "$(DESTDIR)$(includedir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-includeHEADERS
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-includeHEADERS
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+	check-am clean clean-generic clean-libtool cscopelist-am ctags \
+	ctags-am distclean distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am install-dvi \
+	install-dvi-am install-exec install-exec-am install-html \
+	install-html-am install-includeHEADERS install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs installdirs-am maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+	uninstall-am uninstall-includeHEADERS
+
+
+# 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/src/include/autoinit_funcs.h b/src/include/autoinit_funcs.h
new file mode 100644
index 0000000..2f309e1
--- /dev/null
+++ b/src/include/autoinit_funcs.h
@@ -0,0 +1,242 @@
+/*
+ *  AutoinitFuncs: Automatic Initialization and Deinitialization Functions
+ *  CopyrightCopyright (C) 2014  Karlson2k (Evgeny Grin)
+ *
+ *  This header is free software; you can redistribute it and / or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This header is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this header; if not, see
+ *  <http://www.gnu.org/licenses/>.
+ */
+
+/*
+   General usage is simple: include this header, declare or define two
+   functions with zero parameters (void) and any return type: one for
+   initialization and one for deinitialization, add 
+   _SET_INIT_AND_DEINIT_FUNCS(FuncInitName, FuncDeInitName) to the code
+   and functions will be automatically called during application startup
+   and shutdown.
+   This is useful for libraries as libraries doesn't have direct access
+   to main() functions.
+   Example:
+   -------------------------------------------------
+   #include <stdlib.h>
+   #include "autoinit_funcs.h"
+
+   int someVar;
+   void* somePtr;
+
+   void libInit(void)
+   {
+     someVar = 3;
+     somePtr = malloc(100);
+   }
+
+   void libDeinit(void)
+   {
+     free(somePtr);
+   }
+
+   _SET_INIT_AND_DEINIT_FUNCS(libInit,libDeinit);
+   -------------------------------------------------
+   
+   If initializer or deinitializer functions is not needed, just define
+   it as empty function.
+   
+   This header should work with GCC, clang, MSVC (2010 or later).
+   Supported C and C++ languages; application, static and dynamic (DLL)
+   libraries; non-optimized (Debug) and optimized (Release) compilation
+   and linking.
+
+   For more information see header code and comments in code.
+ */
+#ifndef AUTOINIT_FUNCS_INCLUDED
+#define AUTOINIT_FUNCS_INCLUDED 1
+
+/**
+* Current version of the header.
+* 0x01093001 = 1.9.30-1.
+*/
+#define AUTOINIT_FUNCS_VERSION 0x01000001
+
+#if defined(__GNUC__)
+#/* if possible - check for supported attribute */
+#ifdef __has_attribute
+#if !__has_attribute(constructor) || !__has_attribute(destructor)
+#define _GNUC_ATTR_CONSTR_NOT_SUPPORTED 1
+#endif /* !__has_attribute(constructor) || !__has_attribute(destructor) */
+#endif /* __has_attribute */
+#endif /* __GNUC__ */
+
+#if defined(__GNUC__) && !defined(_GNUC_ATTR_CONSTR_NOT_SUPPORTED)
+
+#define GNUC_SET_INIT_AND_DEINIT(FI,FD) \
+  void __attribute__ ((constructor)) _GNUC_init_helper_##FI(void) \
+    { (void)(FI)(); } \
+  void __attribute__ ((destructor)) _GNUC_deinit_helper_##FD(void) \
+    { (void)(FD)(); } \
+  struct _GNUC_dummy_str_##FI{int i;}
+
+#define _SET_INIT_AND_DEINIT_FUNCS(FI,FD) GNUC_SET_INIT_AND_DEINIT(FI,FD)
+#define _AUTOINIT_FUNCS_ARE_SUPPORTED 1
+
+#elif defined (_MSC_FULL_VER) && _MSC_VER+0 >= 1600
+
+/* Make sure that your project/sources define:
+   _LIB if building a static library (_LIB is ignored if _CONSOLE is defined);
+   _USRDLL if building DLL-library;
+   not defined both _LIB and _USRDLL if building an application */
+
+/* Define AUTOINIT_FUNCS_DECLARE_STATIC_REG if you need macro declaration
+   for registering static initialization functions even if you building DLL */
+/* Define AUTOINIT_FUNCS_FORCE_STATIC_REG if you want to set main macro
+   _SET_INIT_AND_DEINIT_FUNCS to static version even if building a DLL*/
+
+/* Stringify macros */
+#define _INSTRMACRO(a) #a
+#define _STRMACRO(a) _INSTRMACRO(a)
+
+#if !defined(_USRDLL) || defined(AUTOINIT_FUNCS_DECLARE_STATIC_REG)
+
+/* required for atexit() */
+#include <stdlib.h>
+
+/* Use "C" linkage for variable to simplify variable decoration */
+#ifdef __cplusplus
+#define W32_INITVARDECL extern "C"
+#else
+#define W32_INITVARDECL extern
+#endif
+
+/* How variable is decorated by compiler */
+#if defined(_M_X64) || defined(_M_AMD64)
+#define W32_VARDECORPREFIX
+#define W32_DECORVARNAME(v) v
+#define W32_VARDECORPEFIXSTR ""
+#elif defined(_M_IX86) || defined(_X86_)
+#define W32_VARDECORPREFIX _
+#define W32_DECORVARNAME(v) _##v
+#define W32_VARDECORPEFIXSTR "_"
+#else
+#error Do not know how to decorate symbols for this architecture
+#endif
+
+/* Internal variable prefix (can be any) */
+#define W32_INITHELPERVARNAME(f) _initHelperDummy_##f
+#define W32_INITHELPERVARNAMEDECORSTR(f) W32_VARDECORPEFIXSTR _STRMACRO(W32_INITHELPERVARNAME(f))
+
+/* Declare section (segment), put variable pointing to init function to chosen segment,
+   force linker to include variable to avoid omitting by optimizer */
+/* Initialization function must be declared as
+   int __cdecl FuncName(void) */
+/* Return value is ignored for C++ initializers */
+/* For C initializers: startup process is aborted if initializer return non-zero */
+#define W32_FPTR_IN_SEG(S,F) \
+  __pragma(section(S,long,read)) \
+  __pragma(comment(linker, "/INCLUDE:" W32_INITHELPERVARNAMEDECORSTR(F))) \
+  W32_INITVARDECL __declspec(allocate(S)) int(__cdecl *W32_INITHELPERVARNAME(F))(void) = &F
+
+/* Section (segment) names for pointers to initializers */
+#define W32_SEG_INIT_C_USER   ".CRT$XCU"
+#define W32_SEG_INIT_C_LIB    ".CRT$XCL"
+#define W32_SEG_INIT_CXX_USER ".CRT$XIU"
+#define W32_SEG_INIT_CXX_LIB  ".CRT$XIL"
+
+/* Declare macro for different initializers sections */
+/* Macro can be used several times to register several initializers */
+/* Once function is registered as initializer, it will be called automatically
+   during application startup */
+/* "lib" initializers are called before "user" initializers */
+/* "C" initializers are called before "C++" initializers */
+#define W32_REG_INIT_C_USER(F) W32_FPTR_IN_SEG(W32_SEG_INIT_C_USER,F)
+#define W32_REG_INIT_C_LIB(F) W32_FPTR_IN_SEG(W32_SEG_INIT_C_LIB,F)
+#define W32_REG_INIT_CXX_USER(F) W32_FPTR_IN_SEG(W32_SEG_INIT_CXX_USER,F)
+#define W32_REG_INIT_CXX_LIB(F) W32_FPTR_IN_SEG(W32_SEG_INIT_CXX_LIB,F)
+
+/* Choose main register macro based on language and program type */
+/* Assuming that _LIB or _USRDLL is defined for static or DLL-library */
+/* Macro can be used several times to register several initializers */
+/* Once function is registered as initializer, it will be called automatically
+   during application startup */
+/* Define AUTOINIT_FUNCS_FORCE_USER_LVL_INIT to register initializers
+   at user level even if building library */
+#ifdef __cplusplus
+#if ((defined(_LIB) && !defined(_CONSOLE)) || defined(_USRDLL)) && !defined(AUTOINIT_FUNCS_FORCE_USER_LVL_INIT)
+#define W32_REGISTER_INIT(F) W32_REG_INIT_CXX_LIB(F)
+#else  /* ! _LIB && ! _DLL */
+#define W32_REGISTER_INIT(F) W32_REG_INIT_CXX_USER(F)
+#endif /* ! _LIB && ! _DLL */
+#else  /* !__cplusplus*/
+#if ((defined(_LIB) && !defined(_CONSOLE)) || defined(_USRDLL)) && !defined(AUTOINIT_FUNCS_FORCE_USER_LVL_INIT)
+#define W32_REGISTER_INIT(F) W32_REG_INIT_C_LIB(F)
+#else  /* ! _LIB && ! _DLL */
+#define W32_REGISTER_INIT(F) W32_REG_INIT_C_USER(F)
+#endif /* ! _LIB && ! _DLL */
+#endif /* !__cplusplus*/
+
+#else /* _USRDLL */
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* WIN32_LEAN_AND_MEAN */
+/* Required for DllMain */
+#include <Windows.h>
+#endif /* _USRDLL */
+
+
+#if !defined(_USRDLL) || defined(AUTOINIT_FUNCS_FORCE_STATIC_REG)
+#define W32_SET_INIT_AND_DEINIT(FI,FD) \
+  void __cdecl _W32_deinit_helper_##FD(void) \
+   { (void)(FD)(); } \
+  int __cdecl _W32_init_helper_##FI(void) \
+   { (void)(FI)(); atexit(_W32_deinit_helper_##FD); return 0; } \
+  W32_REGISTER_INIT(_W32_init_helper_##FI)
+#else  /* _USRDLL */
+
+/* If DllMain is already present in code, define AUTOINIT_FUNCS_CALL_USR_DLLMAIN 
+   and rename DllMain to usr_DllMain */
+#ifndef AUTOINIT_FUNCS_CALL_USR_DLLMAIN
+#define W32_SET_INIT_AND_DEINIT(FI,FD) \
+  BOOL WINAPI DllMain(HINSTANCE hinst,DWORD reason,LPVOID unused) \
+    { if(DLL_PROCESS_ATTACH==reason) {(void)(FI)();} \
+      else if(DLL_PROCESS_DETACH==reason) {(void)(FD)();} \
+      return TRUE; \
+    } struct _W32_dummy_strc_##FI{int i;}
+#else  /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */
+#define W32_SET_INIT_AND_DEINIT(FI,FD) \
+  BOOL WINAPI usr_DllMain(HINSTANCE hinst,DWORD reason,LPVOID unused); \
+  BOOL WINAPI DllMain(HINSTANCE hinst,DWORD reason,LPVOID unused) \
+    { if(DLL_PROCESS_ATTACH==reason) {(void)(FI)();} \
+      else if(DLL_PROCESS_DETACH==reason) {(void)(FD)();} \
+      return usr_DllMain(hinst,reason,unused); \
+    } struct _W32_dummy_strc_##FI{int i;}
+#endif /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */
+#endif /* _USRDLL */
+
+#define _SET_INIT_AND_DEINIT_FUNCS(FI,FD) W32_SET_INIT_AND_DEINIT(FI,FD)
+/* Indicate that automatic initializers/deinitializers are supported */
+#define _AUTOINIT_FUNCS_ARE_SUPPORTED 1
+
+#else  /* !__GNUC__ && !_MSC_FULL_VER */
+
+/* Define EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED before inclusion of header to
+   abort compilation if automatic initializers/deinitializers are not supported */
+#ifdef EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED
+#error Compiler/platform don not support automatic calls of user-defined initializer and deinitializer
+#endif /* EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED */
+
+/* Do nothing */
+#define _SET_INIT_AND_DEINIT_FUNCS(FI,FD)
+/* Indicate that automatic initializers/deinitializers are not supported */
+#define _AUTOINIT_FUNCS_ARE_NOT_SUPPORTED 1
+
+#endif /* !__GNUC__ && !_MSC_FULL_VER */
+#endif /* !AUTOINIT_FUNCS_INCLUDED */
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
new file mode 100644
index 0000000..ca3bb86
--- /dev/null
+++ b/src/include/microhttpd.h
@@ -0,0 +1,2675 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2006-2015 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file microhttpd.h
+ * @brief public interface to libmicrohttpd
+ * @author Christian Grothoff
+ * @author Chris GauthierDickey
+ *
+ * All symbols defined in this header start with MHD.  MHD is a small
+ * HTTP daemon library.  As such, it does not have any API for logging
+ * errors (you can only enable or disable logging to stderr).  Also,
+ * it may not support all of the HTTP features directly, where
+ * applicable, portions of HTTP may have to be handled by clients of
+ * the library.
+ *
+ * The library is supposed to handle everything that it must handle
+ * (because the API would not allow clients to do this), such as basic
+ * connection management; however, detailed interpretations of headers
+ * -- such as range requests -- and HTTP methods are left to clients.
+ * The library does understand HEAD and will only send the headers of
+ * the response and not the body, even if the client supplied a body.
+ * The library also understands headers that control connection
+ * management (specifically, "Connection: close" and "Expect: 100
+ * continue" are understood and handled automatically).
+ *
+ * MHD understands POST data and is able to decode certain formats
+ * (at the moment only "application/x-www-form-urlencoded" and
+ * "mulitpart/formdata"). Unsupported encodings and large POST
+ * submissions may require the application to manually process
+ * the stream, which is provided to the main application (and thus can be
+ * processed, just not conveniently by MHD).
+ *
+ * The header file defines various constants used by the HTTP protocol.
+ * This does not mean that MHD actually interprets all of these
+ * values.  The provided constants are exported as a convenience
+ * for users of the library.  MHD does not verify that transmitted
+ * HTTP headers are part of the standard specification; users of the
+ * library are free to define their own extensions of the HTTP
+ * standard and use those with MHD.
+ *
+ * All functions are guaranteed to be completely reentrant and
+ * thread-safe (with the exception of #MHD_set_connection_value,
+ * which must only be used in a particular context).
+ *
+ * NEW: Before including "microhttpd.h" you should add the necessary
+ * includes to define the `uint64_t`, `size_t`, `fd_set`, `socklen_t`
+ * and `struct sockaddr` data types (which headers are needed may
+ * depend on your platform; for possible suggestions consult
+ * "platform.h" in the MHD distribution).  If you have done so, you
+ * should also have a line with "#define MHD_PLATFORM_H" which will
+ * prevent this header from trying (and, depending on your platform,
+ * failing) to include the right headers.
+ *
+ * @defgroup event event-loop control
+ * MHD API to start and stop the HTTP server and manage the event loop.
+ * @defgroup response generation of responses
+ * MHD API used to generate responses.
+ * @defgroup request handling of requests
+ * MHD API used to access information about requests.
+ * @defgroup authentication HTTP authentication
+ * MHD API related to basic and digest HTTP authentication.
+ * @defgroup logging logging
+ * MHD API to mange logging and error handling
+ * @defgroup specialized misc. specialized functions
+ * This group includes functions that do not fit into any particular
+ * category and that are rarely used.
+ */
+
+#ifndef MHD_MICROHTTPD_H
+#define MHD_MICROHTTPD_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0                           /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+/* While we generally would like users to use a configure-driven
+   build process which detects which headers are present and
+   hence works on any platform, we use "standard" includes here
+   to build out-of-the-box for beginning users on common systems.
+
+   Once you have a proper build system and go for more exotic
+   platforms, you should define MHD_PLATFORM_H in some header that
+   you always include *before* "microhttpd.h".  Then the following
+   "standard" includes won't be used (which might be a good
+   idea, especially on platforms where they do not exist). */
+#ifndef MHD_PLATFORM_H
+#include <stdarg.h>
+#include <stdint.h>
+#include <sys/types.h>
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <ws2tcpip.h>
+#if defined(_MSC_FULL_VER) && !defined (_SSIZE_T_DEFINED)
+#define _SSIZE_T_DEFINED
+typedef intptr_t ssize_t;
+#endif // !_SSIZE_T_DEFINED */
+#else
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#endif
+#endif
+
+#if defined(__CYGWIN__) && !defined(_SYS_TYPES_FD_SET)
+/* Do not define __USE_W32_SOCKETS under Cygwin! */
+#error Cygwin with winsock fd_set is not supported
+#endif
+
+/**
+ * Current version of the library.
+ * 0x01093001 = 1.9.30-1.
+ */
+#define MHD_VERSION 0x00094200
+
+/**
+ * MHD-internal return code for "YES".
+ */
+#define MHD_YES 1
+
+/**
+ * MHD-internal return code for "NO".
+ */
+#define MHD_NO 0
+
+/**
+ * MHD digest auth internal code for an invalid nonce.
+ */
+#define MHD_INVALID_NONCE -1
+
+/**
+ * Constant used to indicate unknown size (use when
+ * creating a response).
+ */
+#ifdef UINT64_MAX
+#define MHD_SIZE_UNKNOWN UINT64_MAX
+#else
+#define MHD_SIZE_UNKNOWN  ((uint64_t) -1LL)
+#endif
+
+#ifdef SIZE_MAX
+#define MHD_CONTENT_READER_END_OF_STREAM SIZE_MAX
+#define MHD_CONTENT_READER_END_WITH_ERROR (SIZE_MAX - 1)
+#else
+#define MHD_CONTENT_READER_END_OF_STREAM ((size_t) -1LL)
+#define MHD_CONTENT_READER_END_WITH_ERROR (((size_t) -1LL) - 1)
+#endif
+
+#ifndef _MHD_EXTERN
+#if defined(_WIN32) && defined(MHD_W32LIB)
+#define _MHD_EXTERN extern
+#elif defined (_WIN32) && defined(MHD_W32DLL)
+/* Define MHD_W32DLL when using MHD as W32 .DLL to speed up linker a little */
+#define _MHD_EXTERN __declspec(dllimport)
+#else
+#define _MHD_EXTERN extern
+#endif
+#endif
+
+#ifndef MHD_SOCKET_DEFINED
+/**
+ * MHD_socket is type for socket FDs
+ */
+#if !defined(_WIN32) || defined(_SYS_TYPES_FD_SET)
+#define MHD_POSIX_SOCKETS 1
+typedef int MHD_socket;
+#define MHD_INVALID_SOCKET (-1)
+#else /* !defined(_WIN32) || defined(_SYS_TYPES_FD_SET) */
+#define MHD_WINSOCK_SOCKETS 1
+#include <winsock2.h>
+typedef SOCKET MHD_socket;
+#define MHD_INVALID_SOCKET (INVALID_SOCKET)
+#endif /* !defined(_WIN32) || defined(_SYS_TYPES_FD_SET) */
+#define MHD_SOCKET_DEFINED 1
+#endif /* MHD_SOCKET_DEFINED */
+
+/**
+ * Not all architectures and `printf()`'s support the `long long` type.
+ * This gives the ability to replace `long long` with just a `long`,
+ * standard `int` or a `short`.
+ */
+#ifndef MHD_LONG_LONG
+/**
+ * @deprecated use #MHD_UNSIGNED_LONG_LONG instead!
+ */
+#define MHD_LONG_LONG long long
+#define MHD_UNSIGNED_LONG_LONG unsigned long long
+#endif
+/**
+ * Format string for printing a variable of type #MHD_LONG_LONG.
+ * You should only redefine this if you also define #MHD_LONG_LONG.
+ */
+#ifndef MHD_LONG_LONG_PRINTF
+/**
+ * @deprecated use #MHD_UNSIGNED_LONG_LONG_PRINTF instead!
+ */
+#define MHD_LONG_LONG_PRINTF "ll"
+#define MHD_UNSIGNED_LONG_LONG_PRINTF "%llu"
+#endif
+
+
+/**
+ * @defgroup httpcode HTTP response codes.
+ * These are the status codes defined for HTTP responses.
+ * @{
+ */
+#define MHD_HTTP_CONTINUE 100
+#define MHD_HTTP_SWITCHING_PROTOCOLS 101
+#define MHD_HTTP_PROCESSING 102
+
+#define MHD_HTTP_OK 200
+#define MHD_HTTP_CREATED 201
+#define MHD_HTTP_ACCEPTED 202
+#define MHD_HTTP_NON_AUTHORITATIVE_INFORMATION 203
+#define MHD_HTTP_NO_CONTENT 204
+#define MHD_HTTP_RESET_CONTENT 205
+#define MHD_HTTP_PARTIAL_CONTENT 206
+#define MHD_HTTP_MULTI_STATUS 207
+
+#define MHD_HTTP_MULTIPLE_CHOICES 300
+#define MHD_HTTP_MOVED_PERMANENTLY 301
+#define MHD_HTTP_FOUND 302
+#define MHD_HTTP_SEE_OTHER 303
+#define MHD_HTTP_NOT_MODIFIED 304
+#define MHD_HTTP_USE_PROXY 305
+#define MHD_HTTP_SWITCH_PROXY 306
+#define MHD_HTTP_TEMPORARY_REDIRECT 307
+
+#define MHD_HTTP_BAD_REQUEST 400
+#define MHD_HTTP_UNAUTHORIZED 401
+#define MHD_HTTP_PAYMENT_REQUIRED 402
+#define MHD_HTTP_FORBIDDEN 403
+#define MHD_HTTP_NOT_FOUND 404
+#define MHD_HTTP_METHOD_NOT_ALLOWED 405
+#define MHD_HTTP_NOT_ACCEPTABLE 406
+/** @deprecated */
+#define MHD_HTTP_METHOD_NOT_ACCEPTABLE 406
+#define MHD_HTTP_PROXY_AUTHENTICATION_REQUIRED 407
+#define MHD_HTTP_REQUEST_TIMEOUT 408
+#define MHD_HTTP_CONFLICT 409
+#define MHD_HTTP_GONE 410
+#define MHD_HTTP_LENGTH_REQUIRED 411
+#define MHD_HTTP_PRECONDITION_FAILED 412
+#define MHD_HTTP_REQUEST_ENTITY_TOO_LARGE 413
+#define MHD_HTTP_REQUEST_URI_TOO_LONG 414
+#define MHD_HTTP_UNSUPPORTED_MEDIA_TYPE 415
+#define MHD_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE 416
+#define MHD_HTTP_EXPECTATION_FAILED 417
+#define MHD_HTTP_UNPROCESSABLE_ENTITY 422
+#define MHD_HTTP_LOCKED 423
+#define MHD_HTTP_FAILED_DEPENDENCY 424
+#define MHD_HTTP_UNORDERED_COLLECTION 425
+#define MHD_HTTP_UPGRADE_REQUIRED 426
+#define MHD_HTTP_NO_RESPONSE 444
+#define MHD_HTTP_RETRY_WITH 449
+#define MHD_HTTP_BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS 450
+#define MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS 451
+
+#define MHD_HTTP_INTERNAL_SERVER_ERROR 500
+#define MHD_HTTP_NOT_IMPLEMENTED 501
+#define MHD_HTTP_BAD_GATEWAY 502
+#define MHD_HTTP_SERVICE_UNAVAILABLE 503
+#define MHD_HTTP_GATEWAY_TIMEOUT 504
+#define MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED 505
+#define MHD_HTTP_VARIANT_ALSO_NEGOTIATES 506
+#define MHD_HTTP_INSUFFICIENT_STORAGE 507
+#define MHD_HTTP_BANDWIDTH_LIMIT_EXCEEDED 509
+#define MHD_HTTP_NOT_EXTENDED 510
+
+/** @} */ /* end of group httpcode */
+
+/**
+ * Flag to be or-ed with MHD_HTTP status code for
+ * SHOUTcast.  This will cause the response to begin
+ * with the SHOUTcast "ICY" line instad of "HTTP".
+ * @ingroup specialized
+ */
+#define MHD_ICY_FLAG ((uint32_t)(((uint32_t)1) << 31))
+
+/**
+ * @defgroup headers HTTP headers
+ * These are the standard headers found in HTTP requests and responses.
+ * @{
+ */
+/* See also: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html */
+#define MHD_HTTP_HEADER_ACCEPT "Accept"
+#define MHD_HTTP_HEADER_ACCEPT_CHARSET "Accept-Charset"
+#define MHD_HTTP_HEADER_ACCEPT_ENCODING "Accept-Encoding"
+#define MHD_HTTP_HEADER_ACCEPT_LANGUAGE "Accept-Language"
+#define MHD_HTTP_HEADER_ACCEPT_RANGES "Accept-Ranges"
+#define MHD_HTTP_HEADER_AGE "Age"
+#define MHD_HTTP_HEADER_ALLOW "Allow"
+#define MHD_HTTP_HEADER_AUTHORIZATION "Authorization"
+#define MHD_HTTP_HEADER_CACHE_CONTROL "Cache-Control"
+#define MHD_HTTP_HEADER_CONNECTION "Connection"
+#define MHD_HTTP_HEADER_CONTENT_ENCODING "Content-Encoding"
+#define MHD_HTTP_HEADER_CONTENT_LANGUAGE "Content-Language"
+#define MHD_HTTP_HEADER_CONTENT_LENGTH "Content-Length"
+#define MHD_HTTP_HEADER_CONTENT_LOCATION "Content-Location"
+#define MHD_HTTP_HEADER_CONTENT_MD5 "Content-MD5"
+#define MHD_HTTP_HEADER_CONTENT_RANGE "Content-Range"
+#define MHD_HTTP_HEADER_CONTENT_TYPE "Content-Type"
+#define MHD_HTTP_HEADER_COOKIE "Cookie"
+#define MHD_HTTP_HEADER_DATE "Date"
+#define MHD_HTTP_HEADER_ETAG "ETag"
+#define MHD_HTTP_HEADER_EXPECT "Expect"
+#define MHD_HTTP_HEADER_EXPIRES "Expires"
+#define MHD_HTTP_HEADER_FROM "From"
+#define MHD_HTTP_HEADER_HOST "Host"
+#define MHD_HTTP_HEADER_IF_MATCH "If-Match"
+#define MHD_HTTP_HEADER_IF_MODIFIED_SINCE "If-Modified-Since"
+#define MHD_HTTP_HEADER_IF_NONE_MATCH "If-None-Match"
+#define MHD_HTTP_HEADER_IF_RANGE "If-Range"
+#define MHD_HTTP_HEADER_IF_UNMODIFIED_SINCE "If-Unmodified-Since"
+#define MHD_HTTP_HEADER_LAST_MODIFIED "Last-Modified"
+#define MHD_HTTP_HEADER_LOCATION "Location"
+#define MHD_HTTP_HEADER_MAX_FORWARDS "Max-Forwards"
+#define MHD_HTTP_HEADER_PRAGMA "Pragma"
+#define MHD_HTTP_HEADER_PROXY_AUTHENTICATE "Proxy-Authenticate"
+#define MHD_HTTP_HEADER_PROXY_AUTHORIZATION "Proxy-Authorization"
+#define MHD_HTTP_HEADER_RANGE "Range"
+/* This is not a typo, see HTTP spec */
+#define MHD_HTTP_HEADER_REFERER "Referer"
+#define MHD_HTTP_HEADER_RETRY_AFTER "Retry-After"
+#define MHD_HTTP_HEADER_SERVER "Server"
+#define MHD_HTTP_HEADER_SET_COOKIE "Set-Cookie"
+#define MHD_HTTP_HEADER_SET_COOKIE2 "Set-Cookie2"
+#define MHD_HTTP_HEADER_TE "TE"
+#define MHD_HTTP_HEADER_TRAILER "Trailer"
+#define MHD_HTTP_HEADER_TRANSFER_ENCODING "Transfer-Encoding"
+#define MHD_HTTP_HEADER_UPGRADE "Upgrade"
+#define MHD_HTTP_HEADER_USER_AGENT "User-Agent"
+#define MHD_HTTP_HEADER_VARY "Vary"
+#define MHD_HTTP_HEADER_VIA "Via"
+#define MHD_HTTP_HEADER_WARNING "Warning"
+#define MHD_HTTP_HEADER_WWW_AUTHENTICATE "WWW-Authenticate"
+#define MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN "Access-Control-Allow-Origin"
+
+/** @} */ /* end of group headers */
+
+/**
+ * @defgroup versions HTTP versions
+ * These strings should be used to match against the first line of the
+ * HTTP header.
+ * @{
+ */
+#define MHD_HTTP_VERSION_1_0 "HTTP/1.0"
+#define MHD_HTTP_VERSION_1_1 "HTTP/1.1"
+
+/** @} */ /* end of group versions */
+
+/**
+ * @defgroup methods HTTP methods
+ * Standard HTTP methods (as strings).
+ * @{
+ */
+#define MHD_HTTP_METHOD_CONNECT "CONNECT"
+#define MHD_HTTP_METHOD_DELETE "DELETE"
+#define MHD_HTTP_METHOD_GET "GET"
+#define MHD_HTTP_METHOD_HEAD "HEAD"
+#define MHD_HTTP_METHOD_OPTIONS "OPTIONS"
+#define MHD_HTTP_METHOD_POST "POST"
+#define MHD_HTTP_METHOD_PUT "PUT"
+#define MHD_HTTP_METHOD_PATCH "PATCH"
+#define MHD_HTTP_METHOD_TRACE "TRACE"
+
+/** @} */ /* end of group methods */
+
+/**
+ * @defgroup postenc HTTP POST encodings
+ * See also: http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4
+ * @{
+ */
+#define MHD_HTTP_POST_ENCODING_FORM_URLENCODED "application/x-www-form-urlencoded"
+#define MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA "multipart/form-data"
+
+/** @} */ /* end of group postenc */
+
+
+/**
+ * @brief Handle for the daemon (listening on a socket for HTTP traffic).
+ * @ingroup event
+ */
+struct MHD_Daemon;
+
+/**
+ * @brief Handle for a connection / HTTP request.
+ *
+ * With HTTP/1.1, multiple requests can be run over the same
+ * connection.  However, MHD will only show one request per TCP
+ * connection to the client at any given time.
+ * @ingroup request
+ */
+struct MHD_Connection;
+
+/**
+ * @brief Handle for a response.
+ * @ingroup response
+ */
+struct MHD_Response;
+
+/**
+ * @brief Handle for POST processing.
+ * @ingroup response
+ */
+struct MHD_PostProcessor;
+
+
+/**
+ * @brief Flags for the `struct MHD_Daemon`.
+ *
+ * Note that if neither #MHD_USE_THREAD_PER_CONNECTION nor
+ * #MHD_USE_SELECT_INTERNALLY is used, the client wants control over
+ * the process and will call the appropriate microhttpd callbacks.
+ *
+ * Starting the daemon may also fail if a particular option is not
+ * implemented or not supported on the target platform (i.e. no
+ * support for SSL, threads or IPv6).
+ */
+enum MHD_FLAG
+{
+  /**
+   * No options selected.
+   */
+  MHD_NO_FLAG = 0,
+
+  /**
+   * Run in debug mode.  If this flag is used, the library should
+   * print error messages and warnings to `stderr`.
+   */
+  MHD_USE_DEBUG = 1,
+
+  /**
+   * Run in HTTPS mode.
+   */
+  MHD_USE_SSL = 2,
+
+  /**
+   * Run using one thread per connection.
+   */
+  MHD_USE_THREAD_PER_CONNECTION = 4,
+
+  /**
+   * Run using an internal thread (or thread pool) doing `select()`.
+   */
+  MHD_USE_SELECT_INTERNALLY = 8,
+
+  /**
+   * Run using the IPv6 protocol (otherwise, MHD will just support
+   * IPv4).  If you want MHD to support IPv4 and IPv6 using a single
+   * socket, pass #MHD_USE_DUAL_STACK, otherwise, if you only pass
+   * this option, MHD will try to bind to IPv6-only (resulting in
+   * no IPv4 support).
+   */
+  MHD_USE_IPv6 = 16,
+
+  /**
+   * Be pedantic about the protocol (as opposed to as tolerant as
+   * possible).  Specifically, at the moment, this flag causes MHD to
+   * reject HTTP 1.1 connections without a "Host" header.  This is
+   * required by the standard, but of course in violation of the "be
+   * as liberal as possible in what you accept" norm.  It is
+   * recommended to turn this ON if you are testing clients against
+   * MHD, and OFF in production.
+   */
+  MHD_USE_PEDANTIC_CHECKS = 32,
+
+  /**
+   * Use `poll()` instead of `select()`. This allows sockets with `fd >=
+   * FD_SETSIZE`.  This option is not compatible with using an
+   * 'external' `select()` mode (as there is no API to get the file
+   * descriptors for the external select from MHD) and must also not
+   * be used in combination with #MHD_USE_EPOLL_LINUX_ONLY.
+   */
+  MHD_USE_POLL = 64,
+
+  /**
+   * Run using an internal thread (or thread pool) doing `poll()`.
+   */
+  MHD_USE_POLL_INTERNALLY = MHD_USE_SELECT_INTERNALLY | MHD_USE_POLL,
+
+  /**
+   * Suppress (automatically) adding the 'Date:' header to HTTP responses.
+   * This option should ONLY be used on systems that do not have a clock
+   * and that DO provide other mechanisms for cache control.  See also
+   * RFC 2616, section 14.18 (exception 3).
+   */
+  MHD_SUPPRESS_DATE_NO_CLOCK = 128,
+
+  /**
+   * Run without a listen socket.  This option only makes sense if
+   * #MHD_add_connection is to be used exclusively to connect HTTP
+   * clients to the HTTP server.  This option is incompatible with
+   * using a thread pool; if it is used, #MHD_OPTION_THREAD_POOL_SIZE
+   * is ignored.
+   */
+  MHD_USE_NO_LISTEN_SOCKET = 256,
+
+  /**
+   * Use `epoll()` instead of `select()` or `poll()` for the event loop.
+   * This option is only available on Linux; using the option on
+   * non-Linux systems will cause #MHD_start_daemon to fail.
+   */
+  MHD_USE_EPOLL_LINUX_ONLY = 512,
+
+  /**
+   * Run using an internal thread (or thread pool) doing `epoll()`.
+   * This option is only available on Linux; using the option on
+   * non-Linux systems will cause #MHD_start_daemon to fail.
+   */
+  MHD_USE_EPOLL_INTERNALLY_LINUX_ONLY = MHD_USE_SELECT_INTERNALLY | MHD_USE_EPOLL_LINUX_ONLY,
+
+  /**
+   * Force MHD to use a signal pipe to notify the event loop (of
+   * threads) of our shutdown.  This is required if an appliction uses
+   * #MHD_USE_SELECT_INTERNALLY or #MHD_USE_THREAD_PER_CONNECTION and
+   * then performs #MHD_quiesce_daemon (which eliminates our ability
+   * to signal termination via the listen socket).  In these modes,
+   * #MHD_quiesce_daemon will fail if this option was not set.  Also,
+   * use of this option is automatic (as in, you do not even have to
+   * specify it), if #MHD_USE_NO_LISTEN_SOCKET is specified.  In
+   * "external" `select()` mode, this option is always simply ignored.
+   * MHD can be build for use a pair of sockets instead of a pipe.
+   * Pair of sockets is forced on W32.
+   *
+   * You must also use this option if you use internal select mode
+   * or a thread pool in conjunction with #MHD_add_connection.
+   */
+  MHD_USE_PIPE_FOR_SHUTDOWN = 1024,
+
+  /**
+   * Use a single socket for IPv4 and IPv6.
+   */
+  MHD_USE_DUAL_STACK = MHD_USE_IPv6 | 2048,
+
+  /**
+   * Enable `epoll()` turbo.  Disables certain calls to `shutdown()`
+   * and enables aggressive non-blocking optimisitc reads.
+   * Most effects only happen with #MHD_USE_EPOLL_LINUX_ONLY.
+   * Enalbed always on W32 as winsock does not properly behave
+   * with `shutdown()` and this then fixes potential problems.
+   */
+  MHD_USE_EPOLL_TURBO = 4096,
+
+  /**
+   * Enable suspend/resume functions, which also implies setting up
+   * pipes to signal resume.
+   */
+  MHD_USE_SUSPEND_RESUME = 8192 | MHD_USE_PIPE_FOR_SHUTDOWN,
+
+  /**
+   * Enable TCP_FASTOPEN option.  This option is only available on Linux with a
+   * kernel >= 3.6.  On other systems, using this option cases #MHD_start_daemon
+   * to fail.
+   */
+  MHD_USE_TCP_FASTOPEN = 16384
+
+};
+
+
+/**
+ * Type of a callback function used for logging by MHD.
+ *
+ * @param cls closure
+ * @param fm format string (`printf()`-style)
+ * @param ap arguments to @a fm
+ * @ingroup logging
+ */
+typedef void (*MHD_LogCallback)(void *cls, const char *fm, va_list ap);
+
+
+/**
+ * @brief MHD options.
+ *
+ * Passed in the varargs portion of #MHD_start_daemon.
+ */
+enum MHD_OPTION
+{
+
+  /**
+   * No more options / last option.  This is used
+   * to terminate the VARARGs list.
+   */
+  MHD_OPTION_END = 0,
+
+  /**
+   * Maximum memory size per connection (followed by a `size_t`).
+   * Default is 32 kb (#MHD_POOL_SIZE_DEFAULT).
+   * Values above 128k are unlikely to result in much benefit, as half
+   * of the memory will be typically used for IO, and TCP buffers are
+   * unlikely to support window sizes above 64k on most systems.
+   */
+  MHD_OPTION_CONNECTION_MEMORY_LIMIT = 1,
+
+  /**
+   * Maximum number of concurrent connections to
+   * accept (followed by an `unsigned int`).
+   */
+  MHD_OPTION_CONNECTION_LIMIT = 2,
+
+  /**
+   * After how many seconds of inactivity should a
+   * connection automatically be timed out? (followed
+   * by an `unsigned int`; use zero for no timeout).
+   */
+  MHD_OPTION_CONNECTION_TIMEOUT = 3,
+
+  /**
+   * Register a function that should be called whenever a request has
+   * been completed (this can be used for application-specific clean
+   * up).  Requests that have never been presented to the application
+   * (via #MHD_AccessHandlerCallback) will not result in
+   * notifications.
+   *
+   * This option should be followed by TWO pointers.  First a pointer
+   * to a function of type #MHD_RequestCompletedCallback and second a
+   * pointer to a closure to pass to the request completed callback.
+   * The second pointer maybe NULL.
+   */
+  MHD_OPTION_NOTIFY_COMPLETED = 4,
+
+  /**
+   * Limit on the number of (concurrent) connections made to the
+   * server from the same IP address.  Can be used to prevent one
+   * IP from taking over all of the allowed connections.  If the
+   * same IP tries to establish more than the specified number of
+   * connections, they will be immediately rejected.  The option
+   * should be followed by an `unsigned int`.  The default is
+   * zero, which means no limit on the number of connections
+   * from the same IP address.
+   */
+  MHD_OPTION_PER_IP_CONNECTION_LIMIT = 5,
+
+  /**
+   * Bind daemon to the supplied `struct sockaddr`. This option should
+   * be followed by a `struct sockaddr *`.  If #MHD_USE_IPv6 is
+   * specified, the `struct sockaddr*` should point to a `struct
+   * sockaddr_in6`, otherwise to a `struct sockaddr_in`.
+   */
+  MHD_OPTION_SOCK_ADDR = 6,
+
+  /**
+   * Specify a function that should be called before parsing the URI from
+   * the client.  The specified callback function can be used for processing
+   * the URI (including the options) before it is parsed.  The URI after
+   * parsing will no longer contain the options, which maybe inconvenient for
+   * logging.  This option should be followed by two arguments, the first
+   * one must be of the form
+   *
+   *     void * my_logger(void *cls, const char *uri, struct MHD_Connection *con)
+   *
+   * where the return value will be passed as
+   * (`* con_cls`) in calls to the #MHD_AccessHandlerCallback
+   * when this request is processed later; returning a
+   * value of NULL has no special significance (however,
+   * note that if you return non-NULL, you can no longer
+   * rely on the first call to the access handler having
+   * `NULL == *con_cls` on entry;)
+   * "cls" will be set to the second argument following
+   * #MHD_OPTION_URI_LOG_CALLBACK.  Finally, uri will
+   * be the 0-terminated URI of the request.
+   *
+   * Note that during the time of this call, most of the connection's
+   * state is not initialized (as we have not yet parsed he headers).
+   * However, information about the connecting client (IP, socket)
+   * is available.
+   */
+  MHD_OPTION_URI_LOG_CALLBACK = 7,
+
+  /**
+   * Memory pointer for the private key (key.pem) to be used by the
+   * HTTPS daemon.  This option should be followed by a
+   * `const char *` argument.
+   * This should be used in conjunction with #MHD_OPTION_HTTPS_MEM_CERT.
+   */
+  MHD_OPTION_HTTPS_MEM_KEY = 8,
+
+  /**
+   * Memory pointer for the certificate (cert.pem) to be used by the
+   * HTTPS daemon.  This option should be followed by a
+   * `const char *` argument.
+   * This should be used in conjunction with #MHD_OPTION_HTTPS_MEM_KEY.
+   */
+  MHD_OPTION_HTTPS_MEM_CERT = 9,
+
+  /**
+   * Daemon credentials type.
+   * Followed by an argument of type
+   * `gnutls_credentials_type_t`.
+   */
+  MHD_OPTION_HTTPS_CRED_TYPE = 10,
+
+  /**
+   * Memory pointer to a `const char *` specifying the
+   * cipher algorithm (default: "NORMAL").
+   */
+  MHD_OPTION_HTTPS_PRIORITIES = 11,
+
+  /**
+   * Pass a listen socket for MHD to use (systemd-style).  If this
+   * option is used, MHD will not open its own listen socket(s). The
+   * argument passed must be of type `int` and refer to an
+   * existing socket that has been bound to a port and is listening.
+   */
+  MHD_OPTION_LISTEN_SOCKET = 12,
+
+  /**
+   * Use the given function for logging error messages.  This option
+   * must be followed by two arguments; the first must be a pointer to
+   * a function of type #MHD_LogCallback and the second a pointer
+   * `void *` which will be passed as the first argument to the log
+   * callback.
+   *
+   * Note that MHD will not generate any log messages
+   * if it was compiled without the "--enable-messages"
+   * flag being set.
+   */
+  MHD_OPTION_EXTERNAL_LOGGER = 13,
+
+  /**
+   * Number (`unsigned int`) of threads in thread pool. Enable
+   * thread pooling by setting this value to to something
+   * greater than 1. Currently, thread model must be
+   * #MHD_USE_SELECT_INTERNALLY if thread pooling is enabled
+   * (#MHD_start_daemon returns NULL for an unsupported thread
+   * model).
+   */
+  MHD_OPTION_THREAD_POOL_SIZE = 14,
+
+  /**
+   * Additional options given in an array of `struct MHD_OptionItem`.
+   * The array must be terminated with an entry `{MHD_OPTION_END, 0, NULL}`.
+   * An example for code using #MHD_OPTION_ARRAY is:
+   *
+   *     struct MHD_OptionItem ops[] = {
+   *       { MHD_OPTION_CONNECTION_LIMIT, 100, NULL },
+   *       { MHD_OPTION_CONNECTION_TIMEOUT, 10, NULL },
+   *       { MHD_OPTION_END, 0, NULL }
+   *     };
+   *     d = MHD_start_daemon (0, 8080, NULL, NULL, dh, NULL,
+   *                           MHD_OPTION_ARRAY, ops,
+   *                           MHD_OPTION_END);
+   *
+   * For options that expect a single pointer argument, the
+   * second member of the `struct MHD_OptionItem` is ignored.
+   * For options that expect two pointer arguments, the first
+   * argument must be cast to `intptr_t`.
+   */
+  MHD_OPTION_ARRAY = 15,
+
+  /**
+   * Specify a function that should be called for unescaping escape
+   * sequences in URIs and URI arguments.  Note that this function
+   * will NOT be used by the `struct MHD_PostProcessor`.  If this
+   * option is not specified, the default method will be used which
+   * decodes escape sequences of the form "%HH".  This option should
+   * be followed by two arguments, the first one must be of the form
+   *
+   *     size_t my_unescaper(void *cls,
+   *                         struct MHD_Connection *c,
+   *                         char *s)
+   *
+   * where the return value must be "strlen(s)" and "s" should be
+   * updated.  Note that the unescape function must not lengthen "s"
+   * (the result must be shorter than the input and still be
+   * 0-terminated).  "cls" will be set to the second argument
+   * following #MHD_OPTION_UNESCAPE_CALLBACK.
+   */
+  MHD_OPTION_UNESCAPE_CALLBACK = 16,
+
+  /**
+   * Memory pointer for the random values to be used by the Digest
+   * Auth module. This option should be followed by two arguments.
+   * First an integer of type  `size_t` which specifies the size
+   * of the buffer pointed to by the second argument in bytes.
+   * Note that the application must ensure that the buffer of the
+   * second argument remains allocated and unmodified while the
+   * deamon is running.
+   */
+  MHD_OPTION_DIGEST_AUTH_RANDOM = 17,
+
+  /**
+   * Size of the internal array holding the map of the nonce and
+   * the nonce counter. This option should be followed by an `unsigend int`
+   * argument.
+   */
+  MHD_OPTION_NONCE_NC_SIZE = 18,
+
+  /**
+   * Desired size of the stack for threads created by MHD. Followed
+   * by an argument of type `size_t`.  Use 0 for system default.
+   */
+  MHD_OPTION_THREAD_STACK_SIZE = 19,
+
+  /**
+   * Memory pointer for the certificate (ca.pem) to be used by the
+   * HTTPS daemon for client authentification.
+   * This option should be followed by a `const char *` argument.
+   */
+  MHD_OPTION_HTTPS_MEM_TRUST = 20,
+
+  /**
+   * Increment to use for growing the read buffer (followed by a
+   * `size_t`). Must fit within #MHD_OPTION_CONNECTION_MEMORY_LIMIT.
+   */
+  MHD_OPTION_CONNECTION_MEMORY_INCREMENT = 21,
+
+  /**
+   * Use a callback to determine which X.509 certificate should be
+   * used for a given HTTPS connection.  This option should be
+   * followed by a argument of type `gnutls_certificate_retrieve_function2 *`.
+   * This option provides an
+   * alternative to #MHD_OPTION_HTTPS_MEM_KEY,
+   * #MHD_OPTION_HTTPS_MEM_CERT.  You must use this version if
+   * multiple domains are to be hosted at the same IP address using
+   * TLS's Server Name Indication (SNI) extension.  In this case,
+   * the callback is expected to select the correct certificate
+   * based on the SNI information provided.  The callback is expected
+   * to access the SNI data using `gnutls_server_name_get()`.
+   * Using this option requires GnuTLS 3.0 or higher.
+   */
+  MHD_OPTION_HTTPS_CERT_CALLBACK = 22,
+
+  /**
+   * When using #MHD_USE_TCP_FASTOPEN, this option changes the default TCP
+   * fastopen queue length of 50.  Note that having a larger queue size can
+   * cause resource exhaustion attack as the TCP stack has to now allocate
+   * resources for the SYN packet along with its DATA.  This option should be
+   * followed by an `unsigned int` argument.
+   */
+  MHD_OPTION_TCP_FASTOPEN_QUEUE_SIZE = 23,
+
+  /**
+   * Memory pointer for the Diffie-Hellman parameters (dh.pem) to be used by the
+   * HTTPS daemon for key exchange.
+   * This option must be followed by a `const char *` argument.
+   */
+  MHD_OPTION_HTTPS_MEM_DHPARAMS = 24,
+
+  /**
+   * If present and set to true, allow reusing address:port socket
+   * (by using SO_REUSEPORT on most platform, or platform-specific ways).
+   * If present and set to false, disallow reusing address:port socket
+   * (does nothing on most plaform, but uses SO_EXCLUSIVEADDRUSE on Windows).
+   * This option must be followed by a `unsigned int` argument.
+   */
+  MHD_OPTION_LISTENING_ADDRESS_REUSE = 25,
+
+  /**
+   * Memory pointer for a password that decrypts the private key (key.pem)
+   * to be used by the HTTPS daemon. This option should be followed by a
+   * `const char *` argument.
+   * This should be used in conjunction with #MHD_OPTION_HTTPS_MEM_KEY.
+   * @sa ::MHD_FEATURE_HTTPS_KEY_PASSWORD
+   */
+  MHD_OPTION_HTTPS_KEY_PASSWORD = 26,
+
+  /**
+   * Register a function that should be called whenever a connection is
+   * started or closed.
+   *
+   * This option should be followed by TWO pointers.  First a pointer
+   * to a function of type #MHD_NotifyConnectionCallback and second a
+   * pointer to a closure to pass to the request completed callback.
+   * The second pointer maybe NULL.
+   */
+  MHD_OPTION_NOTIFY_CONNECTION = 27
+
+};
+
+
+/**
+ * Entry in an #MHD_OPTION_ARRAY.
+ */
+struct MHD_OptionItem
+{
+  /**
+   * Which option is being given.  Use #MHD_OPTION_END
+   * to terminate the array.
+   */
+  enum MHD_OPTION option;
+
+  /**
+   * Option value (for integer arguments, and for options requiring
+   * two pointer arguments); should be 0 for options that take no
+   * arguments or only a single pointer argument.
+   */
+  intptr_t value;
+
+  /**
+   * Pointer option value (use NULL for options taking no arguments
+   * or only an integer option).
+   */
+  void *ptr_value;
+
+};
+
+
+/**
+ * The `enum MHD_ValueKind` specifies the source of
+ * the key-value pairs in the HTTP protocol.
+ */
+enum MHD_ValueKind
+{
+
+  /**
+   * Response header
+   */
+  MHD_RESPONSE_HEADER_KIND = 0,
+
+  /**
+   * HTTP header.
+   */
+  MHD_HEADER_KIND = 1,
+
+  /**
+   * Cookies.  Note that the original HTTP header containing
+   * the cookie(s) will still be available and intact.
+   */
+  MHD_COOKIE_KIND = 2,
+
+  /**
+   * POST data.  This is available only if a content encoding
+   * supported by MHD is used (currently only URL encoding),
+   * and only if the posted content fits within the available
+   * memory pool.  Note that in that case, the upload data
+   * given to the #MHD_AccessHandlerCallback will be
+   * empty (since it has already been processed).
+   */
+  MHD_POSTDATA_KIND = 4,
+
+  /**
+   * GET (URI) arguments.
+   */
+  MHD_GET_ARGUMENT_KIND = 8,
+
+  /**
+   * HTTP footer (only for HTTP 1.1 chunked encodings).
+   */
+  MHD_FOOTER_KIND = 16
+};
+
+
+/**
+ * The `enum MHD_RequestTerminationCode` specifies reasons
+ * why a request has been terminated (or completed).
+ * @ingroup request
+ */
+enum MHD_RequestTerminationCode
+{
+
+  /**
+   * We finished sending the response.
+   * @ingroup request
+   */
+  MHD_REQUEST_TERMINATED_COMPLETED_OK = 0,
+
+  /**
+   * Error handling the connection (resources
+   * exhausted, other side closed connection,
+   * application error accepting request, etc.)
+   * @ingroup request
+   */
+  MHD_REQUEST_TERMINATED_WITH_ERROR = 1,
+
+  /**
+   * No activity on the connection for the number
+   * of seconds specified using
+   * #MHD_OPTION_CONNECTION_TIMEOUT.
+   * @ingroup request
+   */
+  MHD_REQUEST_TERMINATED_TIMEOUT_REACHED = 2,
+
+  /**
+   * We had to close the session since MHD was being
+   * shut down.
+   * @ingroup request
+   */
+  MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN = 3,
+
+  /**
+   * We tried to read additional data, but the other side closed the
+   * connection.  This error is similar to
+   * #MHD_REQUEST_TERMINATED_WITH_ERROR, but specific to the case where
+   * the connection died because the other side did not send expected
+   * data.
+   * @ingroup request
+   */
+  MHD_REQUEST_TERMINATED_READ_ERROR = 4,
+
+  /**
+   * The client terminated the connection by closing the socket
+   * for writing (TCP half-closed); MHD aborted sending the
+   * response according to RFC 2616, section 8.1.4.
+   * @ingroup request
+   */
+  MHD_REQUEST_TERMINATED_CLIENT_ABORT = 5
+
+};
+
+
+/**
+ * The `enum MHD_ConnectionNotificationCode` specifies types
+ * of connection notifications.
+ * @ingroup request
+ */
+enum MHD_ConnectionNotificationCode
+{
+
+  /**
+   * A new connection has been started.
+   * @ingroup request
+   */
+  MHD_CONNECTION_NOTIFY_STARTED = 0,
+
+  /**
+   * A connection is closed.
+   * @ingroup request
+   */
+  MHD_CONNECTION_NOTIFY_CLOSED = 1
+
+};
+
+
+/**
+ * Information about a connection.
+ */
+union MHD_ConnectionInfo
+{
+
+  /**
+   * Cipher algorithm used, of type "enum gnutls_cipher_algorithm".
+   */
+  int /* enum gnutls_cipher_algorithm */ cipher_algorithm;
+
+  /**
+   * Protocol used, of type "enum gnutls_protocol".
+   */
+  int /* enum gnutls_protocol */ protocol;
+
+  /**
+   * Connect socket
+   */
+  MHD_socket connect_fd;
+
+  /**
+   * GNUtls session handle, of type "gnutls_session_t".
+   */
+  void * /* gnutls_session_t */ tls_session;
+
+  /**
+   * GNUtls client certificate handle, of type "gnutls_x509_crt_t".
+   */
+  void * /* gnutls_x509_crt_t */ client_cert;
+
+  /**
+   * Address information for the client.
+   */
+  struct sockaddr *client_addr;
+
+  /**
+   * Which daemon manages this connection (useful in case there are many
+   * daemons running).
+   */
+  struct MHD_Daemon *daemon;
+
+  /**
+   * Socket-specific client context.  Points to the same address as
+   * the "socket_context" of the #MHD_NotifyConnectionCallback.
+   */
+  void **socket_context;
+};
+
+
+/**
+ * Values of this enum are used to specify what
+ * information about a connection is desired.
+ * @ingroup request
+ */
+enum MHD_ConnectionInfoType
+{
+  /**
+   * What cipher algorithm is being used.
+   * Takes no extra arguments.
+   * @ingroup request
+   */
+  MHD_CONNECTION_INFO_CIPHER_ALGO,
+
+  /**
+   *
+   * Takes no extra arguments.
+   * @ingroup request
+   */
+  MHD_CONNECTION_INFO_PROTOCOL,
+
+  /**
+   * Obtain IP address of the client.  Takes no extra arguments.
+   * Returns essentially a `struct sockaddr **` (since the API returns
+   * a `union MHD_ConnectionInfo *` and that union contains a `struct
+   * sockaddr *`).
+   * @ingroup request
+   */
+  MHD_CONNECTION_INFO_CLIENT_ADDRESS,
+
+  /**
+   * Get the gnuTLS session handle.
+   * @ingroup request
+   */
+  MHD_CONNECTION_INFO_GNUTLS_SESSION,
+
+  /**
+   * Get the gnuTLS client certificate handle.  Dysfunctional (never
+   * implemented, deprecated).  Use #MHD_CONNECTION_INFO_GNUTLS_SESSION
+   * to get the `gnutls_session_t` and then call
+   * gnutls_certificate_get_peers().
+   */
+  MHD_CONNECTION_INFO_GNUTLS_CLIENT_CERT,
+
+  /**
+   * Get the `struct MHD_Daemon *` responsible for managing this connection.
+   * @ingroup request
+   */
+  MHD_CONNECTION_INFO_DAEMON,
+
+  /**
+   * Request the file descriptor for the listening socket.
+   * No extra arguments should be passed.
+   * @ingroup request
+   */
+  MHD_CONNECTION_INFO_CONNECTION_FD,
+
+  /**
+   * Returns the client-specific pointer to a `void *` that was (possibly)
+   * set during a #MHD_NotifyConnectionCallback when the socket was
+   * first accepted.  Note that this is NOT the same as the "con_cls"
+   * argument of the #MHD_AccessHandlerCallback.  The "con_cls" is
+   * fresh for each HTTP request, while the "socket_context" is fresh
+   * for each socket.
+   */
+  MHD_CONNECTION_INFO_SOCKET_CONTEXT
+
+};
+
+
+/**
+ * Values of this enum are used to specify what
+ * information about a deamon is desired.
+ */
+enum MHD_DaemonInfoType
+{
+  /**
+   * No longer supported (will return NULL).
+   */
+  MHD_DAEMON_INFO_KEY_SIZE,
+
+  /**
+   * No longer supported (will return NULL).
+   */
+  MHD_DAEMON_INFO_MAC_KEY_SIZE,
+
+  /**
+   * Request the file descriptor for the listening socket.
+   * No extra arguments should be passed.
+   */
+  MHD_DAEMON_INFO_LISTEN_FD,
+
+  /**
+   * Request the file descriptor for the external epoll.
+   * No extra arguments should be passed.
+   */
+  MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY,
+
+  /**
+   * Request the number of current connections handled by the daemon.
+   * No extra arguments should be passed.
+   */
+  MHD_DAEMON_INFO_CURRENT_CONNECTIONS
+};
+
+
+/**
+ * Callback for serious error condition. The default action is to print
+ * an error message and `abort()`.
+ *
+ * @param cls user specified value
+ * @param file where the error occured
+ * @param line where the error occured
+ * @param reason error detail, may be NULL
+ * @ingroup logging
+ */
+typedef void
+(*MHD_PanicCallback) (void *cls,
+                      const char *file,
+                      unsigned int line,
+                      const char *reason);
+
+/**
+ * Allow or deny a client to connect.
+ *
+ * @param cls closure
+ * @param addr address information from the client
+ * @param addrlen length of @a addr
+ * @return #MHD_YES if connection is allowed, #MHD_NO if not
+ */
+typedef int
+(*MHD_AcceptPolicyCallback) (void *cls,
+                             const struct sockaddr *addr,
+                             socklen_t addrlen);
+
+
+/**
+ * A client has requested the given url using the given method
+ * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
+ * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc).  The callback
+ * must call MHD callbacks to provide content to give back to the
+ * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
+ * #MHD_HTTP_NOT_FOUND, etc.).
+ *
+ * @param cls argument given together with the function
+ *        pointer when the handler was registered with MHD
+ * @param url the requested url
+ * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
+ *        #MHD_HTTP_METHOD_PUT, etc.)
+ * @param version the HTTP version string (i.e.
+ *        #MHD_HTTP_VERSION_1_1)
+ * @param upload_data the data being uploaded (excluding HEADERS,
+ *        for a POST that fits into memory and that is encoded
+ *        with a supported encoding, the POST data will NOT be
+ *        given in upload_data and is instead available as
+ *        part of #MHD_get_connection_values; very large POST
+ *        data *will* be made available incrementally in
+ *        @a upload_data)
+ * @param upload_data_size set initially to the size of the
+ *        @a upload_data provided; the method must update this
+ *        value to the number of bytes NOT processed;
+ * @param con_cls pointer that the callback can set to some
+ *        address and that will be preserved by MHD for future
+ *        calls for this request; since the access handler may
+ *        be called many times (i.e., for a PUT/POST operation
+ *        with plenty of upload data) this allows the application
+ *        to easily associate some request-specific state.
+ *        If necessary, this state can be cleaned up in the
+ *        global #MHD_RequestCompletedCallback (which
+ *        can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
+ *        Initially, `*con_cls` will be NULL.
+ * @return #MHD_YES if the connection was handled successfully,
+ *         #MHD_NO if the socket must be closed due to a serios
+ *         error while handling the request
+ */
+typedef int
+(*MHD_AccessHandlerCallback) (void *cls,
+                              struct MHD_Connection *connection,
+                              const char *url,
+                              const char *method,
+                              const char *version,
+                              const char *upload_data,
+                              size_t *upload_data_size,
+                              void **con_cls);
+
+
+/**
+ * Signature of the callback used by MHD to notify the
+ * application about completed requests.
+ *
+ * @param cls client-defined closure
+ * @param connection connection handle
+ * @param con_cls value as set by the last call to
+ *        the #MHD_AccessHandlerCallback
+ * @param toe reason for request termination
+ * @see #MHD_OPTION_NOTIFY_COMPLETED
+ * @ingroup request
+ */
+typedef void
+(*MHD_RequestCompletedCallback) (void *cls,
+                                 struct MHD_Connection *connection,
+                                 void **con_cls,
+                                 enum MHD_RequestTerminationCode toe);
+
+/**
+ * Signature of the callback used by MHD to notify the
+ * application about started/stopped connections
+ *
+ * @param cls client-defined closure
+ * @param connection connection handle
+ * @param socket_context socket-specific pointer where the
+ *                       client can associate some state specific
+ *                       to the TCP connection; note that this is
+ *                       different from the "con_cls" which is per
+ *                       HTTP request.  The client can initialize
+ *                       during #MHD_CONNECTION_NOTIFY_STARTED and
+ *                       cleanup during #MHD_CONNECTION_NOTIFY_CLOSED
+ *                       and access in the meantime using
+ *                       #MHD_CONNECTION_INFO_SOCKET_CONTEXT.
+ * @param toe reason for connection notification
+ * @see #MHD_OPTION_NOTIFY_CONNECTION
+ * @ingroup request
+ */
+typedef void
+(*MHD_NotifyConnectionCallback) (void *cls,
+                                 struct MHD_Connection *connection,
+                                 void **socket_context,
+                                 enum MHD_ConnectionNotificationCode toe);
+
+
+/**
+ * Iterator over key-value pairs.  This iterator
+ * can be used to iterate over all of the cookies,
+ * headers, or POST-data fields of a request, and
+ * also to iterate over the headers that have been
+ * added to a response.
+ *
+ * @param cls closure
+ * @param kind kind of the header we are looking at
+ * @param key key for the value, can be an empty string
+ * @param value corresponding value, can be NULL
+ * @return #MHD_YES to continue iterating,
+ *         #MHD_NO to abort the iteration
+ * @ingroup request
+ */
+typedef int
+(*MHD_KeyValueIterator) (void *cls,
+                         enum MHD_ValueKind kind,
+                         const char *key,
+                         const char *value);
+
+
+/**
+ * Callback used by libmicrohttpd in order to obtain content.  The
+ * callback is to copy at most @a max bytes of content into @a buf.  The
+ * total number of bytes that has been placed into @a buf should be
+ * returned.
+ *
+ * Note that returning zero will cause libmicrohttpd to try again.
+ * Thus, returning zero should only be used in conjunction
+ * with MHD_suspend_connection() to avoid busy waiting.
+ *
+ * @param cls extra argument to the callback
+ * @param pos position in the datastream to access;
+ *        note that if a `struct MHD_Response` object is re-used,
+ *        it is possible for the same content reader to
+ *        be queried multiple times for the same data;
+ *        however, if a `struct MHD_Response` is not re-used,
+ *        libmicrohttpd guarantees that "pos" will be
+ *        the sum of all non-negative return values
+ *        obtained from the content reader so far.
+ * @param buf where to copy the data
+ * @param max maximum number of bytes to copy to @a buf (size of @a buf)
+ * @return number of bytes written to @a buf;
+ *  0 is legal unless we are running in internal select mode (since
+ *    this would cause busy-waiting); 0 in external select mode
+ *    will cause this function to be called again once the external
+ *    select calls MHD again;
+ *  #MHD_CONTENT_READER_END_OF_STREAM (-1) for the regular
+ *    end of transmission (with chunked encoding, MHD will then
+ *    terminate the chunk and send any HTTP footers that might be
+ *    present; without chunked encoding and given an unknown
+ *    response size, MHD will simply close the connection; note
+ *    that while returning #MHD_CONTENT_READER_END_OF_STREAM is not technically
+ *    legal if a response size was specified, MHD accepts this
+ *    and treats it just as #MHD_CONTENT_READER_END_WITH_ERROR;
+ *  #MHD_CONTENT_READER_END_WITH_ERROR (-2) to indicate a server
+ *    error generating the response; this will cause MHD to simply
+ *    close the connection immediately.  If a response size was
+ *    given or if chunked encoding is in use, this will indicate
+ *    an error to the client.  Note, however, that if the client
+ *    does not know a response size and chunked encoding is not in
+ *    use, then clients will not be able to tell the difference between
+ *    #MHD_CONTENT_READER_END_WITH_ERROR and #MHD_CONTENT_READER_END_OF_STREAM.
+ *    This is not a limitation of MHD but rather of the HTTP protocol.
+ */
+typedef ssize_t
+(*MHD_ContentReaderCallback) (void *cls,
+                              uint64_t pos,
+                              char *buf,
+                              size_t max);
+
+
+/**
+ * This method is called by libmicrohttpd if we
+ * are done with a content reader.  It should
+ * be used to free resources associated with the
+ * content reader.
+ *
+ * @param cls closure
+ * @ingroup response
+ */
+typedef void
+(*MHD_ContentReaderFreeCallback) (void *cls);
+
+
+/**
+ * Iterator over key-value pairs where the value
+ * maybe made available in increments and/or may
+ * not be zero-terminated.  Used for processing
+ * POST data.
+ *
+ * @param cls user-specified closure
+ * @param kind type of the value, always #MHD_POSTDATA_KIND when called from MHD
+ * @param key 0-terminated key for the value
+ * @param filename name of the uploaded file, NULL if not known
+ * @param content_type mime-type of the data, NULL if not known
+ * @param transfer_encoding encoding of the data, NULL if not known
+ * @param data pointer to @a size bytes of data at the
+ *              specified offset
+ * @param off offset of data in the overall value
+ * @param size number of bytes in @a data available
+ * @return #MHD_YES to continue iterating,
+ *         #MHD_NO to abort the iteration
+ */
+typedef int
+(*MHD_PostDataIterator) (void *cls,
+                         enum MHD_ValueKind kind,
+                         const char *key,
+                         const char *filename,
+                         const char *content_type,
+                         const char *transfer_encoding,
+                         const char *data,
+                         uint64_t off,
+                         size_t size);
+
+/* **************** Daemon handling functions ***************** */
+
+/**
+ * Start a webserver on the given port.
+ *
+ * @param flags combination of `enum MHD_FLAG` values
+ * @param port port to bind to (in host byte order)
+ * @param apc callback to call to check which clients
+ *        will be allowed to connect; you can pass NULL
+ *        in which case connections from any IP will be
+ *        accepted
+ * @param apc_cls extra argument to apc
+ * @param dh handler called for all requests (repeatedly)
+ * @param dh_cls extra argument to @a dh
+ * @param ap list of options (type-value pairs,
+ *        terminated with #MHD_OPTION_END).
+ * @return NULL on error, handle to daemon on success
+ * @ingroup event
+ */
+_MHD_EXTERN struct MHD_Daemon *
+MHD_start_daemon_va (unsigned int flags,
+		     uint16_t port,
+		     MHD_AcceptPolicyCallback apc, void *apc_cls,
+		     MHD_AccessHandlerCallback dh, void *dh_cls,
+		     va_list ap);
+
+
+/**
+ * Start a webserver on the given port.  Variadic version of
+ * #MHD_start_daemon_va.
+ *
+ * @param flags combination of `enum MHD_FLAG` values
+ * @param port port to bind to
+ * @param apc callback to call to check which clients
+ *        will be allowed to connect; you can pass NULL
+ *        in which case connections from any IP will be
+ *        accepted
+ * @param apc_cls extra argument to apc
+ * @param dh handler called for all requests (repeatedly)
+ * @param dh_cls extra argument to @a dh
+ * @return NULL on error, handle to daemon on success
+ * @ingroup event
+ */
+_MHD_EXTERN struct MHD_Daemon *
+MHD_start_daemon (unsigned int flags,
+		  uint16_t port,
+		  MHD_AcceptPolicyCallback apc, void *apc_cls,
+		  MHD_AccessHandlerCallback dh, void *dh_cls,
+		  ...);
+
+
+/**
+ * Stop accepting connections from the listening socket.  Allows
+ * clients to continue processing, but stops accepting new
+ * connections.  Note that the caller is responsible for closing the
+ * returned socket; however, if MHD is run using threads (anything but
+ * external select mode), it must not be closed until AFTER
+ * #MHD_stop_daemon has been called (as it is theoretically possible
+ * that an existing thread is still using it).
+ *
+ * Note that some thread modes require the caller to have passed
+ * #MHD_USE_PIPE_FOR_SHUTDOWN when using this API.  If this daemon is
+ * in one of those modes and this option was not given to
+ * #MHD_start_daemon, this function will return #MHD_INVALID_SOCKET.
+ *
+ * @param daemon daemon to stop accepting new connections for
+ * @return old listen socket on success, #MHD_INVALID_SOCKET if
+ *         the daemon was already not listening anymore
+ * @ingroup specialized
+ */
+_MHD_EXTERN MHD_socket
+MHD_quiesce_daemon (struct MHD_Daemon *daemon);
+
+
+/**
+ * Shutdown an HTTP daemon.
+ *
+ * @param daemon daemon to stop
+ * @ingroup event
+ */
+_MHD_EXTERN void
+MHD_stop_daemon (struct MHD_Daemon *daemon);
+
+
+/**
+ * Add another client connection to the set of connections managed by
+ * MHD.  This API is usually not needed (since MHD will accept inbound
+ * connections on the server socket).  Use this API in special cases,
+ * for example if your HTTP server is behind NAT and needs to connect
+ * out to the HTTP client, or if you are building a proxy.
+ *
+ * If you use this API in conjunction with a internal select or a
+ * thread pool, you must set the option
+ * #MHD_USE_PIPE_FOR_SHUTDOWN to ensure that the freshly added
+ * connection is immediately processed by MHD.
+ *
+ * The given client socket will be managed (and closed!) by MHD after
+ * this call and must no longer be used directly by the application
+ * afterwards.
+ *
+ * Per-IP connection limits are ignored when using this API.
+ *
+ * @param daemon daemon that manages the connection
+ * @param client_socket socket to manage (MHD will expect
+ *        to receive an HTTP request from this socket next).
+ * @param addr IP address of the client
+ * @param addrlen number of bytes in @a addr
+ * @return #MHD_YES on success, #MHD_NO if this daemon could
+ *        not handle the connection (i.e. `malloc()` failed, etc).
+ *        The socket will be closed in any case; `errno` is
+ *        set to indicate further details about the error.
+ * @ingroup specialized
+ */
+_MHD_EXTERN int
+MHD_add_connection (struct MHD_Daemon *daemon,
+		    MHD_socket client_socket,
+		    const struct sockaddr *addr,
+		    socklen_t addrlen);
+
+
+/**
+ * Obtain the `select()` sets for this daemon.
+ * Daemon's FDs will be added to fd_sets. To get only
+ * daemon FDs in fd_sets, call FD_ZERO for each fd_set
+ * before calling this function. FD_SETSIZE is assumed
+ * to be platform's default.
+ *
+ * @param daemon daemon to get sets from
+ * @param read_fd_set read set
+ * @param write_fd_set write set
+ * @param except_fd_set except set
+ * @param max_fd increased to largest FD added (if larger
+ *               than existing value); can be NULL
+ * @return #MHD_YES on success, #MHD_NO if this
+ *         daemon was not started with the right
+ *         options for this call or any FD didn't
+ *         fit fd_set.
+ * @ingroup event
+ */
+_MHD_EXTERN int
+MHD_get_fdset (struct MHD_Daemon *daemon,
+               fd_set *read_fd_set,
+               fd_set *write_fd_set,
+	       fd_set *except_fd_set,
+	       MHD_socket *max_fd);
+
+
+/**
+ * Obtain the `select()` sets for this daemon.
+ * Daemon's FDs will be added to fd_sets. To get only
+ * daemon FDs in fd_sets, call FD_ZERO for each fd_set
+ * before calling this function. Passing custom FD_SETSIZE
+ * as @a fd_setsize allow usage of larger/smaller than
+ * platform's default fd_sets.
+ *
+ * @param daemon daemon to get sets from
+ * @param read_fd_set read set
+ * @param write_fd_set write set
+ * @param except_fd_set except set
+ * @param max_fd increased to largest FD added (if larger
+ *               than existing value); can be NULL
+ * @param fd_setsize value of FD_SETSIZE
+ * @return #MHD_YES on success, #MHD_NO if this
+ *         daemon was not started with the right
+ *         options for this call or any FD didn't
+ *         fit fd_set.
+ * @ingroup event
+ */
+_MHD_EXTERN int
+MHD_get_fdset2 (struct MHD_Daemon *daemon,
+               fd_set *read_fd_set,
+               fd_set *write_fd_set,
+               fd_set *except_fd_set,
+               MHD_socket *max_fd,
+               unsigned int fd_setsize);
+
+
+/**
+ * Obtain the `select()` sets for this daemon.
+ * Daemon's FDs will be added to fd_sets. To get only
+ * daemon FDs in fd_sets, call FD_ZERO for each fd_set
+ * before calling this function. Size of fd_set is
+ * determined by current value of FD_SETSIZE.
+ *
+ * @param daemon daemon to get sets from
+ * @param read_fd_set read set
+ * @param write_fd_set write set
+ * @param except_fd_set except set
+ * @param max_fd increased to largest FD added (if larger
+ *               than existing value); can be NULL
+ * @return #MHD_YES on success, #MHD_NO if this
+ *         daemon was not started with the right
+ *         options for this call or any FD didn't
+ *         fit fd_set.
+ * @ingroup event
+ */
+#define MHD_get_fdset(daemon,read_fd_set,write_fd_set,except_fd_set,max_fd) \
+  MHD_get_fdset2((daemon),(read_fd_set),(write_fd_set),(except_fd_set),(max_fd),FD_SETSIZE)
+
+
+/**
+ * Obtain timeout value for `select()` for this daemon (only needed if
+ * connection timeout is used).  The returned value is how many milliseconds
+ * `select()` or `poll()` should at most block, not the timeout value set for
+ * connections.  This function MUST NOT be called if MHD is running with
+ * #MHD_USE_THREAD_PER_CONNECTION.
+ *
+ * @param daemon daemon to query for timeout
+ * @param timeout set to the timeout (in milliseconds)
+ * @return #MHD_YES on success, #MHD_NO if timeouts are
+ *        not used (or no connections exist that would
+ *        necessiate the use of a timeout right now).
+ * @ingroup event
+ */
+_MHD_EXTERN int
+MHD_get_timeout (struct MHD_Daemon *daemon,
+		 MHD_UNSIGNED_LONG_LONG *timeout);
+
+
+/**
+ * Run webserver operations (without blocking unless in client
+ * callbacks).  This method should be called by clients in combination
+ * with #MHD_get_fdset if the client-controlled select method is used.
+ *
+ * This function is a convenience method, which is useful if the
+ * fd_sets from #MHD_get_fdset were not directly passed to `select()`;
+ * with this function, MHD will internally do the appropriate `select()`
+ * call itself again.  While it is always safe to call #MHD_run (in
+ * external select mode), you should call #MHD_run_from_select if
+ * performance is important (as it saves an expensive call to
+ * `select()`).
+ *
+ * @param daemon daemon to run
+ * @return #MHD_YES on success, #MHD_NO if this
+ *         daemon was not started with the right
+ *         options for this call.
+ * @ingroup event
+ */
+_MHD_EXTERN int
+MHD_run (struct MHD_Daemon *daemon);
+
+
+/**
+ * Run webserver operations. This method should be called by clients
+ * in combination with #MHD_get_fdset if the client-controlled select
+ * method is used.
+ *
+ * You can use this function instead of #MHD_run if you called
+ * `select()` on the result from #MHD_get_fdset.  File descriptors in
+ * the sets that are not controlled by MHD will be ignored.  Calling
+ * this function instead of #MHD_run is more efficient as MHD will
+ * not have to call `select()` again to determine which operations are
+ * ready.
+ *
+ * @param daemon daemon to run select loop for
+ * @param read_fd_set read set
+ * @param write_fd_set write set
+ * @param except_fd_set except set (not used, can be NULL)
+ * @return #MHD_NO on serious errors, #MHD_YES on success
+ * @ingroup event
+ */
+_MHD_EXTERN int
+MHD_run_from_select (struct MHD_Daemon *daemon,
+		     const fd_set *read_fd_set,
+		     const fd_set *write_fd_set,
+		     const fd_set *except_fd_set);
+
+
+
+
+/* **************** Connection handling functions ***************** */
+
+/**
+ * Get all of the headers from the request.
+ *
+ * @param connection connection to get values from
+ * @param kind types of values to iterate over
+ * @param iterator callback to call on each header;
+ *        maybe NULL (then just count headers)
+ * @param iterator_cls extra argument to @a iterator
+ * @return number of entries iterated over
+ * @ingroup request
+ */
+_MHD_EXTERN int
+MHD_get_connection_values (struct MHD_Connection *connection,
+                           enum MHD_ValueKind kind,
+                           MHD_KeyValueIterator iterator, void *iterator_cls);
+
+
+/**
+ * This function can be used to add an entry to the HTTP headers of a
+ * connection (so that the #MHD_get_connection_values function will
+ * return them -- and the `struct MHD_PostProcessor` will also see
+ * them).  This maybe required in certain situations (see Mantis
+ * #1399) where (broken) HTTP implementations fail to supply values
+ * needed by the post processor (or other parts of the application).
+ *
+ * This function MUST only be called from within the
+ * #MHD_AccessHandlerCallback (otherwise, access maybe improperly
+ * synchronized).  Furthermore, the client must guarantee that the key
+ * and value arguments are 0-terminated strings that are NOT freed
+ * until the connection is closed.  (The easiest way to do this is by
+ * passing only arguments to permanently allocated strings.).
+ *
+ * @param connection the connection for which a
+ *  value should be set
+ * @param kind kind of the value
+ * @param key key for the value
+ * @param value the value itself
+ * @return #MHD_NO if the operation could not be
+ *         performed due to insufficient memory;
+ *         #MHD_YES on success
+ * @ingroup request
+ */
+_MHD_EXTERN int
+MHD_set_connection_value (struct MHD_Connection *connection,
+                          enum MHD_ValueKind kind,
+                          const char *key,
+			  const char *value);
+
+
+/**
+ * Sets the global error handler to a different implementation.  @a cb
+ * will only be called in the case of typically fatal, serious
+ * internal consistency issues.  These issues should only arise in the
+ * case of serious memory corruption or similar problems with the
+ * architecture.  While @a cb is allowed to return and MHD will then
+ * try to continue, this is never safe.
+ *
+ * The default implementation that is used if no panic function is set
+ * simply prints an error message and calls `abort()`.  Alternative
+ * implementations might call `exit()` or other similar functions.
+ *
+ * @param cb new error handler
+ * @param cls passed to @a cb
+ * @ingroup logging
+ */
+_MHD_EXTERN void
+MHD_set_panic_func (MHD_PanicCallback cb, void *cls);
+
+
+/**
+ * Process escape sequences ('%HH') Updates val in place; the
+ * result should be UTF-8 encoded and cannot be larger than the input.
+ * The result must also still be 0-terminated.
+ *
+ * @param val value to unescape (modified in the process)
+ * @return length of the resulting val (`strlen(val)` may be
+ *  shorter afterwards due to elimination of escape sequences)
+ */
+_MHD_EXTERN size_t
+MHD_http_unescape (char *val);
+
+
+/**
+ * Get a particular header value.  If multiple
+ * values match the kind, return any one of them.
+ *
+ * @param connection connection to get values from
+ * @param kind what kind of value are we looking for
+ * @param key the header to look for, NULL to lookup 'trailing' value without a key
+ * @return NULL if no such item was found
+ * @ingroup request
+ */
+_MHD_EXTERN const char *
+MHD_lookup_connection_value (struct MHD_Connection *connection,
+			     enum MHD_ValueKind kind,
+			     const char *key);
+
+
+/**
+ * Queue a response to be transmitted to the client (as soon as
+ * possible but after #MHD_AccessHandlerCallback returns).
+ *
+ * @param connection the connection identifying the client
+ * @param status_code HTTP status code (i.e. #MHD_HTTP_OK)
+ * @param response response to transmit
+ * @return #MHD_NO on error (i.e. reply already sent),
+ *         #MHD_YES on success or if message has been queued
+ * @ingroup response
+ */
+_MHD_EXTERN int
+MHD_queue_response (struct MHD_Connection *connection,
+                    unsigned int status_code,
+		    struct MHD_Response *response);
+
+
+/**
+ * Suspend handling of network data for a given connection.  This can
+ * be used to dequeue a connection from MHD's event loop (external
+ * select, internal select or thread pool; not applicable to
+ * thread-per-connection!) for a while.
+ *
+ * If you use this API in conjunction with a internal select or a
+ * thread pool, you must set the option #MHD_USE_PIPE_FOR_SHUTDOWN to
+ * ensure that a resumed connection is immediately processed by MHD.
+ *
+ * Suspended connections continue to count against the total number of
+ * connections allowed (per daemon, as well as per IP, if such limits
+ * are set).  Suspended connections will NOT time out; timeouts will
+ * restart when the connection handling is resumed.  While a
+ * connection is suspended, MHD will not detect disconnects by the
+ * client.
+ *
+ * The only safe time to suspend a connection is from the
+ * #MHD_AccessHandlerCallback.
+ *
+ * Finally, it is an API violation to call #MHD_stop_daemon while
+ * having suspended connections (this will at least create memory and
+ * socket leaks or lead to undefined behavior).  You must explicitly
+ * resume all connections before stopping the daemon.
+ *
+ * @param connection the connection to suspend
+ */
+_MHD_EXTERN void
+MHD_suspend_connection (struct MHD_Connection *connection);
+
+
+/**
+ * Resume handling of network data for suspended connection.  It is
+ * safe to resume a suspended connection at any time.  Calling this
+ * function on a connection that was not previously suspended will
+ * result in undefined behavior.
+ *
+ * @param connection the connection to resume
+ */
+_MHD_EXTERN void
+MHD_resume_connection (struct MHD_Connection *connection);
+
+
+/* **************** Response manipulation functions ***************** */
+
+
+/**
+ * Flags for special handling of responses.
+ */
+enum MHD_ResponseFlags
+{
+  /**
+   * Default: no special flags.
+   */
+  MHD_RF_NONE = 0,
+
+  /**
+   * Only respond in conservative HTTP 1.0-mode.   In particular,
+   * do not (automatically) sent "Connection" headers and always
+   * close the connection after generating the response.
+   */
+  MHD_RF_HTTP_VERSION_1_0_ONLY = 1
+
+};
+
+
+/**
+ * MHD options (for future extensions).
+ */
+enum MHD_ResponseOptions
+{
+  /**
+   * End of the list of options.
+   */
+  MHD_RO_END = 0
+};
+
+
+/**
+ * Set special flags and options for a response.
+ *
+ * @param response the response to modify
+ * @param flags to set for the response
+ * @param ... #MHD_RO_END terminated list of options
+ * @return #MHD_YES on success, #MHD_NO on error
+ */
+_MHD_EXTERN int
+MHD_set_response_options (struct MHD_Response *response,
+                          enum MHD_ResponseFlags flags,
+                          ...);
+
+
+/**
+ * Create a response object.  The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the data portion of the response, #MHD_SIZE_UNKNOWN for unknown
+ * @param block_size preferred block size for querying crc (advisory only,
+ *                   MHD may still call @a crc using smaller chunks); this
+ *                   is essentially the buffer size used for IO, clients
+ *                   should pick a value that is appropriate for IO and
+ *                   memory performance requirements
+ * @param crc callback to use to obtain response data
+ * @param crc_cls extra argument to @a crc
+ * @param crfc callback to call to free @a crc_cls resources
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @ingroup response
+ */
+_MHD_EXTERN struct MHD_Response *
+MHD_create_response_from_callback (uint64_t size,
+				   size_t block_size,
+				   MHD_ContentReaderCallback crc, void *crc_cls,
+				   MHD_ContentReaderFreeCallback crfc);
+
+
+/**
+ * Create a response object.  The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the @a data portion of the response
+ * @param data the data itself
+ * @param must_free libmicrohttpd should free data when done
+ * @param must_copy libmicrohttpd must make a copy of @a data
+ *        right away, the data maybe released anytime after
+ *        this call returns
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @deprecated use #MHD_create_response_from_buffer instead
+ * @ingroup response
+ */
+_MHD_EXTERN struct MHD_Response *
+MHD_create_response_from_data (size_t size,
+			       void *data,
+			       int must_free,
+			       int must_copy);
+
+
+/**
+ * Specification for how MHD should treat the memory buffer
+ * given for the response.
+ * @ingroup response
+ */
+enum MHD_ResponseMemoryMode
+{
+
+  /**
+   * Buffer is a persistent (static/global) buffer that won't change
+   * for at least the lifetime of the response, MHD should just use
+   * it, not free it, not copy it, just keep an alias to it.
+   * @ingroup response
+   */
+  MHD_RESPMEM_PERSISTENT,
+
+  /**
+   * Buffer is heap-allocated with `malloc()` (or equivalent) and
+   * should be freed by MHD after processing the response has
+   * concluded (response reference counter reaches zero).
+   * @ingroup response
+   */
+  MHD_RESPMEM_MUST_FREE,
+
+  /**
+   * Buffer is in transient memory, but not on the heap (for example,
+   * on the stack or non-`malloc()` allocated) and only valid during the
+   * call to #MHD_create_response_from_buffer.  MHD must make its
+   * own private copy of the data for processing.
+   * @ingroup response
+   */
+  MHD_RESPMEM_MUST_COPY
+
+};
+
+
+/**
+ * Create a response object.  The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the data portion of the response
+ * @param buffer size bytes containing the response's data portion
+ * @param mode flags for buffer management
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @ingroup response
+ */
+_MHD_EXTERN struct MHD_Response *
+MHD_create_response_from_buffer (size_t size,
+				 void *buffer,
+				 enum MHD_ResponseMemoryMode mode);
+
+
+/**
+ * Create a response object.  The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the data portion of the response
+ * @param fd file descriptor referring to a file on disk with the
+ *        data; will be closed when response is destroyed;
+ *        fd should be in 'blocking' mode
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @ingroup response
+ */
+_MHD_EXTERN struct MHD_Response *
+MHD_create_response_from_fd (size_t size,
+			     int fd);
+
+
+/**
+ * Create a response object.  The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the data portion of the response
+ * @param fd file descriptor referring to a file on disk with the
+ *        data; will be closed when response is destroyed;
+ *        fd should be in 'blocking' mode
+ * @param offset offset to start reading from in the file;
+ *        Be careful! `off_t` may have been compiled to be a
+ *        64-bit variable for MHD, in which case your application
+ *        also has to be compiled using the same options! Read
+ *        the MHD manual for more details.
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @ingroup response
+ */
+_MHD_EXTERN struct MHD_Response *
+MHD_create_response_from_fd_at_offset (size_t size,
+				       int fd,
+				       off_t offset);
+
+
+#if 0
+/**
+ * Enumeration for actions MHD should perform on the underlying socket
+ * of the upgrade.  This API is not finalized, and in particular
+ * the final set of actions is yet to be decided. This is just an
+ * idea for what we might want.
+ */
+enum MHD_UpgradeAction
+{
+
+  /**
+   * Close the socket, the application is done with it.
+   *
+   * Takes no extra arguments.
+   *
+   * NOTE: it is unclear if we want to have this in the
+   * "final" API, this is all just ideas.
+   */
+  MHD_UPGRADE_ACTION_CLOSE = 0,
+
+  /**
+   * Uncork the TCP write buffer (that is, tell the OS to transmit all
+   * bytes in the buffer now, and to not use TCP-CORKing).
+   *
+   * Takes no extra arguments.
+   *
+   * NOTE: it is unclear if we want to have this in the
+   * "final" API, this is all just ideas.
+   */
+  MHD_UPGRADE_ACTION_CORK
+
+};
+
+
+/**
+ * This connection-specific callback is provided by MHD to
+ * applications (unusual) during the #MHD_UpgradeHandler.
+ * It allows applications to perform 'special' actions on
+ * the underlying socket from the upgrade.
+ *
+ * @param cls the closure (from `upgrade_action_cls`)
+ * @param action which action should be performed
+ * @param ... arguments to the action (depends on the action)
+ * @return #MHD_NO on error, #MHD_YES on success
+ */
+typedef int
+(*MHD_UpgradeActionCallback)(void *cls,
+                             enum MHD_UpgradeAction action,
+                             ...);
+
+/**
+ * Function called after a protocol "upgrade" response was sent
+ * successfully and the socket should now be controlled by some
+ * protocol other than HTTP.
+ *
+ * Any data received on the socket will be made available in
+ * 'data_in'.  The function should update 'data_in_size' to
+ * reflect the number of bytes consumed from 'data_in' (the remaining
+ * bytes will be made available in the next call to the handler).
+ *
+ * Any data that should be transmitted on the socket should be
+ * stored in 'data_out'.  '*data_out_size' is initially set to
+ * the available buffer space in 'data_out'.  It should be set to
+ * the number of bytes stored in 'data_out' (which can be zero).
+ *
+ * The return value is a BITMASK that indicates how the function
+ * intends to interact with the event loop.  It can request to be
+ * notified for reading, writing, request to UNCORK the send buffer
+ * (which MHD is allowed to ignore, if it is not possible to uncork on
+ * the local platform), to wait for the 'external' select loop to
+ * trigger another round.  It is also possible to specify "no events"
+ * to terminate the connection; in this case, the
+ * #MHD_RequestCompletedCallback will be called and all resources of
+ * the connection will be released.
+ *
+ * Except when in 'thread-per-connection' mode, implementations
+ * of this function should never block (as it will still be called
+ * from within the main event loop).
+ *
+ * @param cls closure
+ * @param connection original HTTP connection handle,
+ *                   giving the function a last chance
+ *                   to inspect the original HTTP request
+ * @param sock socket to use for bi-directional communication
+ *        with the client.  For HTTPS, this may not be a socket
+ *        that is directly connected to the client and thus certain
+ *        operations (TCP-specific setsockopt(), getsockopt(), etc.)
+ *        may not work as expected (as the socket could be from a
+ *        socketpair() or a TCP-loopback)
+ * @param upgrade_action function that can be used to perform actions
+ *        on the @a sock (like those that cannot be done explicitly).
+ *        Applications must use this callback to perform the
+ *        close() action on the @a sock.
+ * @param upgrade_action_cls closure that must be passed to @a upgrade_action
+ */
+typedef void
+(*MHD_UpgradeHandler)(void *cls,
+                      struct MHD_Connection *connection,
+                      MHD_SOCKET sock,
+                      MHD_UpgradeActionCallback upgrade_action,
+                      void *upgrade_action_cls);
+
+
+/**
+ * Create a response object that can be used for 101 UPGRADE
+ * responses, for example to implement WebSockets.  After sending the
+ * response, control over the data stream is given to the callback (which
+ * can then, for example, start some bi-directional communication).
+ * If the response is queued for multiple connections, the callback
+ * will be called for each connection.  The callback
+ * will ONLY be called after the response header was successfully passed
+ * to the OS; if there are communication errors before, the usual MHD
+ * connection error handling code will be performed.
+ *
+ * Setting the correct HTTP code (i.e. MHD_HTTP_SWITCHING_PROTOCOLS)
+ * and setting correct HTTP headers for the upgrade must be done
+ * manually (this way, it is possible to implement most existing
+ * WebSocket versions using this API; in fact, this API might be useful
+ * for any protocol switch, not just WebSockets).  Note that
+ * draft-ietf-hybi-thewebsocketprotocol-00 cannot be implemented this
+ * way as the header "HTTP/1.1 101 WebSocket Protocol Handshake"
+ * cannot be generated; instead, MHD will always produce "HTTP/1.1 101
+ * Switching Protocols" (if the response code 101 is used).
+ *
+ * As usual, the response object can be extended with header
+ * information and then be used any number of times (as long as the
+ * header information is not connection-specific).
+ *
+ * @param upgrade_handler function to call with the 'upgraded' socket
+ * @param upgrade_handler_cls closure for @a upgrade_handler
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ */
+struct MHD_Response *
+MHD_create_response_for_upgrade (MHD_UpgradeHandler upgrade_handler,
+				 void *upgrade_handler_cls);
+#endif
+
+/**
+ * Destroy a response object and associated resources.  Note that
+ * libmicrohttpd may keep some of the resources around if the response
+ * is still in the queue for some clients, so the memory may not
+ * necessarily be freed immediatley.
+ *
+ * @param response response to destroy
+ * @ingroup response
+ */
+_MHD_EXTERN void
+MHD_destroy_response (struct MHD_Response *response);
+
+
+/**
+ * Add a header line to the response.
+ *
+ * @param response response to add a header to
+ * @param header the header to add
+ * @param content value to add
+ * @return #MHD_NO on error (i.e. invalid header or content format),
+ *         or out of memory
+ * @ingroup response
+ */
+_MHD_EXTERN int
+MHD_add_response_header (struct MHD_Response *response,
+                         const char *header,
+			 const char *content);
+
+
+/**
+ * Add a footer line to the response.
+ *
+ * @param response response to remove a header from
+ * @param footer the footer to delete
+ * @param content value to delete
+ * @return #MHD_NO on error (i.e. invalid footer or content format).
+ * @ingroup response
+ */
+_MHD_EXTERN int
+MHD_add_response_footer (struct MHD_Response *response,
+                         const char *footer,
+			 const char *content);
+
+
+/**
+ * Delete a header (or footer) line from the response.
+ *
+ * @param response response to remove a header from
+ * @param header the header to delete
+ * @param content value to delete
+ * @return #MHD_NO on error (no such header known)
+ * @ingroup response
+ */
+_MHD_EXTERN int
+MHD_del_response_header (struct MHD_Response *response,
+                         const char *header,
+			 const char *content);
+
+
+/**
+ * Get all of the headers (and footers) added to a response.
+ *
+ * @param response response to query
+ * @param iterator callback to call on each header;
+ *        maybe NULL (then just count headers)
+ * @param iterator_cls extra argument to @a iterator
+ * @return number of entries iterated over
+ * @ingroup response
+ */
+_MHD_EXTERN int
+MHD_get_response_headers (struct MHD_Response *response,
+                          MHD_KeyValueIterator iterator, void *iterator_cls);
+
+
+/**
+ * Get a particular header (or footer) from the response.
+ *
+ * @param response response to query
+ * @param key which header to get
+ * @return NULL if header does not exist
+ * @ingroup response
+ */
+_MHD_EXTERN const char *
+MHD_get_response_header (struct MHD_Response *response,
+			 const char *key);
+
+
+/* ********************** PostProcessor functions ********************** */
+
+/**
+ * Create a `struct MHD_PostProcessor`.
+ *
+ * A `struct MHD_PostProcessor` can be used to (incrementally) parse
+ * the data portion of a POST request.  Note that some buggy browsers
+ * fail to set the encoding type.  If you want to support those, you
+ * may have to call #MHD_set_connection_value with the proper encoding
+ * type before creating a post processor (if no supported encoding
+ * type is set, this function will fail).
+ *
+ * @param connection the connection on which the POST is
+ *        happening (used to determine the POST format)
+ * @param buffer_size maximum number of bytes to use for
+ *        internal buffering (used only for the parsing,
+ *        specifically the parsing of the keys).  A
+ *        tiny value (256-1024) should be sufficient.
+ *        Do NOT use a value smaller than 256.  For good
+ *        performance, use 32 or 64k (i.e. 65536).
+ * @param iter iterator to be called with the parsed data,
+ *        Must NOT be NULL.
+ * @param iter_cls first argument to @a iter
+ * @return NULL on error (out of memory, unsupported encoding),
+ *         otherwise a PP handle
+ * @ingroup request
+ */
+_MHD_EXTERN struct MHD_PostProcessor *
+MHD_create_post_processor (struct MHD_Connection *connection,
+			   size_t buffer_size,
+			   MHD_PostDataIterator iter, void *iter_cls);
+
+
+/**
+ * Parse and process POST data.  Call this function when POST data is
+ * available (usually during an #MHD_AccessHandlerCallback) with the
+ * "upload_data" and "upload_data_size".  Whenever possible, this will
+ * then cause calls to the #MHD_PostDataIterator.
+ *
+ * @param pp the post processor
+ * @param post_data @a post_data_len bytes of POST data
+ * @param post_data_len length of @a post_data
+ * @return #MHD_YES on success, #MHD_NO on error
+ *         (out-of-memory, iterator aborted, parse error)
+ * @ingroup request
+ */
+_MHD_EXTERN int
+MHD_post_process (struct MHD_PostProcessor *pp,
+                  const char *post_data, size_t post_data_len);
+
+
+/**
+ * Release PostProcessor resources.
+ *
+ * @param pp the PostProcessor to destroy
+ * @return #MHD_YES if processing completed nicely,
+ *         #MHD_NO if there were spurious characters / formatting
+ *                problems; it is common to ignore the return
+ *                value of this function
+ * @ingroup request
+ */
+_MHD_EXTERN int
+MHD_destroy_post_processor (struct MHD_PostProcessor *pp);
+
+
+/* ********************* Digest Authentication functions *************** */
+
+
+/**
+ * Constant to indicate that the nonce of the provided
+ * authentication code was wrong.
+ * @ingroup authentication
+ */
+#define MHD_INVALID_NONCE -1
+
+
+/**
+ * Get the username from the authorization header sent by the client
+ *
+ * @param connection The MHD connection structure
+ * @return NULL if no username could be found, a pointer
+ * 			to the username if found
+ * @ingroup authentication
+ */
+_MHD_EXTERN char *
+MHD_digest_auth_get_username (struct MHD_Connection *connection);
+
+
+/**
+ * Authenticates the authorization header sent by the client
+ *
+ * @param connection The MHD connection structure
+ * @param realm The realm presented to the client
+ * @param username The username needs to be authenticated
+ * @param password The password used in the authentication
+ * @param nonce_timeout The amount of time for a nonce to be
+ * 			invalid in seconds
+ * @return #MHD_YES if authenticated, #MHD_NO if not,
+ * 			#MHD_INVALID_NONCE if nonce is invalid
+ * @ingroup authentication
+ */
+_MHD_EXTERN int
+MHD_digest_auth_check (struct MHD_Connection *connection,
+		       const char *realm,
+		       const char *username,
+		       const char *password,
+		       unsigned int nonce_timeout);
+
+
+/**
+ * Queues a response to request authentication from the client
+ *
+ * @param connection The MHD connection structure
+ * @param realm The realm presented to the client
+ * @param opaque string to user for opaque value
+ * @param response reply to send; should contain the "access denied"
+ *        body; note that this function will set the "WWW Authenticate"
+ *        header and that the caller should not do this
+ * @param signal_stale #MHD_YES if the nonce is invalid to add
+ * 			'stale=true' to the authentication header
+ * @return #MHD_YES on success, #MHD_NO otherwise
+ * @ingroup authentication
+ */
+_MHD_EXTERN int
+MHD_queue_auth_fail_response (struct MHD_Connection *connection,
+			      const char *realm,
+			      const char *opaque,
+			      struct MHD_Response *response,
+			      int signal_stale);
+
+
+/**
+ * Get the username and password from the basic authorization header sent by the client
+ *
+ * @param connection The MHD connection structure
+ * @param password a pointer for the password
+ * @return NULL if no username could be found, a pointer
+ * 			to the username if found
+ * @ingroup authentication
+ */
+_MHD_EXTERN char *
+MHD_basic_auth_get_username_password (struct MHD_Connection *connection,
+				      char** password);
+
+
+/**
+ * Queues a response to request basic authentication from the client
+ * The given response object is expected to include the payload for
+ * the response; the "WWW-Authenticate" header will be added and the
+ * response queued with the 'UNAUTHORIZED' status code.
+ *
+ * @param connection The MHD connection structure
+ * @param realm the realm presented to the client
+ * @param response response object to modify and queue
+ * @return #MHD_YES on success, #MHD_NO otherwise
+ * @ingroup authentication
+ */
+_MHD_EXTERN int
+MHD_queue_basic_auth_fail_response (struct MHD_Connection *connection,
+				    const char *realm,
+				    struct MHD_Response *response);
+
+/* ********************** generic query functions ********************** */
+
+
+/**
+ * Obtain information about the given connection.
+ *
+ * @param connection what connection to get information about
+ * @param info_type what information is desired?
+ * @param ... depends on @a info_type
+ * @return NULL if this information is not available
+ *         (or if the @a info_type is unknown)
+ * @ingroup specialized
+ */
+_MHD_EXTERN const union MHD_ConnectionInfo *
+MHD_get_connection_info (struct MHD_Connection *connection,
+			 enum MHD_ConnectionInfoType info_type,
+			 ...);
+
+
+/**
+ * MHD connection options.  Given to #MHD_set_connection_option to
+ * set custom options for a particular connection.
+ */
+enum MHD_CONNECTION_OPTION
+{
+
+  /**
+   * Set a custom timeout for the given connection.  Specified
+   * as the number of seconds, given as an `unsigned int`.  Use
+   * zero for no timeout.
+   */
+  MHD_CONNECTION_OPTION_TIMEOUT
+
+};
+
+
+/**
+ * Set a custom option for the given connection, overriding defaults.
+ *
+ * @param connection connection to modify
+ * @param option option to set
+ * @param ... arguments to the option, depending on the option type
+ * @return #MHD_YES on success, #MHD_NO if setting the option failed
+ * @ingroup specialized
+ */
+_MHD_EXTERN int
+MHD_set_connection_option (struct MHD_Connection *connection,
+			   enum MHD_CONNECTION_OPTION option,
+			   ...);
+
+
+/**
+ * Information about an MHD daemon.
+ */
+union MHD_DaemonInfo
+{
+  /**
+   * Size of the key, no longer supported.
+   * @deprecated
+   */
+  size_t key_size;
+
+  /**
+   * Size of the mac key, no longer supported.
+   * @deprecated
+   */
+  size_t mac_key_size;
+
+  /**
+   * Listen socket file descriptor, for #MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY
+   * and #MHD_DAEMON_INFO_LISTEN_FD.
+   */
+  MHD_socket listen_fd;
+
+  /**
+   * Number of active connections, for #MHD_DAEMON_INFO_CURRENT_CONNECTIONS.
+   */
+  unsigned int num_connections;
+};
+
+
+/**
+ * Obtain information about the given daemon
+ * (not fully implemented!).
+ *
+ * @param daemon what daemon to get information about
+ * @param info_type what information is desired?
+ * @param ... depends on @a info_type
+ * @return NULL if this information is not available
+ *         (or if the @a info_type is unknown)
+ * @ingroup specialized
+ */
+_MHD_EXTERN const union MHD_DaemonInfo *
+MHD_get_daemon_info (struct MHD_Daemon *daemon,
+		     enum MHD_DaemonInfoType info_type,
+		     ...);
+
+
+/**
+ * Obtain the version of this library
+ *
+ * @return static version string, e.g. "0.9.9"
+ * @ingroup specialized
+ */
+_MHD_EXTERN const char*
+MHD_get_version (void);
+
+
+/**
+ * Types of information about MHD features,
+ * used by #MHD_is_feature_supported().
+ */
+enum MHD_FEATURE
+{
+  /**
+   * Get whether messages are supported. If supported then in debug
+   * mode messages can be printed to stderr or to external logger.
+   */
+  MHD_FEATURE_MESSGES = 1,
+
+  /**
+   * Get whether HTTPS is supported.  If supported then flag
+   * #MHD_USE_SSL and options #MHD_OPTION_HTTPS_MEM_KEY,
+   * #MHD_OPTION_HTTPS_MEM_CERT, #MHD_OPTION_HTTPS_MEM_TRUST,
+   * #MHD_OPTION_HTTPS_MEM_DHPARAMS, #MHD_OPTION_HTTPS_CRED_TYPE,
+   * #MHD_OPTION_HTTPS_PRIORITIES can be used.
+   */
+  MHD_FEATURE_SSL = 2,
+
+  /**
+   * Get whether option #MHD_OPTION_HTTPS_CERT_CALLBACK is
+   * supported.
+   */
+  MHD_FEATURE_HTTPS_CERT_CALLBACK = 3,
+
+  /**
+   * Get whether IPv6 is supported. If supported then flag
+   * #MHD_USE_IPv6 can be used.
+   */
+  MHD_FEATURE_IPv6 = 4,
+
+  /**
+   * Get whether IPv6 without IPv4 is supported. If not supported
+   * then IPv4 is always enabled in IPv6 sockets and
+   * flag #MHD_USE_DUAL_STACK if always used when #MHD_USE_IPv6 is
+   * specified.
+   */
+  MHD_FEATURE_IPv6_ONLY = 5,
+
+  /**
+   * Get whether `poll()` is supported. If supported then flag
+   * #MHD_USE_POLL can be used.
+   */
+  MHD_FEATURE_POLL = 6,
+
+  /**
+   * Get whether `epoll()` is supported. If supported then Flags
+   * #MHD_USE_EPOLL_LINUX_ONLY and
+   * #MHD_USE_EPOLL_INTERNALLY_LINUX_ONLY can be used.
+   */
+  MHD_FEATURE_EPOLL = 7,
+
+  /**
+   * Get whether shutdown on listen socket to signal other
+   * threads is supported. If not supported flag
+   * #MHD_USE_PIPE_FOR_SHUTDOWN is automatically forced.
+   */
+  MHD_FEATURE_SHUTDOWN_LISTEN_SOCKET = 8,
+
+  /**
+   * Get whether socketpair is used internally instead of pipe to
+   * signal other threads.
+   */
+  MHD_FEATURE_SOCKETPAIR = 9,
+
+  /**
+   * Get whether TCP Fast Open is supported. If supported then
+   * flag #MHD_USE_TCP_FASTOPEN and option
+   * #MHD_OPTION_TCP_FASTOPEN_QUEUE_SIZE can be used.
+   */
+  MHD_FEATURE_TCP_FASTOPEN = 10,
+
+  /**
+   * Get whether HTTP Basic authorization is supported. If supported
+   * then functions #MHD_basic_auth_get_username_password and
+   * #MHD_queue_basic_auth_fail_response can be used.
+   */
+  MHD_FEATURE_BASIC_AUTH = 11,
+
+  /**
+   * Get whether HTTP Digest authorization is supported. If
+   * supported then options #MHD_OPTION_DIGEST_AUTH_RANDOM,
+   * #MHD_OPTION_NONCE_NC_SIZE and
+   * #MHD_digest_auth_check() can be used.
+   */
+  MHD_FEATURE_DIGEST_AUTH = 12,
+
+  /**
+   * Get whether postprocessor is supported. If supported then
+   * functions #MHD_create_post_processor(), #MHD_post_process() and
+   * #MHD_destroy_post_processor() can
+   * be used.
+   */
+  MHD_FEATURE_POSTPROCESSOR = 13,
+
+  /**
+  * Get whether password encrypted private key for HTTPS daemon is
+  * supported. If supported then option
+  * ::MHD_OPTION_HTTPS_KEY_PASSWORD can be used.
+  */
+  MHD_FEATURE_HTTPS_KEY_PASSWORD = 14
+};
+
+
+/**
+ * Get information about supported MHD features.
+ * Indicate that MHD was compiled with or without support for
+ * particular feature. Some features require additional support
+ * by kernel. Kernel support is not checked by this function.
+ *
+ * @param feature type of requested information
+ * @return #MHD_YES if feature is supported by MHD, #MHD_NO if
+ * feature is not supported or feature is unknown.
+ * @ingroup specialized
+ */
+_MHD_EXTERN int
+MHD_is_feature_supported(enum MHD_FEATURE feature);
+
+
+#if 0                           /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/include/microspdy.h b/src/include/microspdy.h
new file mode 100644
index 0000000..7da5fbe
--- /dev/null
+++ b/src/include/microspdy.h
@@ -0,0 +1,1380 @@
+/*

+    This file is part of libmicrospdy

+    Copyright Copyright (C) 2012, 2013 Christian Grothoff

+

+    This program is free software: you can redistribute it and/or modify

+    it under the terms of the GNU General Public License as published by

+    the Free Software Foundation, either version 3 of the License, or

+    (at your option) any later version.

+

+    This program is distributed in the hope that it will be useful,

+    but WITHOUT ANY WARRANTY; without even the implied warranty of

+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+    GNU General Public License for more details.

+

+    You should have received a copy of the GNU General Public License

+    along with this program.  If not, see <http://www.gnu.org/licenses/>.

+*/

+

+/**

+ * @file microspdy.h

+ * @brief public interface to libmicrospdy

+ * @author Andrey Uzunov

+ * @author Christian Grothoff

+ *

+ * All symbols defined in this header start with SPDY_.  libmisrospdy is a small

+ * SPDY daemon library. The application can start multiple daemons

+ * and they are independent.<p>

+ *

+ * The header file defines various constants used by the SPDY and the HTTP protocol.

+ * This does not mean that the lib actually interprets all of these

+ * values. Not everything is implemented. The provided constants are exported as a convenience

+ * for users of the library.  The lib does not verify that provided

+ * HTTP headers and if their values conform to the SPDY protocol,

+ * it only checks if the required headers for the SPDY requests and

+ * responses are provided.<p>

+ *

+ * The library uses just a single thread.<p>

+ *

+ * Before including "microspdy.h" you should add the necessary

+ * includes to define the types used in this file (which headers are needed may

+ * depend on your platform; for possible suggestions consult

+ * "platform.h" in the libmicrospdy distribution).<p>

+ *

+ * All of the functions returning SPDY_YES/SPDY_NO return

+ * SPDY_INPUT_ERROR when any of the parameters are invalid, e.g.

+ * required parameter is NULL.<p>

+ *

+ * The library does not check if anything at the application layer --

+ * requests and responses -- is correct. For example, it

+ * is up to the user to check if a client is sending HTTP body but the

+ * method is GET.<p>

+ *

+ * The SPDY flow control is just partially implemented: the receiving

+ * window is updated, and the client is notified, to prevent a client

+ * from stop sending POST body data, for example.

+ */

+#ifndef SPDY_MICROSPDY_H

+#define SPDY_MICROSPDY_H

+

+#include <zlib.h>

+#include <stdbool.h>

+

+/* While we generally would like users to use a configure-driven

+   build process which detects which headers are present and

+   hence works on any platform, we use "standard" includes here

+   to build out-of-the-box for beginning users on common systems.

+

+   Once you have a proper build system and go for more exotic

+   platforms, you should define MHD_PLATFORM_H in some header that

+   you always include *before* "microhttpd.h".  Then the following

+   "standard" includes won't be used (which might be a good

+   idea, especially on platforms where they do not exist). */

+#ifndef MHD_PLATFORM_H

+#include <unistd.h>

+#include <stdarg.h>

+#include <stdint.h>

+#ifdef __MINGW32__

+#include <ws2tcpip.h>

+#else

+#include <sys/time.h>

+#include <sys/types.h>

+#include <sys/socket.h>

+#endif

+#endif

+

+#ifndef _MHD_EXTERN

+#define _MHD_EXTERN extern

+#endif

+

+/**

+ * return code for "YES".

+ */

+#define SPDY_YES 1

+

+/**

+ * return code for "NO".

+ */

+#define SPDY_NO 0

+

+/**

+ * return code for error when input parameters are wrong. To be returned

+ * only by functions which return int. The others will return NULL on

+ * input error.

+ */

+#define SPDY_INPUT_ERROR -1

+

+/**

+ * SPDY version supported by the lib.

+ */

+#define SPDY_VERSION 3

+

+/**

+ * The maximum allowed size (without 8 byte headers) of

+ * SPDY frames (value length) is 8192. The lib will accept and

+ * send frames with length at most this value here.

+ */

+#define SPDY_MAX_SUPPORTED_FRAME_SIZE 8192

+

+/**

+ * HTTP response codes.

+ */

+#define SPDY_HTTP_CONTINUE 100

+#define SPDY_HTTP_SWITCHING_PROTOCOLS 101

+#define SPDY_HTTP_PROCESSING 102

+

+#define SPDY_HTTP_OK 200

+#define SPDY_HTTP_CREATED 201

+#define SPDY_HTTP_ACCEPTED 202

+#define SPDY_HTTP_NON_AUTHORITATIVE_INFORMATION 203

+#define SPDY_HTTP_NO_CONTENT 204

+#define SPDY_HTTP_RESET_CONTENT 205

+#define SPDY_HTTP_PARTIAL_CONTENT 206

+#define SPDY_HTTP_MULTI_STATUS 207

+

+#define SPDY_HTTP_MULTIPLE_CHOICES 300

+#define SPDY_HTTP_MOVED_PERMANENTLY 301

+#define SPDY_HTTP_FOUND 302

+#define SPDY_HTTP_SEE_OTHER 303

+#define SPDY_HTTP_NOT_MODIFIED 304

+#define SPDY_HTTP_USE_PROXY 305

+#define SPDY_HTTP_SWITCH_PROXY 306

+#define SPDY_HTTP_TEMPORARY_REDIRECT 307

+

+#define SPDY_HTTP_BAD_REQUEST 400

+#define SPDY_HTTP_UNAUTHORIZED 401

+#define SPDY_HTTP_PAYMENT_REQUIRED 402

+#define SPDY_HTTP_FORBIDDEN 403

+#define SPDY_HTTP_NOT_FOUND 404

+#define SPDY_HTTP_METHOD_NOT_ALLOWED 405

+#define SPDY_HTTP_METHOD_NOT_ACCEPTABLE 406

+#define SPDY_HTTP_PROXY_AUTHENTICATION_REQUIRED 407

+#define SPDY_HTTP_REQUEST_TIMEOUT 408

+#define SPDY_HTTP_CONFLICT 409

+#define SPDY_HTTP_GONE 410

+#define SPDY_HTTP_LENGTH_REQUIRED 411

+#define SPDY_HTTP_PRECONDITION_FAILED 412

+#define SPDY_HTTP_REQUEST_ENTITY_TOO_LARGE 413

+#define SPDY_HTTP_REQUEST_URI_TOO_LONG 414

+#define SPDY_HTTP_UNSUPPORTED_MEDIA_TYPE 415

+#define SPDY_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE 416

+#define SPDY_HTTP_EXPECTATION_FAILED 417

+#define SPDY_HTTP_UNPROCESSABLE_ENTITY 422

+#define SPDY_HTTP_LOCKED 423

+#define SPDY_HTTP_FAILED_DEPENDENCY 424

+#define SPDY_HTTP_UNORDERED_COLLECTION 425

+#define SPDY_HTTP_UPGRADE_REQUIRED 426

+#define SPDY_HTTP_NO_RESPONSE 444

+#define SPDY_HTTP_RETRY_WITH 449

+#define SPDY_HTTP_BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS 450

+#define SPDY_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS 451

+

+#define SPDY_HTTP_INTERNAL_SERVER_ERROR 500

+#define SPDY_HTTP_NOT_IMPLEMENTED 501

+#define SPDY_HTTP_BAD_GATEWAY 502

+#define SPDY_HTTP_SERVICE_UNAVAILABLE 503

+#define SPDY_HTTP_GATEWAY_TIMEOUT 504

+#define SPDY_HTTP_HTTP_VERSION_NOT_SUPPORTED 505

+#define SPDY_HTTP_VARIANT_ALSO_NEGOTIATES 506

+#define SPDY_HTTP_INSUFFICIENT_STORAGE 507

+#define SPDY_HTTP_BANDWIDTH_LIMIT_EXCEEDED 509

+#define SPDY_HTTP_NOT_EXTENDED 510

+

+/**

+ * HTTP headers are used in SPDY, but all of them MUST be lowercase.

+ * Some are not valid in SPDY and MUST not be used

+ */

+#define SPDY_HTTP_HEADER_ACCEPT "accept"

+#define SPDY_HTTP_HEADER_ACCEPT_CHARSET "accept-charset"

+#define SPDY_HTTP_HEADER_ACCEPT_ENCODING "accept-encoding"

+#define SPDY_HTTP_HEADER_ACCEPT_LANGUAGE "accept-language"

+#define SPDY_HTTP_HEADER_ACCEPT_RANGES "accept-ranges"

+#define SPDY_HTTP_HEADER_AGE "age"

+#define SPDY_HTTP_HEADER_ALLOW "allow"

+#define SPDY_HTTP_HEADER_AUTHORIZATION "authorization"

+#define SPDY_HTTP_HEADER_CACHE_CONTROL "cache-control"

+/* Connection header is forbidden in SPDY */

+#define SPDY_HTTP_HEADER_CONNECTION "connection"

+#define SPDY_HTTP_HEADER_CONTENT_ENCODING "content-encoding"

+#define SPDY_HTTP_HEADER_CONTENT_LANGUAGE "content-language"

+#define SPDY_HTTP_HEADER_CONTENT_LENGTH "content-length"

+#define SPDY_HTTP_HEADER_CONTENT_LOCATION "content-location"

+#define SPDY_HTTP_HEADER_CONTENT_MD5 "content-md5"

+#define SPDY_HTTP_HEADER_CONTENT_RANGE "content-range"

+#define SPDY_HTTP_HEADER_CONTENT_TYPE "content-type"

+#define SPDY_HTTP_HEADER_COOKIE "cookie"

+#define SPDY_HTTP_HEADER_DATE "date"

+#define SPDY_HTTP_HEADER_ETAG "etag"

+#define SPDY_HTTP_HEADER_EXPECT "expect"

+#define SPDY_HTTP_HEADER_EXPIRES "expires"

+#define SPDY_HTTP_HEADER_FROM "from"

+/* Host header is forbidden in SPDY */

+#define SPDY_HTTP_HEADER_HOST "host"

+#define SPDY_HTTP_HEADER_IF_MATCH "if-match"

+#define SPDY_HTTP_HEADER_IF_MODIFIED_SINCE "if-modified-since"

+#define SPDY_HTTP_HEADER_IF_NONE_MATCH "if-none-match"

+#define SPDY_HTTP_HEADER_IF_RANGE "if-range"

+#define SPDY_HTTP_HEADER_IF_UNMODIFIED_SINCE "if-unmodified-since"

+/* Keep-Alive header is forbidden in SPDY */

+#define SPDY_HTTP_HEADER_KEEP_ALIVE "keep-alive"

+#define SPDY_HTTP_HEADER_LAST_MODIFIED "last-modified"

+#define SPDY_HTTP_HEADER_LOCATION "location"

+#define SPDY_HTTP_HEADER_MAX_FORWARDS "max-forwards"

+#define SPDY_HTTP_HEADER_PRAGMA "pragma"

+#define SPDY_HTTP_HEADER_PROXY_AUTHENTICATE "proxy-authenticate"

+#define SPDY_HTTP_HEADER_PROXY_AUTHORIZATION "proxy-authorization"

+/* Proxy-Connection header is forbidden in SPDY */

+#define SPDY_HTTP_HEADER_PROXY_CONNECTION "proxy-connection"

+#define SPDY_HTTP_HEADER_RANGE "range"

+#define SPDY_HTTP_HEADER_REFERER "referer"

+#define SPDY_HTTP_HEADER_RETRY_AFTER "retry-after"

+#define SPDY_HTTP_HEADER_SERVER "server"

+#define SPDY_HTTP_HEADER_SET_COOKIE "set-cookie"

+#define SPDY_HTTP_HEADER_SET_COOKIE2 "set-cookie2"

+#define SPDY_HTTP_HEADER_TE "te"

+#define SPDY_HTTP_HEADER_TRAILER "trailer"

+/* Transfer-Encoding header is forbidden in SPDY */

+#define SPDY_HTTP_HEADER_TRANSFER_ENCODING "transfer-encoding"

+#define SPDY_HTTP_HEADER_UPGRADE "upgrade"

+#define SPDY_HTTP_HEADER_USER_AGENT "user-agent"

+#define SPDY_HTTP_HEADER_VARY "vary"

+#define SPDY_HTTP_HEADER_VIA "via"

+#define SPDY_HTTP_HEADER_WARNING "warning"

+#define SPDY_HTTP_HEADER_WWW_AUTHENTICATE "www-authenticate"

+

+/**

+ * HTTP versions (a value must be provided in SPDY requests/responses).

+ */

+#define SPDY_HTTP_VERSION_1_0 "HTTP/1.0"

+#define SPDY_HTTP_VERSION_1_1 "HTTP/1.1"

+

+/**

+ * HTTP methods

+ */

+#define SPDY_HTTP_METHOD_CONNECT "CONNECT"

+#define SPDY_HTTP_METHOD_DELETE "DELETE"

+#define SPDY_HTTP_METHOD_GET "GET"

+#define SPDY_HTTP_METHOD_HEAD "HEAD"

+#define SPDY_HTTP_METHOD_OPTIONS "OPTIONS"

+#define SPDY_HTTP_METHOD_POST "POST"

+#define SPDY_HTTP_METHOD_PUT "PUT"

+#define SPDY_HTTP_METHOD_TRACE "TRACE"

+

+/**

+ * HTTP POST encodings, see also

+ * http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4

+ */

+#define SPDY_HTTP_POST_ENCODING_FORM_URLENCODED "application/x-www-form-urlencoded"

+#define SPDY_HTTP_POST_ENCODING_MULTIPART_FORMDATA "multipart/form-data"

+

+

+/**

+ * Handle for the daemon (listening on a socket).

+ */

+struct SPDY_Daemon;

+

+

+/**

+ * Handle for a SPDY session/connection.

+ */

+struct SPDY_Session;

+

+

+/**

+ * Handle for a SPDY request sent by a client. The structure has pointer

+ * to the session's handler

+ */

+struct SPDY_Request;

+

+

+/**

+ * Handle for a response containing HTTP headers and data to be sent.

+ * The structure has pointer to the session's handler

+ * for this response.

+ */

+struct SPDY_Response;

+

+

+/**

+ * Collection of tuples of an HTTP header and values used in requests

+ * and responses.

+ */

+struct SPDY_NameValue;

+

+

+/**

+ * Collection of tuples of a SPDY setting ID, value

+ * and flags used to control the sessions.

+ */

+struct SPDY_Settings;

+

+

+/**

+ * SPDY IO sybsystem flags used by SPDY_init() and SPDY_deinit().<p>

+ *

+ * The values are used internally as flags, that is why they must be

+ * powers of 2.

+ */

+enum SPDY_IO_SUBSYSTEM

+{

+

+  /**

+   * No subsystem. For internal use.

+   */

+  SPDY_IO_SUBSYSTEM_NONE = 0,

+

+  /**

+   * Default TLS implementation provided by openSSL/libssl.

+   */

+  SPDY_IO_SUBSYSTEM_OPENSSL = 1,

+

+  /**

+   * No TLS is used.

+   */

+  SPDY_IO_SUBSYSTEM_RAW = 2

+};

+

+

+/**

+ * SPDY daemon options. Passed in the varargs portion of

+ * SPDY_start_daemon to customize the daemon. Each option must

+ * be followed by a value of a specific type.<p>

+ *

+ * The values are used internally as flags, that is why they must be

+ * powers of 2.

+ */

+enum SPDY_DAEMON_OPTION

+{

+

+  /**

+   * No more options / last option.  This is used

+   * to terminate the VARARGs list.

+   */

+  SPDY_DAEMON_OPTION_END = 0,

+

+  /**

+   * Set a custom timeout for all connections.  Must be followed by

+   * a number of seconds, given as an 'unsigned int'.  Use

+   * zero for no timeout.

+   */

+  SPDY_DAEMON_OPTION_SESSION_TIMEOUT = 1,

+

+  /**

+   * Bind daemon to the supplied sockaddr. This option must be

+   * followed by a 'struct sockaddr *'.  The 'struct sockaddr*'

+   * should point to a 'struct sockaddr_in6' or to a

+   * 'struct sockaddr_in'.

+   */

+  SPDY_DAEMON_OPTION_SOCK_ADDR = 2,

+

+  /**

+   * Flags for the daemon. Must be followed by a SPDY_DAEMON_FLAG value

+   * which is the result of bitwise OR of desired flags.

+   */

+  SPDY_DAEMON_OPTION_FLAGS = 4,

+

+  /**

+   * IO subsystem type used by daemon and all its sessions. If not set,

+   * TLS provided by openssl is used. Must be followed by a

+   * SPDY_IO_SUBSYSTEM value.

+   */

+  SPDY_DAEMON_OPTION_IO_SUBSYSTEM = 8,

+

+  /**

+   * Maximum number of frames to be written to the socket at once. The

+   * library tries to send max_num_frames in a single call to SPDY_run

+   * for a single session. This means no requests can be received nor

+   * other sessions can send data as long the current one has enough

+   * frames to send and there is no error on writing. Thus, a big value

+   * will affect the performance. Small value gives fairnes for sessions.

+   * Must be followed by a positive integer (uin32_t). If not set, the

+   * default value 10 will be used.

+   */

+  SPDY_DAEMON_OPTION_MAX_NUM_FRAMES = 16

+};

+

+

+/**

+ * Flags for starting SPDY daemon. They are used to set some settings

+ * for the daemon, which do not require values.

+ */

+enum SPDY_DAEMON_FLAG

+{

+  /**

+   * No flags selected.

+   */

+  SPDY_DAEMON_FLAG_NO = 0,

+

+  /**

+   * The server will bind only on IPv6 addresses. If the flag is set and

+   * the daemon is provided with IPv4 address or IPv6 is not supported,

+   * starting daemon will fail.

+   */

+  SPDY_DAEMON_FLAG_ONLY_IPV6 = 1,

+

+  /**

+   * All sessions' sockets will be set with TCP_NODELAY if the flag is

+   * used. Option considered only by SPDY_IO_SUBSYSTEM_RAW.

+   */

+  SPDY_DAEMON_FLAG_NO_DELAY = 2

+};

+

+

+/**

+ * SPDY settings IDs sent by both client and server in SPDY SETTINGS frame.

+ * They affect the whole SPDY session. Defined in SPDY Protocol - Draft 3.

+ */

+enum SPDY_SETTINGS

+{

+

+  /**

+   * Allows the sender to send its expected upload bandwidth on this

+   * channel. This number is an estimate. The value should be the

+   * integral number of kilobytes per second that the sender predicts

+   * as an expected maximum upload channel capacity.

+   */

+  SPDY_SETTINGS_UPLOAD_BANDWIDTH = 1,

+

+  /**

+   * Allows the sender to send its expected download bandwidth on this

+   * channel. This number is an estimate. The value should be the

+   * integral number of kilobytes per second that the sender predicts as

+   * an expected maximum download channel capacity.

+   */

+  SPDY_SETTINGS_DOWNLOAD_BANDWIDTH = 2,

+

+  /**

+   * Allows the sender to send its expected round-trip-time on this

+   * channel. The round trip time is defined as the minimum amount of

+   * time to send a control frame from this client to the remote and

+   * receive a response. The value is represented in milliseconds.

+   */

+  SPDY_SETTINGS_ROUND_TRIP_TIME = 3,

+

+  /**

+   * Allows the sender to inform the remote endpoint the maximum number

+   * of concurrent streams which it will allow. By default there is no

+   * limit. For implementors it is recommended that this value be no

+   * smaller than 100.

+   */

+  SPDY_SETTINGS_MAX_CONCURRENT_STREAMS = 4,

+

+  /**

+   * Allows the sender to inform the remote endpoint of the current TCP

+   * CWND value.

+   */

+  SPDY_SETTINGS_CURRENT_CWND = 5,

+

+  /**

+   * Allows the sender to inform the remote endpoint the retransmission

+   * rate (bytes retransmitted / total bytes transmitted).

+   */

+  SPDY_SETTINGS_DOWNLOAD_RETRANS_RATE = 6,

+

+  /**

+   * Allows the sender to inform the remote endpoint the initial window

+   * size (in bytes) for new streams.

+   */

+  SPDY_SETTINGS_INITIAL_WINDOW_SIZE = 7,

+

+  /**

+   * Allows the server to inform the client if the new size of the

+   * client certificate vector.

+   */

+  SPDY_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE = 8

+};

+

+

+/**

+ * Flags for each individual SPDY setting in the SPDY SETTINGS frame.

+ * They affect only one setting to which they are set.

+ * Defined in SPDY Protocol - Draft 3.

+ */

+enum SPDY_FLAG_SETTINGS

+{

+

+  /**

+   * When set, the sender of this SETTINGS frame is requesting that the

+   * recipient persist the ID/Value and return it in future SETTINGS

+   * frames sent from the sender to this recipient. Because persistence

+   * is only implemented on the client, this flag is only sent by the

+   * server.

+   */

+  SPDY_FLAG_SETTINGS_PERSIST_VALUE = 1,

+

+  /**

+   * When set, the sender is notifying the recipient that this ID/Value

+   * pair was previously sent to the sender by the recipient with the

+   * #SPDY_FLAG_SETTINGS_PERSIST_VALUE, and the sender is returning it.

+   * Because persistence is only implemented on the client, this flag is

+   * only sent by the client.

+   */

+  SPDY_FLAG_SETTINGS_PERSISTED = 2

+};

+

+

+/**

+ * Flag associated with a whole SPDY SETTINGS frame. Affect all the

+ * settings in the frame. Defined in SPDY Protocol - Draft 3.

+ */

+enum SPDY_FLAG_SETTINGS_FRAME

+{

+

+  /**

+   * When set, the client should clear any previously persisted SETTINGS

+   * ID/Value pairs. If this frame contains ID/Value pairs with the

+   * #SPDY_FLAG_SETTINGS_PERSIST_VALUE set, then the client will first

+   * clear its existing, persisted settings, and then persist the values

+   * with the flag set which are contained within this frame. Because

+   * persistence is only implemented on the client, this flag can only

+   * be used when the sender is the server.

+   */

+  SPDY_FLAG_SETTINGS_CLEAR_SETTINGS = 1

+};

+

+

+/**

+ * SPDY settings function options. Passed in the varargs portion of

+ * SPDY_SettingsReceivedCallback and SPDY_send_settings to customize

+ * more the settings handling. Each option must

+ * be followed by a value of a specific type.<p>

+ *

+ * The values are used internally as flags, that is why they must be

+ * powers of 2.

+ */

+enum SPDY_SETTINGS_OPTION

+{

+

+  /**

+   * No more options / last option.  This is used

+   * to terminate the VARARGs list.

+   */

+  SPDY_SETTINGS_OPTION_END = 0

+};

+

+

+/**

+ * Used as a parameter for SPDY_ResponseResultCallback and shows if the

+ * response was actually written to the TLS socket or discarded by the

+ * lib for any reason (and respectively the reason).

+ */

+enum SPDY_RESPONSE_RESULT

+{

+

+  /**

+   * The lib has written the full response to the TLS socket.

+   */

+  SPDY_RESPONSE_RESULT_SUCCESS = 0,

+

+  /**

+   * The session is being closed, so the data is being discarded

+   */

+  SPDY_RESPONSE_RESULT_SESSION_CLOSED = 1,

+

+  /**

+   * The stream for this response has been closed. May happen when the

+   * sender had sent first SYN_STREAM and after that RST_STREAM.

+   */

+  SPDY_RESPONSE_RESULT_STREAM_CLOSED = 2

+};

+

+

+/**

+ * Callback for serious error condition. The default action is to print

+ * an error message and abort().

+ *

+ * @param cls user specified value

+ * @param file where the error occured

+ * @param line where the error occured

+ * @param reason error details message, may be NULL

+ */

+typedef void

+(*SPDY_PanicCallback) (void * cls,

+                       const char *file,

+                       unsigned int line,

+                       const char *reason);

+

+

+/**

+ * Callback for new SPDY session established by a client. Called

+ * immediately after the TCP connection was established.

+ *

+ * @param cls client-defined closure

+ * @param session handler for the new SPDY session

+ */

+typedef void

+(*SPDY_NewSessionCallback) (void * cls,

+                            struct SPDY_Session * session);

+

+

+/**

+ * Callback for closed session. Called after the TCP connection was

+ * closed. In this callback function the user has the last

+ * chance to access the SPDY_Session structure. After that the latter

+ * will be cleaned!

+ *

+ * @param cls client-defined closure

+ * @param session handler for the closed SPDY session

+ * @param by_client #SPDY_YES if the session close was initiated by the

+ * 					client;

+ * 		    #SPDY_NO if closed by the server

+ */

+typedef void

+(*SPDY_SessionClosedCallback) (void *cls,

+                               struct SPDY_Session *session,

+                               int by_client);

+

+

+/**

+ * Iterator over name-value pairs.

+ *

+ * @param cls client-defined closure

+ * @param name of the pair

+ * @param value of the pair

+ * @return #SPDY_YES to continue iterating,

+ *         #SPDY_NO to abort the iteration

+ */

+typedef int

+(*SPDY_NameValueIterator) (void *cls,

+                           const char *name,

+                           const char * const * value,

+                           int num_values);

+

+

+/**

+ * Callback for received SPDY request. The functions is called whenever

+ * a reqest comes, but will also be called if more headers/trailers are

+ * received.

+ *

+ * @param cls client-defined closure

+ * @param request handler. The request object is required for

+ * 			sending responses.

+ * @param priority of the SPDY stream which the request was

+ * 			sent over

+ * @param method HTTP method

+ * @param path HTTP path

+ * @param version HTTP version just like in HTTP request/response:

+ * 			"HTTP/1.0" or "HTTP/1.1" currently

+ * @param host called host as in HTTP

+ * @param scheme used ("http" or "https"). In SPDY 3 it is only "https".

+ * @param headers other HTTP headers from the request

+ * @param more a flag saying if more data related to the request is

+ *        expected to be received. HTTP body may arrive (e.g. POST data);

+ *        then SPDY_NewDataCallback will be called for the connection.

+ *        It is also possible that more headers/trailers arrive;

+ *        then the same callback will be invoked. The user should detect

+ *        that it is not the first invocation of the function for that

+ *        request.

+ */

+typedef void

+(*SPDY_NewRequestCallback) (void *cls,

+                            struct SPDY_Request *request,

+                            uint8_t priority,

+                            const char *method,

+                            const char *path,

+                            const char *version,

+                            const char *host,

+                            const char *scheme,

+                            struct SPDY_NameValue *headers,

+                            bool more);

+

+

+/**

+ * Callback for received new data chunk (HTTP body) from a given

+ * request (e.g. POST data).

+ *

+ * @param cls client-defined closure

+ * @param request handler

+ * @param buf data chunk from the POST data

+ * @param size the size of the data chunk 'buf' in bytes. Note that it

+ *             may be 0.

+ * @param more false if this is the last chunk from the data. Note:

+ *             true does not mean that more data will come, exceptional

+ *             situation is possible

+ * @return #SPDY_YES to continue calling the function,

+ *         #SPDY_NO to stop calling the function for this request

+ */

+typedef int

+(*SPDY_NewDataCallback) (void *cls,

+                         struct SPDY_Request *request,

+                         const void *buf,

+                         size_t size,

+                         bool more);

+// How about passing POST encoding information

+// here as well?

+//TODO

+

+

+/**

+ * Callback to be used with SPDY_build_response_with_callback. The

+ * callback will be called when the lib wants to write to the TLS socket.

+ * The application should provide the data to be sent.

+ *

+ * @param cls client-defined closure

+ * @param max maximum number of bytes that are allowed to be written

+ * 			to the buffer.

+ * @param more true if more data will be sent (i.e. the function must

+ * 				be calleed again),

+ *             false if this is the last chunk, the lib will close

+ * 				the stream

+ * @return number of bytes written to buffer. On error the call MUST

+ * 			return value less than 0 to indicate the library.

+ */

+typedef ssize_t

+(*SPDY_ResponseCallback) (void *cls,

+                          void *buffer,

+                          size_t max,

+                          bool *more);

+

+

+/**

+ * Callback to be called when the last bytes from the response was sent

+ * to the client or when the response was discarded from the lib. This

+ * callback is a very good place to discard the request and the response

+ * objects, if they will not be reused (e.g., sending the same response

+ * again). If the stream is closed it is safe to discard the request

+ * object.

+ *

+ * @param cls client-defined closure

+ * @param response handler to the response that was just sent

+ * @param request handler to the request for which the response was sent

+ * @param status shows if actually the response was sent or it was

+ * 			discarded by the lib for any reason (e.g., closing session,

+ * 			closing stream, stopping daemon, etc.). It is possible that

+ * 			status indicates an error but parts of the response headers

+ * 			and/or body (in one

+ * 			or several frames) were already sent to the client.

+ * @param streamopened indicates if the the stream for this request/

+ * 			response pair is still opened. If yes, the server may want

+ * 			to use SPDY push to send something additional to the client

+ * 			and/or close the stream.

+ */

+typedef void

+(*SPDY_ResponseResultCallback) (void * cls,

+                                struct SPDY_Response *response,

+                                struct SPDY_Request *request,

+                                enum SPDY_RESPONSE_RESULT status,

+                                bool streamopened);

+

+

+/**

+ * Callback to notify when SPDY ping response is received.

+ *

+ * @param session handler for which the ping request was sent

+ * @param rtt the timespan between sending ping request and receiving it

+ * 			from the library

+ */

+typedef void

+(*SPDY_PingCallback) (void * cls,

+                      struct SPDY_Session *session,

+                      struct timeval *rtt);

+

+

+/**

+ * Iterator over settings ID/Value/Flags tuples.

+ *

+ * @param cls client-defined closure

+ * @param id SPDY settings ID

+ * @param value value for this setting

+ * @param flags flags for this tuple; use

+ * 			`enum SPDY_FLAG_SETTINGS`

+ * @return #SPDY_YES to continue iterating,

+ *         #SPDY_NO to abort the iteration

+ */

+typedef int

+(*SPDY_SettingsIterator) (void *cls,

+                          enum SPDY_SETTINGS id,

+                          int32_t value,

+                          uint8_t flags);

+

+

+/**

+ * Callback to notify when SPDY SETTINGS are received from the client.

+ *

+ * @param session handler for which settings are received

+ * @param settings ID/value/flags tuples of the settings

+ * @param flags for the whole settings frame; use

+ * 			enum SPDY_FLAG_SETTINGS_FRAME

+ * @param ... list of options (type-value pairs,

+ *        terminated with #SPDY_SETTINGS_OPTION_END).

+ */

+typedef void

+(*SPDY_SettingsReceivedCallback) (struct SPDY_Session *session,

+                                  struct SPDY_Settings *settings,

+                                  uint8_t flags,

+                                  ...);

+

+

+/* Global functions for the library */

+

+

+/**

+ * Init function for the whole library. It MUST be called before any

+ * other function of the library to initialize things like TLS context

+ * and possibly other stuff needed by the lib. Currently the call

+ * always returns #SPDY_YES.

+ *

+ * @param io_subsystem the IO subsystem that will

+ *        be initialized. Several can be used with bitwise OR. If no

+ *        parameter is set, the default openssl subsystem will be used.

+ * @return #SPDY_YES if the library was correctly initialized and its

+ * 			functions can be used now;

+ * 			#SPDY_NO on error

+ */

+_MHD_EXTERN int

+(SPDY_init) (enum SPDY_IO_SUBSYSTEM io_subsystem, ...);

+#define SPDY_init() SPDY_init (SPDY_IO_SUBSYSTEM_OPENSSL)

+

+

+/**

+ * Deinit function for the whole lib. It can be called after finishing

+ * using the library. It frees and cleans up resources allocated in

+ * SPDY_init. Currently the function does not do anything.

+ */

+_MHD_EXTERN void

+SPDY_deinit (void);

+

+

+/**

+ * Sets the global error handler to a different implementation. "cb"

+ * will only be called in the case of typically fatal, serious

+ * internal consistency issues.  These issues should only arise in the

+ * case of serious memory corruption or similar problems with the

+ * architecture as well as failed assertions.  While "cb" is allowed to

+ * return and the lib will then try to continue, this is never safe.

+ *

+ * The default implementation that is used if no panic function is set

+ * simply prints an error message and calls "abort".  Alternative

+ * implementations might call "exit" or other similar functions.

+ *

+ * @param cb new error handler

+ * @param cls passed to error handler

+ */

+_MHD_EXTERN void

+SPDY_set_panic_func (SPDY_PanicCallback cb,

+                     void *cls);

+

+

+/* Daemon functions */

+

+

+/**

+ * Start a SPDY webserver on the given port.

+ *

+ * @param port to bind to. The value is ignored if address structure

+ * 			is passed as daemon option

+ * @param certfile path to the certificate that will be used by server

+ * @param keyfile path to the keyfile for the certificate

+ * @param nscb callback called when a new SPDY session is

+ * 			established	by a client

+ * @param sccb callback called when a session is closed

+ * @param nrcb callback called when a client sends request

+ * @param npdcb callback called when HTTP body (POST data) is received

+ * 			after request

+ * @param cls common extra argument to all of the callbacks

+ * @param ... list of options (type-value pairs,

+ *        terminated with #SPDY_DAEMON_OPTION_END).

+ * @return NULL on error, handle to daemon on success

+ */

+_MHD_EXTERN struct SPDY_Daemon *

+SPDY_start_daemon (uint16_t port,

+                   const char *certfile,

+                   const char *keyfile,

+                   SPDY_NewSessionCallback nscb,

+                   SPDY_SessionClosedCallback sccb,

+                   SPDY_NewRequestCallback nrcb,

+                   SPDY_NewDataCallback npdcb,

+                   void *cls,

+                   ...);

+

+

+/**

+ * Shutdown the daemon. First all sessions are closed. It is NOT safe

+ * to call this function in user callbacks.

+ *

+ * @param daemon to stop

+ */

+_MHD_EXTERN void

+SPDY_stop_daemon (struct SPDY_Daemon *daemon);

+

+

+/**

+ * Obtain the select sets for this daemon. Only those are retrieved,

+ * which some processing should be done for, i.e. not all sockets are

+ * added to write_fd_set.<p>

+ *

+ * It is possible that there is

+ * nothing to be read from a socket but there is data either in the

+ * TLS subsystem's read buffers or in libmicrospdy's read buffers, which

+ * waits for being processed. In such case the file descriptor will be

+ * added to write_fd_set. Since it is very likely for the socket to be

+ * ready for writing, the select used in the application's event loop

+ * will return with success, SPDY_run will be called, the data will be

+ * processed and maybe something will be written to the socket. Without

+ * this behaviour, considering a proper event loop, data may stay in the

+ * buffers, but run is never called.

+ *

+ * @param daemon to get sets from

+ * @param read_fd_set read set

+ * @param write_fd_set write set

+ * @param except_fd_set except set

+ * @return largest FD added to any of the sets

+ */

+_MHD_EXTERN int

+SPDY_get_fdset (struct SPDY_Daemon *daemon,

+                fd_set *read_fd_set,

+                fd_set *write_fd_set,

+                fd_set *except_fd_set);

+

+

+/**

+ * Obtain timeout value for select for this daemon. The returned value

+ * is how long select

+ * should at most block, not the timeout value set for connections.

+ *

+ * @param daemon to query for timeout

+ * @param timeout will be set to the timeout value (in milliseconds)

+ * @return #SPDY_YES on success

+ *         #SPDY_NO if no connections exist that

+ * 			would necessiate the use of a timeout right now

+ */

+_MHD_EXTERN int

+SPDY_get_timeout (struct SPDY_Daemon *daemon,

+                  unsigned long long *timeout);

+

+

+/**

+ * Run webserver operations. This method must be called in

+ * the client event loop.

+ *

+ * @param daemon to run

+ */

+_MHD_EXTERN void

+SPDY_run (struct SPDY_Daemon *daemon);

+

+

+/* SPDY Session handling functions */

+

+

+/**

+ * Closes a SPDY session. SPDY clients and servers are expected to keep

+ * sessions opened as long as possible. However, the server may want to

+ * close some connections, e.g. if there are too many, to free some

+ * resources. The function can also be used to close a specific session

+ * if the client is not desired.

+ *

+ * @param session handler to be closed

+ */

+_MHD_EXTERN void

+SPDY_close_session (struct SPDY_Session * session);

+

+

+/**

+ * Associate a void pointer with a session. The data accessible by the

+ * pointer can later be used wherever the session handler is available.

+ *

+ * @param session handler

+ * @param cls any data pointed by a pointer to be accessible later

+ */

+_MHD_EXTERN void

+SPDY_set_cls_to_session (struct SPDY_Session *session,

+                         void *cls);

+

+

+/**

+ * Retrieves the pointer associated with SPDY_set_cls_to_session().

+ *

+ * @param session handler to get its cls

+ * @return same pointer added by SPDY_set_cls_to_session() or

+ * 			NULL when nothing was associated

+ */

+_MHD_EXTERN void *

+SPDY_get_cls_from_session (struct SPDY_Session *session);

+

+

+/**

+ * Retrieves the remote address of a given session.

+ *

+ * @param session handler to get its remote address

+ * @param addr out parameter; pointing to remote address

+ * @return length of the address structure

+ */

+_MHD_EXTERN socklen_t

+SPDY_get_remote_addr (struct SPDY_Session *session,

+                      struct sockaddr **addr);

+

+

+/* SPDY name/value data structure handling functions */

+

+

+/**

+ * Create a new NameValue structure. It is needed for putting inside the

+ * HTTP headers and their values for a response. The user should later

+ * destroy alone the structure.

+ *

+ * @return handler to the new empty structure or NULL on error

+ */

+_MHD_EXTERN struct SPDY_NameValue *

+SPDY_name_value_create (void);

+

+

+/**

+ * Add name/value pair to a NameValue structure. SPDY_NO will be returned

+ * if the name/value pair is already in the structure. It is legal to

+ * add different values for the same name.

+ *

+ * @param container structure to which the new pair is added

+ * @param name for the value. Null-terminated string.

+ * @param value the value itself. Null-terminated string.

+ * @return #SPDY_NO on error or #SPDY_YES on success

+ */

+_MHD_EXTERN int

+SPDY_name_value_add (struct SPDY_NameValue *container,

+                     const char *name,

+                     const char *value);

+

+

+/**

+ * Lookup value for a name in a name/value structure.

+ *

+ * @param container structure in which to lookup

+ * @param name the name to look for

+ * @param num_values length of the returned array with values

+ * @return NULL if no such item was found, or an array containing the

+ * 			values

+ */

+_MHD_EXTERN const char * const *

+SPDY_name_value_lookup (struct SPDY_NameValue *container,

+                        const char *name,

+                        int *num_values);

+

+

+/**

+ * Iterate over name/value structure.

+ *

+ * @param container structure which to iterate over

+ * @param iterator callback to call on each name/value pair;

+ *        maybe NULL (then just count headers)

+ * @param iterator_cls extra argument to @a iterator

+ * @return number of entries iterated over

+ */

+_MHD_EXTERN int

+SPDY_name_value_iterate (struct SPDY_NameValue *container,

+                         SPDY_NameValueIterator iterator,

+                         void *iterator_cls);

+

+

+/**

+ * Destroy a NameValue structure. Use this function to destroy only

+ * objects which, after passed to, will not be destroied by other

+ * functions.

+ *

+ */

+_MHD_EXTERN void

+SPDY_name_value_destroy (struct SPDY_NameValue *container);

+

+

+/* SPDY request handling functions */

+

+

+/**

+ * Gets the session responsible for the given

+ * request.

+ *

+ * @param request for which the session is wanted

+ * @return session handler for the request

+ */

+_MHD_EXTERN struct SPDY_Session *

+SPDY_get_session_for_request (const struct SPDY_Request *request);

+

+

+/**

+ * Associate a void pointer with a request. The data accessible by the

+ * pointer can later be used wherever the request handler is available.

+ *

+ * @param request with which to associate a pointer

+ * @param cls any data pointed by a pointer to be accessible later

+ */

+_MHD_EXTERN void

+SPDY_set_cls_to_request (struct SPDY_Request *request,

+                         void *cls);

+

+

+/**

+ * Retrieves the pointer associated with the request by

+ * SPDY_set_cls_to_request().

+ *

+ * @param request to get its cls

+ * @return same pointer added by SPDY_set_cls_to_request() or

+ * 			NULL when nothing was associated

+ */

+_MHD_EXTERN void *

+SPDY_get_cls_from_request (struct SPDY_Request *request);

+

+

+/* SPDY response handling functions */

+

+

+/**

+ * Create response object containing all needed headers and data. The

+ * response object is not bound to a request, so it can be used multiple

+ * times with SPDY_queue_response() and schould be

+ * destroied by calling the SPDY_destroy_response().<p>

+ *

+ * Currently the library does not provide compression of the body data.

+ * It is up to the user to pass already compressed data and the

+ * appropriate headers to this function when desired.

+ *

+ * @param status HTTP status code for the response (e.g. 404)

+ * @param statustext HTTP status message for the response, which will

+ * 			be appended to the status code (e.g. "OK"). Can be NULL

+ * @param version HTTP version for the response (e.g. "http/1.1")

+ * @param headers name/value structure containing additional HTTP headers.

+ *                Can be NULL. Can be used multiple times, it is up to

+ *                the user to destoy the object when not needed anymore.

+ * @param data the body of the response. The lib will make a copy of it,

+ *             so it is up to the user to take care of the memory

+ *             pointed by data

+ * @param size length of @a data. It can be 0, then the lib will send only

+ * 				headers

+ * @return NULL on error, handle to response object on success

+ */

+_MHD_EXTERN struct SPDY_Response *

+SPDY_build_response (int status,

+                     const char *statustext,

+                     const char *version,

+                     struct SPDY_NameValue *headers,

+                     const void *data,

+                     size_t size);

+

+

+/**

+ * Create response object containing all needed headers. The data will

+ * be provided later when the lib calls the callback function (just

+ * before writing it to the TLS socket). The

+ * response object is not bound to a request, so it can be used multiple

+ * times with SPDY_queue_response() and schould be

+ * destroied by calling the SPDY_destroy_response().<p>

+ *

+ * Currently the library does not provide compression of the body data.

+ * It is up to the user to pass already compressed data and the

+ * appropriate headers to this function and the callback when desired.

+ *

+ * @param status HTTP status code for the response (e.g. 404)

+ * @param statustext HTTP status message for the response, which will

+ * 			be appended to the status code (e.g. "OK"). Can be NULL

+ * @param version HTTP version for the response (e.g. "http/1.1")

+ * @param headers name/value structure containing additional HTTP headers.

+ *                Can be NULL. Can be used multiple times, it is up to

+ *                the user to destoy the object when not needed anymore.

+ * @param rcb callback to use to obtain response data

+ * @param rcb_cls extra argument to @a rcb

+ * @param block_size preferred block size for querying rcb (advisory only,

+ *                   the lib will call rcb specifying the block size); clients

+ *                   should pick a value that is appropriate for IO and

+ *                   memory performance requirements. The function will

+ *                   fail if the value is bigger than the maximum

+ *                   supported value (SPDY_MAX_SUPPORTED_FRAME_SIZE).

+ *                   Can be 0, then the lib will use

+ *                   #SPDY_MAX_SUPPORTED_FRAME_SIZE instead.

+ * @return NULL on error, handle to response object on success

+ */

+_MHD_EXTERN struct SPDY_Response *

+SPDY_build_response_with_callback(int status,

+                                  const char *statustext,

+                                  const char *version,

+                                  struct SPDY_NameValue *headers,

+                                  SPDY_ResponseCallback rcb,

+                                  void *rcb_cls,

+                                  uint32_t block_size);

+

+

+/**

+ * Queue response object to be sent to the client. A successfully queued

+ * response may never be sent, e.g. when the stream gets closed. The

+ * data will be added to the output queue. The call will fail, if the

+ * output for this session

+ * is closed (i.e. the session is closed, half or full) or the output

+ * channel for the stream, on which the request was received, is closed

+ * (i.e. the stream is closed, half or full).

+ *

+ * @param request object identifying the request to which the

+ * 			response is returned

+ * @param response object containg headers and data to be sent

+ * @param closestream TRUE if the server does NOT intend to PUSH

+ * 			something more associated to this request/response later,

+ * 			FALSE otherwise

+ * @param consider_priority if FALSE, the response will be added to the

+ * 			end of the queue. If TRUE, the response will be added after

+ * 			the last previously added response with priority of the

+ * 			request grater or equal to that of the current one. This

+ * 			means that the function should be called with TRUE each time

+ * 			if one wants to be sure that the output queue behaves like

+ * 			a priority queue

+ * @param rrcb callback called when all the data was sent (last frame

+ * 			from response) or when that frame was discarded (e.g. the

+ * 			stream has been closed meanwhile)

+ * @param rrcb_cls extra argument to @a rrcb

+ * @return #SPDY_NO on error or #SPDY_YES on success

+ */

+_MHD_EXTERN int

+SPDY_queue_response (struct SPDY_Request *request,

+                     struct SPDY_Response *response,

+                     bool closestream,

+                     bool consider_priority,

+                     SPDY_ResponseResultCallback rrcb,

+                     void *rrcb_cls);

+

+

+/**

+ * Destroy a response structure. It should be called for all objects

+ * returned by SPDY_build_response*() functions to free the memory

+ * associated with the prepared response. It is safe to call this

+ * function not before being sure that the response will not be used by

+ * the lib anymore, this means after SPDY_ResponseResultCallback

+ * callbacks were called for all calls to SPDY_queue_response() passing

+ * this response.

+ *

+ * @param response to destroy

+ */

+_MHD_EXTERN void

+SPDY_destroy_response (struct SPDY_Response *response);

+

+

+/* SPDY settings ID/value data structure handling functions */

+

+

+/**

+ * Create a new SettingsIDValue structure. It is needed for putting

+ * inside tuples of SPDY option, flags and value for sending to the

+ * client.

+ *

+ * @return hendler to the new empty structure or NULL on error

+ */

+_MHD_EXTERN const struct SPDY_Settings *

+SPDY_settings_create (void);

+

+

+/**

+ * Add or update a tuple to a SettingsIDValue structure.

+ *

+ * @param container structure to which the new tuple is added

+ * @param id SPDY settings ID that will be sent. If this ID already in

+ *           container, the tupple for it will be updated (value and/or

+ *           flags). If it is not in the container, a new tupple will be

+ *           added.

+ * @param flags SPDY settings flags applied only to this setting

+ * @param value of the setting

+ * @return #SPDY_NO on error

+ * 			or #SPDY_YES if a new setting was added

+ */

+_MHD_EXTERN int

+SPDY_settings_add (struct SPDY_Settings *container,

+                   enum SPDY_SETTINGS id,

+                   enum SPDY_FLAG_SETTINGS flags,

+                   int32_t value);

+

+

+/**

+ * Lookup value and flags for an ID in a settings ID/value structure.

+ *

+ * @param container structure in which to lookup

+ * @param id SPDY settings ID to search for

+ * @param flags out param for SPDY settings flags for this setting;

+ * 			check it against the flags in enum SPDY_FLAG_SETTINGS

+ * @param value out param for the value of this setting

+ * @return #SPDY_NO if the setting is not into the structure

+ * 			or #SPDY_YES if it is into it

+ */

+_MHD_EXTERN int

+SPDY_settings_lookup (const struct SPDY_Settings *container,

+                      enum SPDY_SETTINGS id,

+                      enum SPDY_FLAG_SETTINGS *flags,

+                      int32_t *value);

+

+

+/**

+ * Iterate over settings ID/value structure.

+ *

+ * @param container structure which to iterate over

+ * @param iterator callback to call on each ID/value pair;

+ *        maybe NULL (then just count number of settings)

+ * @param iterator_cls extra argument to iterator

+ * @return number of entries iterated over

+ */

+_MHD_EXTERN int

+SPDY_settings_iterate (const struct SPDY_Settings *container,

+                       SPDY_SettingsIterator iterator,

+                       void *iterator_cls);

+

+

+/**

+ * Destroy a settings ID/value structure. Use this function to destroy

+ * only objects which, after passed to, will not be destroied by other

+ * functions.

+ *

+ * @param container structure which to detroy

+ */

+_MHD_EXTERN void

+SPDY_settings_destroy (struct SPDY_Settings * container);

+

+

+/* SPDY SETTINGS handling functions */

+

+

+/**

+ * Send SPDY SETTINGS to the client. The call will return fail if there

+ * in invald setting into the settings container (e.g. invalid setting

+ * ID).

+ *

+ * @param session SPDY_Session handler for which settings are being sent

+ * @param settings ID/value pairs of the settings to be sent.

+ * 			Can be used multiple times, it is up to the user to destoy

+ * 			the object when not needed anymore.

+ * @param flags for the whole settings frame. They are valid for all tuples

+ * @param ... list of options (type-value pairs,

+ *        terminated with #SPDY_SETTINGS_OPTION_END).

+ * @return SPDY_NO on error or SPDY_YES on

+ * 			success

+ */

+_MHD_EXTERN int

+SPDY_send_settings (struct SPDY_Session *session,

+                    struct SPDY_Settings *settings,

+                    enum SPDY_FLAG_SETTINGS_FRAME flags,

+                    ...);

+

+

+/* SPDY misc functions */

+

+

+/**

+ * Destroy a request structure. It should be called for all objects

+ * received as a parameter in SPDY_NewRequestCallback to free the memory

+ * associated with the request. It is safe to call this

+ * function not before being sure that the request will not be used by

+ * the lib anymore, this means after the stream, on which this request

+ * had been sent, was closed and all SPDY_ResponseResultCallback

+ * callbacks were called for all calls to SPDY_queue_response() passing

+ * this request object.

+ *

+ * @param request to destroy

+ */

+_MHD_EXTERN void

+SPDY_destroy_request (struct SPDY_Request * request);

+

+

+/**

+ * Send SPDY ping to the client

+ *

+ * @param session handler for which the ping request is sent

+ * @param rttcb callback called when ping response to the request is

+ * 			received

+ * @param rttcb_cls extra argument to @a rttcb

+ * @return #SPDY_NO on error or #SPDY_YES on success

+ */

+_MHD_EXTERN int

+SPDY_send_ping (struct SPDY_Session *session,

+                SPDY_PingCallback rttcb,

+                void *rttcb_cls);

+

+#endif

diff --git a/src/include/platform.h b/src/include/platform.h
new file mode 100644
index 0000000..22ddddd
--- /dev/null
+++ b/src/include/platform.h
@@ -0,0 +1,205 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2008 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file platform.h
+ * @brief platform-specific includes for libmicrohttpd
+ * @author Christian Grothoff
+ *
+ * This file is included by the libmicrohttpd code
+ * before "microhttpd.h"; it provides the required
+ * standard headers (which are platform-specific).<p>
+ *
+ * Note that this file depends on our configure.ac
+ * build process and the generated config.h file.
+ * Hence you cannot include it directly in applications
+ * that use libmicrohttpd.
+ */
+#ifndef MHD_PLATFORM_H
+#define MHD_PLATFORM_H
+
+#include "MHD_config.h"
+
+#ifndef BUILDING_MHD_LIB
+#ifdef _MHD_EXTERN
+#undef _MHD_EXTERN
+#endif /* _MHD_EXTERN */
+#if defined(_WIN32) && defined(MHD_W32LIB)
+#define _MHD_EXTERN extern
+#elif defined (_WIN32) && defined(MHD_W32DLL)
+#define _MHD_EXTERN __declspec(dllimport) 
+#else
+#define _MHD_EXTERN extern
+#endif
+#elif !defined(_MHD_EXTERN) /* && BUILDING_MHD_LIB */
+#if defined(_WIN32) && defined(MHD_W32LIB)
+#define _MHD_EXTERN extern
+#elif defined (_WIN32) && defined(MHD_W32DLL)
+#define _MHD_EXTERN extern __declspec(dllexport) 
+#else
+#define _MHD_EXTERN extern
+#endif
+#endif /* BUILDING_MHD_LIB */
+
+#define _XOPEN_SOURCE_EXTENDED  1
+#if OS390
+#define _OPEN_THREADS
+#define _OPEN_SYS_SOCK_IPV6
+#define _OPEN_MSGQ_EXT
+#define _LP64
+#endif
+
+#if defined(_WIN32)
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#else // _WIN32_WINNT
+#if _WIN32_WINNT < 0x0501
+#error "Headers for Windows XP or later are required"
+#endif // _WIN32_WINNT < 0x0501
+#endif // _WIN32_WINNT
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#endif // _WIN32
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdarg.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stddef.h>
+#ifdef MHD_USE_POSIX_THREADS
+#undef HAVE_CONFIG_H
+#include <pthread.h>
+#define HAVE_CONFIG_H 1
+#endif // MHD_USE_POSIX_THREADS
+
+/* different OSes have fd_set in
+   a broad range of header files;
+   we just include most of them (if they
+   are available) */
+
+
+#ifdef OS_VXWORKS
+#include <sockLib.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#define RESTRICT __restrict__
+#endif
+#if HAVE_MEMORY_H
+#include <memory.h>
+#endif
+
+#if HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_MSG_H
+#include <sys/msg.h>
+#endif
+#if HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#if HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if HAVE_TIME_H
+#include <time.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <ws2tcpip.h>
+#define sleep(seconds) (SleepEx((seconds)*1000, 1)/1000)
+#define usleep(useconds) (void)SleepEx((useconds)/1000, 1)
+#endif
+
+#if !defined(SHUT_WR) && defined(SD_SEND)
+#define SHUT_WR SD_SEND
+#endif
+#if !defined(SHUT_RD) && defined(SD_RECEIVE)
+#define SHUT_RD SD_RECEIVE
+#endif
+#if !defined(SHUT_RDWR) && defined(SD_BOTH)
+#define SHUT_RDWR SD_BOTH
+#endif
+
+#if defined(_MSC_FULL_VER) && !defined (_SSIZE_T_DEFINED)
+#define _SSIZE_T_DEFINED
+typedef intptr_t ssize_t;
+#endif // !_SSIZE_T_DEFINED */
+#ifndef MHD_SOCKET_DEFINED
+/**
+ * MHD_socket is type for socket FDs
+ */
+#if !defined(_WIN32) || defined(_SYS_TYPES_FD_SET)
+#define MHD_POSIX_SOCKETS 1
+typedef int MHD_socket;
+#define MHD_INVALID_SOCKET (-1)
+#else /* !defined(_WIN32) || defined(_SYS_TYPES_FD_SET) */
+#define MHD_WINSOCK_SOCKETS 1
+#include <winsock2.h>
+typedef SOCKET MHD_socket;
+#define MHD_INVALID_SOCKET (INVALID_SOCKET)
+#endif /* !defined(_WIN32) || defined(_SYS_TYPES_FD_SET) */
+#define MHD_SOCKET_DEFINED 1
+#endif /* MHD_SOCKET_DEFINED */
+
+/* Force don't use pipes on W32 */
+#if defined(_WIN32) && !defined(MHD_DONT_USE_PIPES)
+#define MHD_DONT_USE_PIPES 1
+#endif /* defined(_WIN32) && !defined(MHD_DONT_USE_PIPES) */
+
+/* MHD_pipe is type for pipe FDs*/
+#ifndef MHD_DONT_USE_PIPES
+typedef int MHD_pipe;
+#else /* ! MHD_DONT_USE_PIPES */
+typedef MHD_socket MHD_pipe;
+#endif /* ! MHD_DONT_USE_PIPES */
+
+#if !defined(IPPROTO_IPV6) && defined(_MSC_FULL_VER) && _WIN32_WINNT >= 0x0501
+/* VC use IPPROTO_IPV6 as part of enum */
+#define IPPROTO_IPV6 IPPROTO_IPV6
+#endif
+
+#endif
diff --git a/src/include/platform_interface.h b/src/include/platform_interface.h
new file mode 100644
index 0000000..9e63f7a
--- /dev/null
+++ b/src/include/platform_interface.h
@@ -0,0 +1,344 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2014 Karlson2k (Evgeny Grin)
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library.
+  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file include/platform_interface.h
+ * @brief  internal platform abstraction functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_PLATFORM_INTERFACE_H
+#define MHD_PLATFORM_INTERFACE_H
+
+#include "platform.h"
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include "w32functions.h"
+#endif
+
+/* ***************************** 
+     General function mapping 
+   *****************************/
+#if !defined(_WIN32) || defined(__CYGWIN__)
+/**
+ * Check two strings case-insensitive equality
+ * @param a first string to check
+ * @param b second string to check
+ * @return boolean true if strings are equal, boolean false if strings are unequal
+ */
+#define MHD_str_equal_caseless_(a,b) (0==strcasecmp((a),(b)))
+#else
+/**
+ * Check two strings case-insensitive equality
+ * @param a first string to check
+ * @param b second string to check
+ * @return boolean true if strings are equal, boolean false if strings are unequal
+ */
+#define MHD_str_equal_caseless_(a,b) (0==_stricmp((a),(b)))
+#endif
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+/**
+ * Check not more than n chars in two strings case-insensitive equality
+ * @param a first string to check
+ * @param b second string to check
+ * @param n maximum number of chars to check
+ * @return boolean true if strings are equal, boolean false if strings are unequal
+ */
+#define MHD_str_equal_caseless_n_(a,b,n) (0==strncasecmp((a),(b),(n)))
+#else
+/**
+ * Check not more than n chars in two strings case-insensitive equality
+ * @param a first string to check
+ * @param b second string to check
+ * @param n maximum number of chars to check
+ * @return boolean true if strings are equal, boolean false if strings are unequal
+ */
+#define MHD_str_equal_caseless_n_(a,b,n) (0==_strnicmp((a),(b),(n)))
+#endif
+
+/* Platform-independent snprintf name */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+#define MHD_snprintf_ snprintf
+#else
+#define MHD_snprintf_ W32_snprintf
+#endif
+
+
+
+/* MHD_socket_close_(fd) close any FDs (non-W32) / close only socket FDs (W32) */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+#define MHD_socket_close_(fd) close((fd))
+#else
+#define MHD_socket_close_(fd) closesocket((fd))
+#endif
+
+/* MHD_socket_errno_ is errno of last function (non-W32) / errno of last socket function (W32) */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+#define MHD_socket_errno_ errno
+#else
+#define MHD_socket_errno_ MHD_W32_errno_from_winsock_()
+#endif
+
+/* MHD_socket_last_strerr_ is description string of last errno (non-W32) /
+ *                            description string of last socket error (W32) */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+#define MHD_socket_last_strerr_() strerror(errno)
+#else
+#define MHD_socket_last_strerr_() MHD_W32_strerror_last_winsock_()
+#endif
+
+/* MHD_strerror_ is strerror (both non-W32/W32) */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+#define MHD_strerror_(errnum) strerror((errnum))
+#else
+#define MHD_strerror_(errnum) MHD_W32_strerror_((errnum))
+#endif
+
+/* MHD_set_socket_errno_ set errno to errnum (non-W32) / set socket last error to errnum (W32) */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+#define MHD_set_socket_errno_(errnum) errno=(errnum)
+#else
+#define MHD_set_socket_errno_(errnum) MHD_W32_set_last_winsock_error_((errnum))
+#endif
+
+/* MHD_SYS_select_ is wrapper macro for system select() function */
+#if !defined(MHD_WINSOCK_SOCKETS)
+#define MHD_SYS_select_(n,r,w,e,t) select((n),(r),(w),(e),(t))
+#else
+#define MHD_SYS_select_(n,r,w,e,t) select((int)0,(r),(w),(e),(t))
+#endif
+
+#if defined(HAVE_POLL)
+/* MHD_sys_poll_ is wrapper macro for system poll() function */
+#if !defined(MHD_WINSOCK_SOCKETS)
+#define MHD_sys_poll_ poll
+#else  /* MHD_WINSOCK_SOCKETS */
+#define MHD_sys_poll_ WSAPoll
+#endif /* MHD_WINSOCK_SOCKETS */
+#endif /* HAVE_POLL */
+
+/* MHD_pipe_ create pipe (!MHD_DONT_USE_PIPES) /
+ *           create two connected sockets (MHD_DONT_USE_PIPES) */
+#ifndef MHD_DONT_USE_PIPES
+#define MHD_pipe_(fdarr) pipe((fdarr))
+#else /* MHD_DONT_USE_PIPES */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+#define MHD_pipe_(fdarr) socketpair(AF_LOCAL, SOCK_STREAM, 0, (fdarr))
+#else /* !defined(_WIN32) || defined(__CYGWIN__) */
+#define MHD_pipe_(fdarr) MHD_W32_pair_of_sockets_((fdarr))
+#endif /* !defined(_WIN32) || defined(__CYGWIN__) */
+#endif /* MHD_DONT_USE_PIPES */
+
+/* MHD_pipe_errno_ is errno of last function (!MHD_DONT_USE_PIPES) /
+ *                    errno of last emulated pipe function (MHD_DONT_USE_PIPES) */
+#ifndef MHD_DONT_USE_PIPES
+#define MHD_pipe_errno_ errno
+#else
+#define MHD_pipe_errno_ MHD_socket_errno_
+#endif
+
+/* MHD_pipe_last_strerror_ is description string of last errno (!MHD_DONT_USE_PIPES) /
+ *                            description string of last pipe error (MHD_DONT_USE_PIPES) */
+#ifndef MHD_DONT_USE_PIPES
+#define MHD_pipe_last_strerror_() strerror(errno)
+#else
+#define MHD_pipe_last_strerror_() MHD_socket_last_strerr_()
+#endif
+
+/* MHD_pipe_write_ write data to real pipe (!MHD_DONT_USE_PIPES) /
+ *                 write data to emulated pipe (MHD_DONT_USE_PIPES) */
+#ifndef MHD_DONT_USE_PIPES
+#define MHD_pipe_write_(fd, ptr, sz) write((fd), (const void*)(ptr), (sz))
+#else
+#define MHD_pipe_write_(fd, ptr, sz) send((fd), (const char*)(ptr), (sz), 0)
+#endif
+
+/* MHD_pipe_read_ read data from real pipe (!MHD_DONT_USE_PIPES) /
+ *                read data from emulated pipe (MHD_DONT_USE_PIPES) */
+#ifndef MHD_DONT_USE_PIPES
+#define MHD_pipe_read_(fd, ptr, sz) read((fd), (void*)(ptr), (sz))
+#else
+#define MHD_pipe_read_(fd, ptr, sz) recv((fd), (char*)(ptr), (sz), 0)
+#endif
+
+/* MHD_pipe_close_(fd) close any FDs (non-W32) /
+ *                     close emulated pipe FDs (W32) */
+#ifndef MHD_DONT_USE_PIPES
+#define MHD_pipe_close_(fd) close((fd))
+#else
+#define MHD_pipe_close_(fd) MHD_socket_close_((fd))
+#endif
+
+/* MHD_INVALID_PIPE_ is a value of bad pipe FD */
+#ifndef MHD_DONT_USE_PIPES
+#define MHD_INVALID_PIPE_ (-1)
+#else
+#define MHD_INVALID_PIPE_ MHD_INVALID_SOCKET
+#endif
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+#define MHD_random_() random()
+#else
+#define MHD_random_() MHD_W32_random_()
+#endif
+
+#if defined(MHD_USE_POSIX_THREADS)
+typedef pthread_t MHD_thread_handle_;
+#elif defined(MHD_USE_W32_THREADS)
+#include <windows.h>
+typedef HANDLE MHD_thread_handle_;
+#else
+#error "No threading API is available."
+#endif
+
+#if defined(MHD_USE_POSIX_THREADS)
+#define MHD_THRD_RTRN_TYPE_ void*
+#define MHD_THRD_CALL_SPEC_
+#elif defined(MHD_USE_W32_THREADS)
+#define MHD_THRD_RTRN_TYPE_ unsigned
+#define MHD_THRD_CALL_SPEC_ __stdcall
+#endif
+
+#if defined(MHD_USE_POSIX_THREADS)
+/**
+ * Wait until specified thread is ended
+ * @param thread ID to watch
+ * @return zero on success, nonzero on failure
+ */
+#define MHD_join_thread_(thread) pthread_join((thread), NULL)
+#elif defined(MHD_USE_W32_THREADS)
+/**
+ * Wait until specified thread is ended
+ * Close thread handle on success
+ * @param thread handle to watch
+ * @return zero on success, nonzero on failure
+ */
+#define MHD_join_thread_(thread) (WAIT_OBJECT_0 == WaitForSingleObject((thread), INFINITE) ? (CloseHandle((thread)), 0) : 1 )
+#endif
+
+#if defined(MHD_USE_W32_THREADS)
+#define MHD_W32_MUTEX_ 1
+#include <windows.h>
+typedef CRITICAL_SECTION MHD_mutex_;
+#elif defined(HAVE_PTHREAD_H) && defined(MHD_USE_POSIX_THREADS)
+#define MHD_PTHREAD_MUTEX_ 1
+typedef pthread_mutex_t MHD_mutex_;
+#else
+#error "No base mutex API is available."
+#endif
+
+#if defined(MHD_PTHREAD_MUTEX_)
+/**
+ * Create new mutex.
+ * @param mutex pointer to the mutex
+ * @return #MHD_YES on success, #MHD_NO on failure
+ */
+#define MHD_mutex_create_(mutex) \
+  ((0 == pthread_mutex_init ((mutex), NULL)) ? MHD_YES : MHD_NO)
+#elif defined(MHD_W32_MUTEX_)
+/**
+ * Create new mutex.
+ * @param mutex pointer to mutex
+ * @return #MHD_YES on success, #MHD_NO on failure
+ */
+#define MHD_mutex_create_(mutex) \
+  ((NULL != (mutex) && 0 != InitializeCriticalSectionAndSpinCount((mutex),2000)) ? MHD_YES : MHD_NO)
+#endif
+
+#if defined(MHD_PTHREAD_MUTEX_)
+/**
+ * Destroy previously created mutex.
+ * @param mutex pointer to mutex
+ * @return #MHD_YES on success, #MHD_NO on failure
+ */
+#define MHD_mutex_destroy_(mutex) \
+  ((0 == pthread_mutex_destroy ((mutex))) ? MHD_YES : MHD_NO)
+#elif defined(MHD_W32_MUTEX_)
+/**
+ * Destroy previously created mutex.
+ * @param mutex pointer to mutex
+ * @return #MHD_YES on success, #MHD_NO on failure
+ */
+#define MHD_mutex_destroy_(mutex) \
+  ((NULL != (mutex)) ? (DeleteCriticalSection(mutex), MHD_YES) : MHD_NO)
+#endif
+
+#if defined(MHD_PTHREAD_MUTEX_)
+/**
+ * Acquire lock on previously created mutex.
+ * If mutex was already locked by other thread, function
+ * blocks until mutex becomes available.
+ * @param mutex pointer to mutex
+ * @return #MHD_YES on success, #MHD_NO on failure
+ */
+#define MHD_mutex_lock_(mutex) \
+  ((0 == pthread_mutex_lock((mutex))) ? MHD_YES : MHD_NO)
+#elif defined(MHD_W32_MUTEX_)
+/**
+ * Acquire lock on previously created mutex.
+ * If mutex was already locked by other thread, function
+ * blocks until mutex becomes available.
+ * @param mutex pointer to mutex
+ * @return #MHD_YES on success, #MHD_NO on failure
+ */
+#define MHD_mutex_lock_(mutex) \
+  ((NULL != (mutex)) ? (EnterCriticalSection((mutex)), MHD_YES) : MHD_NO)
+#endif
+
+#if defined(MHD_PTHREAD_MUTEX_)
+/**
+ * Try to acquire lock on previously created mutex.
+ * Function returns immediately.
+ * @param mutex pointer to mutex
+ * @return #MHD_YES if mutex is locked, #MHD_NO if
+ * mutex was not locked.
+ */
+#define MHD_mutex_trylock_(mutex) \
+  ((0 == pthread_mutex_trylock((mutex))) ? MHD_YES : MHD_NO)
+#elif defined(MHD_W32_MUTEX_)
+/**
+ * Try to acquire lock on previously created mutex.
+ * Function returns immediately.
+ * @param mutex pointer to mutex
+ * @return #MHD_YES if mutex is locked, #MHD_NO if
+ * mutex was not locked.
+ */
+#define MHD_mutex_trylock_(mutex) \
+  ((NULL != (mutex) && 0 != TryEnterCriticalSection ((mutex))) ? MHD_YES : MHD_NO)
+#endif
+
+#if defined(MHD_PTHREAD_MUTEX_)
+/**
+ * Unlock previously created and locked mutex.
+ * @param mutex pointer to mutex
+ * @return #MHD_YES on success, #MHD_NO on failure
+ */
+#define MHD_mutex_unlock_(mutex) \
+  ((0 == pthread_mutex_unlock((mutex))) ? MHD_YES : MHD_NO)
+#elif defined(MHD_W32_MUTEX_)
+/**
+ * Unlock previously created and locked mutex.
+ * @param mutex pointer to mutex
+ * @return #MHD_YES on success, #MHD_NO on failure
+ */
+#define MHD_mutex_unlock_(mutex) \
+  ((NULL != (mutex)) ? (LeaveCriticalSection((mutex)), MHD_YES) : MHD_NO)
+#endif
+
+#endif // MHD_PLATFORM_INTERFACE_H
diff --git a/src/include/w32functions.h b/src/include/w32functions.h
new file mode 100644
index 0000000..a86c4b2
--- /dev/null
+++ b/src/include/w32functions.h
@@ -0,0 +1,213 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2014 Karlson2k (Evgeny Grin)
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library.
+  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file include/w32functions.h
+ * @brief  internal functions for W32 systems
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_W32FUNCTIONS_H
+#define MHD_W32FUNCTIONS_H
+#ifndef _WIN32
+#error w32functions.h is designed only for W32 systems
+#endif
+
+#include "platform.h"
+#include <errno.h>
+#include <winsock2.h>
+#include "platform_interface.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define MHDW32ERRBASE 3300
+
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK (MHDW32ERRBASE+1)
+#endif
+#ifndef EINPROGRESS
+#define EINPROGRESS (MHDW32ERRBASE+2)
+#endif
+#ifndef EALREADY
+#define EALREADY (MHDW32ERRBASE+3)
+#endif
+#ifndef ENOTSOCK
+#define ENOTSOCK (MHDW32ERRBASE+4)
+#endif
+#ifndef EDESTADDRREQ
+#define EDESTADDRREQ (MHDW32ERRBASE+5)
+#endif
+#ifndef EMSGSIZE
+#define EMSGSIZE (MHDW32ERRBASE+6)
+#endif
+#ifndef EPROTOTYPE
+#define EPROTOTYPE (MHDW32ERRBASE+7)
+#endif
+#ifndef ENOPROTOOPT
+#define ENOPROTOOPT (MHDW32ERRBASE+8)
+#endif
+#ifndef EPROTONOSUPPORT
+#define EPROTONOSUPPORT (MHDW32ERRBASE+9)
+#endif
+#ifndef EOPNOTSUPP
+#define EOPNOTSUPP (MHDW32ERRBASE+10)
+#endif
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT (MHDW32ERRBASE+11)
+#endif
+#ifndef EADDRINUSE
+#define EADDRINUSE (MHDW32ERRBASE+12)
+#endif
+#ifndef EADDRNOTAVAIL
+#define EADDRNOTAVAIL (MHDW32ERRBASE+13)
+#endif
+#ifndef ENETDOWN
+#define ENETDOWN (MHDW32ERRBASE+14)
+#endif
+#ifndef ENETUNREACH
+#define ENETUNREACH (MHDW32ERRBASE+15)
+#endif
+#ifndef ENETRESET
+#define ENETRESET (MHDW32ERRBASE+16)
+#endif
+#ifndef ECONNABORTED
+#define ECONNABORTED (MHDW32ERRBASE+17)
+#endif
+#ifndef ECONNRESET
+#define ECONNRESET (MHDW32ERRBASE+18)
+#endif
+#ifndef ENOBUFS
+#define ENOBUFS (MHDW32ERRBASE+19)
+#endif
+#ifndef EISCONN
+#define EISCONN (MHDW32ERRBASE+20)
+#endif
+#ifndef ENOTCONN
+#define ENOTCONN (MHDW32ERRBASE+21)
+#endif
+#ifndef ETOOMANYREFS
+#define ETOOMANYREFS (MHDW32ERRBASE+22)
+#endif
+#ifndef ECONNREFUSED
+#define ECONNREFUSED (MHDW32ERRBASE+23)
+#endif
+#ifndef ELOOP
+#define ELOOP (MHDW32ERRBASE+24)
+#endif
+#ifndef EHOSTDOWN
+#define EHOSTDOWN (MHDW32ERRBASE+25)
+#endif
+#ifndef EHOSTUNREACH
+#define EHOSTUNREACH (MHDW32ERRBASE+26)
+#endif
+#ifndef EPROCLIM
+#define EPROCLIM (MHDW32ERRBASE+27)
+#endif
+#ifndef EUSERS
+#define EUSERS (MHDW32ERRBASE+28)
+#endif
+#ifndef EDQUOT
+#define EDQUOT (MHDW32ERRBASE+29)
+#endif
+#ifndef ESTALE
+#define ESTALE (MHDW32ERRBASE+30)
+#endif
+#ifndef EREMOTE
+#define EREMOTE (MHDW32ERRBASE+31)
+#endif
+#ifndef ESOCKTNOSUPPORT
+#define ESOCKTNOSUPPORT (MHDW32ERRBASE+32)
+#endif
+#ifndef EPFNOSUPPORT
+#define EPFNOSUPPORT (MHDW32ERRBASE+33)
+#endif
+#ifndef ESHUTDOWN
+#define ESHUTDOWN (MHDW32ERRBASE+34)
+#endif
+#ifndef ENODATA
+#define ENODATA (MHDW32ERRBASE+35)
+#endif
+#ifndef ETIMEDOUT
+#define ETIMEDOUT (MHDW32ERRBASE+36)
+#endif
+
+/**
+ * Return errno equivalent of last winsock error
+ * @return errno equivalent of last winsock error
+ */
+int MHD_W32_errno_from_winsock_(void);
+
+/**
+ * Return pointer to string description of errnum error
+ * Works fine with both standard errno errnums
+ * and errnums from MHD_W32_errno_from_winsock_
+ * @param errnum the errno or value from MHD_W32_errno_from_winsock_()
+ * @return pointer to string description of error
+ */
+const char* MHD_W32_strerror_(int errnum);
+
+/**
+ * Return pointer to string description of last winsock error
+ * @return pointer to string description of last winsock error
+ */
+const char* MHD_W32_strerror_last_winsock_(void);
+
+/**
+ * Set last winsock error to equivalent of given errno value
+ * @param errnum the errno value to set
+ */
+void MHD_W32_set_last_winsock_error_(int errnum);
+
+/**
+ * Create pair of mutually connected TCP/IP sockets on loopback address
+ * @param sockets_pair array to receive resulted sockets
+ * @return zero on success, -1 otherwise
+ */
+int MHD_W32_pair_of_sockets_(SOCKET sockets_pair[2]);
+
+/**
+ * Generate 31-bit pseudo random number.
+ * Function initialize itself at first call to current time.
+ * @return 31-bit pseudo random number.
+ */
+int MHD_W32_random_(void);
+
+/* Emulate snprintf function on W32 */
+int W32_snprintf(char *__restrict s, size_t n, const char *__restrict format, ...);
+
+#ifndef _MSC_FULL_VER
+/* Thread name available only for VC-compiler */
+static void W32_SetThreadName(const DWORD thread_id, const char *thread_name)
+{ }
+#else  /* _MSC_FULL_VER */
+/**
+ * Set thread name
+ * @param thread_id ID of thread, -1 for current thread
+ * @param thread_name name to set
+ */
+void W32_SetThreadName(const DWORD thread_id, const char *thread_name);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif //MHD_W32FUNCTIONS_H
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am
new file mode 100644
index 0000000..dbf3bac
--- /dev/null
+++ b/src/microhttpd/Makefile.am
@@ -0,0 +1,176 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = \
+  -I$(top_srcdir)/src/include \
+  -I$(top_srcdir)/src/microhttpd
+
+AM_CFLAGS = $(HIDDEN_VISIBILITY_CFLAGS)
+
+if HAVE_W32
+MHD_W32_LIB = $(top_builddir)/src/platform/libplatform_interface.la
+endif
+
+lib_LTLIBRARIES = \
+  libmicrohttpd.la
+
+noinst_DATA =
+MOSTLYCLEANFILES =
+
+if W32_SHARED_LIB_EXP
+W32_MHD_LIB_LDFLAGS = -Wl,--output-def,$(lt_cv_objdir)/libmicrohttpd.def -XCClinker -static-libgcc
+noinst_DATA += $(lt_cv_objdir)/libmicrohttpd.lib $(lt_cv_objdir)/libmicrohttpd.def $(lt_cv_objdir)/libmicrohttpd.exp
+MOSTLYCLEANFILES += $(lt_cv_objdir)/libmicrohttpd.lib $(lt_cv_objdir)/libmicrohttpd.def $(lt_cv_objdir)/libmicrohttpd.exp
+
+$(lt_cv_objdir)/libmicrohttpd.def: libmicrohttpd.la
+
+$(lt_cv_objdir)/libmicrohttpd.exp: $(lt_cv_objdir)/libmicrohttpd.lib
+
+$(lt_cv_objdir)/libmicrohttpd.lib: $(lt_cv_objdir)/libmicrohttpd.def libmicrohttpd.la $(libmicrohttpd_la_OBJECTS)
+if USE_MS_LIB_TOOL
+	@echo Creating $@ and libmicrohttpd.exp by $(MS_LIB_TOOL)... && \
+	dll_name=`$(EGREP) -o dlname=\'.+\' libmicrohttpd.la` && \
+	dll_name=$${dll_name#*\'} && dll_name=$${dll_name%\'} && test -n "$$dll_name" && \
+	echo Creating $$dll_name by $(MS_LIB_TOOL).. && cd "$(lt_cv_objdir)" && \
+	$(MS_LIB_TOOL) -def:libmicrohttpd.def -name:$$dll_name -out:libmicrohttpd.lib $(libmicrohttpd_la_OBJECTS:.lo=.o) && cd ..
+else
+	@echo Creating $@ and libmicrohttpd.exp by $(DLLTOOL)... && \
+	dll_name=`$(EGREP) -o dlname=\'.+\' libmicrohttpd.la` && \
+	dll_name=$${dll_name#*\'} && dll_name=$${dll_name%\'} && test -n "$$dll_name" && \
+	echo Creating $$dll_name by $(DLLTOOL).. && cd "$(lt_cv_objdir)" && \
+	$(DLLTOOL) -d ./libmicrohttpd.def -D $$dll_name -l libmicrohttpd.lib $(libmicrohttpd_la_OBJECTS:.lo=.o) -e ./libmicrohttpd.exp && cd .. &&\
+	echo Created libmicrohttpd.exp and libmicrohttpd.lib.
+endif
+else
+  W32_MHD_LIB_LDFLAGS =
+endif
+
+if W32_STATIC_LIB
+noinst_DATA += $(lt_cv_objdir)/libmicrohttpd-static.lib
+MOSTLYCLEANFILES += $(lt_cv_objdir)/libmicrohttpd-static.lib
+
+$(lt_cv_objdir)/libmicrohttpd-static.lib: libmicrohttpd.la $(libmicrohttpd_la_OBJECTS)
+if USE_MS_LIB_TOOL
+	$(MS_LIB_TOOL) -out:$@ $(libmicrohttpd_la_OBJECTS:.lo=.o)
+else
+	cp $(lt_cv_objdir)/libmicrohttpd.a $@
+endif
+endif
+
+
+libmicrohttpd_la_SOURCES = \
+  connection.c connection.h \
+  reason_phrase.c reason_phrase.h \
+  daemon.c  \
+  internal.c internal.h \
+  memorypool.c memorypool.h \
+  response.c response.h
+libmicrohttpd_la_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(MHD_LIB_CPPFLAGS) \
+  -DBUILDING_MHD_LIB=1
+libmicrohttpd_la_CFLAGS = \
+  $(AM_CFLAGS) $(MHD_LIB_CFLAGS)
+libmicrohttpd_la_LDFLAGS = \
+  $(MHD_LIB_LDFLAGS) \
+  $(W32_MHD_LIB_LDFLAGS) \
+  -version-info @LIB_VERSION_CURRENT@:@LIB_VERSION_REVISION@:@LIB_VERSION_AGE@
+libmicrohttpd_la_LIBADD = \
+  $(MHD_W32_LIB) $(MHD_LIBDEPS)
+libmicrohttpd_la_DEPENDENCIES = \
+  $(MHD_W32_LIB)
+
+if HAVE_W32
+MHD_DLL_RES_SRC = microhttpd_dll_res.rc
+MHD_DLL_RES_LO = libmicrohttpd_la-$(MHD_DLL_RES_SRC:.rc=.lo)
+
+EXTRA_libmicrohttpd_la_DEPENDENCIES = $(MHD_DLL_RES_LO)
+libmicrohttpd_la_LIBADD += $(MHD_DLL_RES_LO)
+
+# General rule is not required, but keep it just in case
+.rc.lo:
+	$(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $< -o $@
+
+# To add dll resource only to .dll file and exclude it form static
+# lib, a little trick was used. Allow libtool to create file.lo,
+# file.o and .libs/file.lo, .libs/file.o files, then overwrite file.o
+# by empty object generated from empty c-file. Later libtool will
+# use .libs/file.o for shared lib and empty file.o for static lib.
+# This implementation is based on trick found in liblzma.
+# Note: windres does not understand '-isystem' flag, so all
+# possible '-isystem' flags are replaced by simple '-I' flags.
+$(MHD_DLL_RES_LO): $(MHD_DLL_RES_SRC)
+	RC_CPP_FLAGS=" $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) " && \
+	$(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $${RC_CPP_FLAGS// -isystem / -I } $< -o $@ && \
+	echo > $@-empty.c && $(CC) $(AM_CFLAGS) $(CFLAGS) -c $@-empty.c -o $(@:.lo=.o) && rm -f $@-empty.c
+endif
+
+if USE_COVERAGE
+  AM_CFLAGS += --coverage
+endif
+
+if !HAVE_TSEARCH
+libmicrohttpd_la_SOURCES += \
+  tsearch.c tsearch.h
+endif
+
+if HAVE_POSTPROCESSOR
+libmicrohttpd_la_SOURCES += \
+  postprocessor.c
+endif
+
+if ENABLE_DAUTH
+libmicrohttpd_la_SOURCES += \
+  digestauth.c \
+  md5.c md5.h
+endif
+
+if ENABLE_BAUTH
+libmicrohttpd_la_SOURCES += \
+  basicauth.c \
+  base64.c base64.h
+endif
+
+if ENABLE_HTTPS
+libmicrohttpd_la_SOURCES += \
+  connection_https.c connection_https.h
+endif
+
+
+
+check_PROGRAMS = \
+  test_daemon
+
+if HAVE_POSTPROCESSOR
+check_PROGRAMS += \
+  test_postprocessor \
+  test_postprocessor_large \
+  test_postprocessor_amp
+endif
+
+TESTS = $(check_PROGRAMS)
+
+test_daemon_SOURCES = \
+  test_daemon.c
+test_daemon_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+test_postprocessor_SOURCES = \
+  test_postprocessor.c
+test_postprocessor_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(GNUTLS_CPPFLAGS)
+test_postprocessor_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(MHD_W32_LIB)
+
+test_postprocessor_amp_SOURCES = \
+  test_postprocessor_amp.c
+test_postprocessor_amp_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(GNUTLS_CPPFLAGS)
+test_postprocessor_amp_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+test_postprocessor_large_SOURCES = \
+  test_postprocessor_large.c
+test_postprocessor_large_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(GNUTLS_CPPFLAGS)
+test_postprocessor_large_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(MHD_W32_LIB)
diff --git a/src/microhttpd/Makefile.in b/src/microhttpd/Makefile.in
new file mode 100644
index 0000000..f6aa7ab
--- /dev/null
+++ b/src/microhttpd/Makefile.in
@@ -0,0 +1,1427 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 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 = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+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@
+@W32_SHARED_LIB_EXP_TRUE@am__append_1 = $(lt_cv_objdir)/libmicrohttpd.lib $(lt_cv_objdir)/libmicrohttpd.def $(lt_cv_objdir)/libmicrohttpd.exp
+@W32_SHARED_LIB_EXP_TRUE@am__append_2 = $(lt_cv_objdir)/libmicrohttpd.lib $(lt_cv_objdir)/libmicrohttpd.def $(lt_cv_objdir)/libmicrohttpd.exp
+@W32_STATIC_LIB_TRUE@am__append_3 = $(lt_cv_objdir)/libmicrohttpd-static.lib
+@W32_STATIC_LIB_TRUE@am__append_4 = $(lt_cv_objdir)/libmicrohttpd-static.lib
+@HAVE_W32_TRUE@am__append_5 = $(MHD_DLL_RES_LO)
+@USE_COVERAGE_TRUE@am__append_6 = --coverage
+@HAVE_TSEARCH_FALSE@am__append_7 = \
+@HAVE_TSEARCH_FALSE@  tsearch.c tsearch.h
+
+@HAVE_POSTPROCESSOR_TRUE@am__append_8 = \
+@HAVE_POSTPROCESSOR_TRUE@  postprocessor.c
+
+@ENABLE_DAUTH_TRUE@am__append_9 = \
+@ENABLE_DAUTH_TRUE@  digestauth.c \
+@ENABLE_DAUTH_TRUE@  md5.c md5.h
+
+@ENABLE_BAUTH_TRUE@am__append_10 = \
+@ENABLE_BAUTH_TRUE@  basicauth.c \
+@ENABLE_BAUTH_TRUE@  base64.c base64.h
+
+@ENABLE_HTTPS_TRUE@am__append_11 = \
+@ENABLE_HTTPS_TRUE@  connection_https.c connection_https.h
+
+check_PROGRAMS = test_daemon$(EXEEXT) $(am__EXEEXT_1)
+@HAVE_POSTPROCESSOR_TRUE@am__append_12 = \
+@HAVE_POSTPROCESSOR_TRUE@  test_postprocessor \
+@HAVE_POSTPROCESSOR_TRUE@  test_postprocessor_large \
+@HAVE_POSTPROCESSOR_TRUE@  test_postprocessor_amp
+
+subdir = src/microhttpd
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(srcdir)/microhttpd_dll_res.rc.in $(top_srcdir)/depcomp \
+	$(top_srcdir)/test-driver
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_compile_flags.m4 \
+	$(top_srcdir)/m4/ax_append_flag.m4 \
+	$(top_srcdir)/m4/ax_check_compile_flag.m4 \
+	$(top_srcdir)/m4/ax_check_link_flag.m4 \
+	$(top_srcdir)/m4/ax_check_openssl.m4 \
+	$(top_srcdir)/m4/ax_count_cpus.m4 \
+	$(top_srcdir)/m4/ax_have_epoll.m4 \
+	$(top_srcdir)/m4/ax_pthread.m4 \
+	$(top_srcdir)/m4/ax_require_defined.m4 \
+	$(top_srcdir)/m4/libcurl.m4 $(top_srcdir)/m4/libgcrypt.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)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/MHD_config.h
+CONFIG_CLEAN_FILES = microhttpd_dll_res.rc
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(libdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+am__libmicrohttpd_la_SOURCES_DIST = connection.c connection.h \
+	reason_phrase.c reason_phrase.h daemon.c internal.c internal.h \
+	memorypool.c memorypool.h response.c response.h tsearch.c \
+	tsearch.h postprocessor.c digestauth.c md5.c md5.h basicauth.c \
+	base64.c base64.h connection_https.c connection_https.h
+@HAVE_TSEARCH_FALSE@am__objects_1 = libmicrohttpd_la-tsearch.lo
+@HAVE_POSTPROCESSOR_TRUE@am__objects_2 =  \
+@HAVE_POSTPROCESSOR_TRUE@	libmicrohttpd_la-postprocessor.lo
+@ENABLE_DAUTH_TRUE@am__objects_3 = libmicrohttpd_la-digestauth.lo \
+@ENABLE_DAUTH_TRUE@	libmicrohttpd_la-md5.lo
+@ENABLE_BAUTH_TRUE@am__objects_4 = libmicrohttpd_la-basicauth.lo \
+@ENABLE_BAUTH_TRUE@	libmicrohttpd_la-base64.lo
+@ENABLE_HTTPS_TRUE@am__objects_5 =  \
+@ENABLE_HTTPS_TRUE@	libmicrohttpd_la-connection_https.lo
+am_libmicrohttpd_la_OBJECTS = libmicrohttpd_la-connection.lo \
+	libmicrohttpd_la-reason_phrase.lo libmicrohttpd_la-daemon.lo \
+	libmicrohttpd_la-internal.lo libmicrohttpd_la-memorypool.lo \
+	libmicrohttpd_la-response.lo $(am__objects_1) $(am__objects_2) \
+	$(am__objects_3) $(am__objects_4) $(am__objects_5)
+libmicrohttpd_la_OBJECTS = $(am_libmicrohttpd_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 = 
+libmicrohttpd_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(libmicrohttpd_la_CFLAGS) $(CFLAGS) \
+	$(libmicrohttpd_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_POSTPROCESSOR_TRUE@am__EXEEXT_1 = test_postprocessor$(EXEEXT) \
+@HAVE_POSTPROCESSOR_TRUE@	test_postprocessor_large$(EXEEXT) \
+@HAVE_POSTPROCESSOR_TRUE@	test_postprocessor_amp$(EXEEXT)
+am_test_daemon_OBJECTS = test_daemon.$(OBJEXT)
+test_daemon_OBJECTS = $(am_test_daemon_OBJECTS)
+test_daemon_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_postprocessor_OBJECTS =  \
+	test_postprocessor-test_postprocessor.$(OBJEXT)
+test_postprocessor_OBJECTS = $(am_test_postprocessor_OBJECTS)
+test_postprocessor_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la $(MHD_W32_LIB)
+am_test_postprocessor_amp_OBJECTS =  \
+	test_postprocessor_amp-test_postprocessor_amp.$(OBJEXT)
+test_postprocessor_amp_OBJECTS = $(am_test_postprocessor_amp_OBJECTS)
+test_postprocessor_amp_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_postprocessor_large_OBJECTS =  \
+	test_postprocessor_large-test_postprocessor_large.$(OBJEXT)
+test_postprocessor_large_OBJECTS =  \
+	$(am_test_postprocessor_large_OBJECTS)
+test_postprocessor_large_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la $(MHD_W32_LIB)
+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__depfiles_maybe = depfiles
+am__mv = mv -f
+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 = $(libmicrohttpd_la_SOURCES) $(test_daemon_SOURCES) \
+	$(test_postprocessor_SOURCES) \
+	$(test_postprocessor_amp_SOURCES) \
+	$(test_postprocessor_large_SOURCES)
+DIST_SOURCES = $(am__libmicrohttpd_la_SOURCES_DIST) \
+	$(test_daemon_SOURCES) $(test_postprocessor_SOURCES) \
+	$(test_postprocessor_amp_SOURCES) \
+	$(test_postprocessor_large_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+DATA = $(noinst_DATA)
+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__tty_colors_dummy = \
+  mgn= red= grn= lgn= blu= brg= std=; \
+  am__color_tests=no
+am__tty_colors = { \
+  $(am__tty_colors_dummy); \
+  if test "X$(AM_COLOR_TESTS)" = Xno; then \
+    am__color_tests=no; \
+  elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+    am__color_tests=yes; \
+  elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+    am__color_tests=yes; \
+  fi; \
+  if test $$am__color_tests = yes; then \
+    red=''; \
+    grn=''; \
+    lgn=''; \
+    blu=''; \
+    mgn=''; \
+    brg=''; \
+    std=''; \
+  fi; \
+}
+am__recheck_rx = ^[ 	]*:recheck:[ 	]*
+am__global_test_result_rx = ^[ 	]*:global-test-result:[ 	]*
+am__copy_in_global_log_rx = ^[ 	]*:copy-in-global-log:[ 	]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+  recheck = 1; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+        { \
+          if ((getline line2 < ($$0 ".log")) < 0) \
+	    recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+        { \
+          recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+        { \
+          break; \
+        } \
+    }; \
+  if (recheck) \
+    print $$0; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+  print "fatal: making $@: " msg | "cat >&2"; \
+  exit 1; \
+} \
+function rst_section(header) \
+{ \
+  print header; \
+  len = length(header); \
+  for (i = 1; i <= len; i = i + 1) \
+    printf "="; \
+  printf "\n\n"; \
+} \
+{ \
+  copy_in_global_log = 1; \
+  global_test_result = "RUN"; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+         fatal("failed to read from " $$0 ".trs"); \
+      if (line ~ /$(am__global_test_result_rx)/) \
+        { \
+          sub("$(am__global_test_result_rx)", "", line); \
+          sub("[ 	]*$$", "", line); \
+          global_test_result = line; \
+        } \
+      else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+        copy_in_global_log = 0; \
+    }; \
+  if (copy_in_global_log) \
+    { \
+      rst_section(global_test_result ": " $$0); \
+      while ((rc = (getline line < ($$0 ".log"))) != 0) \
+      { \
+        if (rc < 0) \
+          fatal("failed to read from " $$0 ".log"); \
+        print line; \
+      }; \
+      printf "\n"; \
+    }; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/   &   /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this.  Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+  --color-tests "$$am__color_tests" \
+  --enable-hard-errors "$$am__enable_hard_errors" \
+  --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test.  Creates the
+# directory for the log if needed.  Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log.  Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT.  Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup);					\
+$(am__vpath_adj_setup) $(am__vpath_adj)			\
+$(am__tty_colors);					\
+srcdir=$(srcdir); export srcdir;			\
+case "$@" in						\
+  */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;;	\
+    *) am__odir=.;; 					\
+esac;							\
+test "x$$am__odir" = x"." || test -d "$$am__odir" 	\
+  || $(MKDIR_P) "$$am__odir" || exit $$?;		\
+if test -f "./$$f"; then dir=./;			\
+elif test -f "$$f"; then dir=;				\
+else dir="$(srcdir)/"; fi;				\
+tst=$$dir$$f; log='$@'; 				\
+if test -n '$(DISABLE_HARD_ERRORS)'; then		\
+  am__enable_hard_errors=no; 				\
+else							\
+  am__enable_hard_errors=yes; 				\
+fi; 							\
+case " $(XFAIL_TESTS) " in				\
+  *[\ \	]$$f[\ \	]* | *[\ \	]$$dir$$f[\ \	]*) \
+    am__expect_failure=yes;;				\
+  *)							\
+    am__expect_failure=no;;				\
+esac; 							\
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed).  The result is saved in the shell variable
+# '$bases'.  This honors runtime overriding of TESTS and TEST_LOGS.  Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+  bases='$(TEST_LOGS)'; \
+  bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+  bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+  case '$@' in \
+    */*) \
+      case '$*' in \
+        */*) b='$*';; \
+          *) b=`echo '$@' | sed 's/\.log$$//'`; \
+       esac;; \
+    *) \
+      b='$*';; \
+  esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+	$(TEST_LOG_FLAGS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPU_COUNT = @CPU_COUNT@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_CPPFLAGS = @GNUTLS_CPPFLAGS@
+GNUTLS_LDFLAGS = @GNUTLS_LDFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+HAVE_CURL_BINARY = @HAVE_CURL_BINARY@
+HAVE_MAKEINFO_BINARY = @HAVE_MAKEINFO_BINARY@
+HIDDEN_VISIBILITY_CFLAGS = @HIDDEN_VISIBILITY_CFLAGS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSPDY_VERSION_AGE = @LIBSPDY_VERSION_AGE@
+LIBSPDY_VERSION_CURRENT = @LIBSPDY_VERSION_CURRENT@
+LIBSPDY_VERSION_REVISION = @LIBSPDY_VERSION_REVISION@
+LIBTOOL = @LIBTOOL@
+LIB_VERSION_AGE = @LIB_VERSION_AGE@
+LIB_VERSION_CURRENT = @LIB_VERSION_CURRENT@
+LIB_VERSION_REVISION = @LIB_VERSION_REVISION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MHD_LIBDEPS = @MHD_LIBDEPS@
+MHD_LIBDEPS_PKGCFG = @MHD_LIBDEPS_PKGCFG@
+MHD_LIB_CFLAGS = @MHD_LIB_CFLAGS@
+MHD_LIB_CPPFLAGS = @MHD_LIB_CPPFLAGS@
+MHD_LIB_LDFLAGS = @MHD_LIB_LDFLAGS@
+MHD_REQ_PRIVATE = @MHD_REQ_PRIVATE@
+MKDIR_P = @MKDIR_P@
+MS_LIB_TOOL = @MS_LIB_TOOL@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_INCLUDES = @OPENSSL_INCLUDES@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@
+PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@
+PACKAGE_VERSION_SUBMINOR = @PACKAGE_VERSION_SUBMINOR@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPDY_LIBDEPS = @SPDY_LIBDEPS@
+SPDY_LIB_CFLAGS = @SPDY_LIB_CFLAGS@
+SPDY_LIB_CPPFLAGS = @SPDY_LIB_CPPFLAGS@
+SPDY_LIB_LDFLAGS = @SPDY_LIB_LDFLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+_libcurl_config = @_libcurl_config@
+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_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@
+ax_pthread_config = @ax_pthread_config@
+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@
+have_socat = @have_socat@
+have_zzuf = @have_zzuf@
+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@
+lt_cv_objdir = @lt_cv_objdir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = \
+  -I$(top_srcdir)/src/include \
+  -I$(top_srcdir)/src/microhttpd
+
+AM_CFLAGS = $(HIDDEN_VISIBILITY_CFLAGS) $(am__append_6)
+@HAVE_W32_TRUE@MHD_W32_LIB = $(top_builddir)/src/platform/libplatform_interface.la
+lib_LTLIBRARIES = \
+  libmicrohttpd.la
+
+noinst_DATA = $(am__append_1) $(am__append_3)
+MOSTLYCLEANFILES = $(am__append_2) $(am__append_4)
+@W32_SHARED_LIB_EXP_FALSE@W32_MHD_LIB_LDFLAGS = 
+@W32_SHARED_LIB_EXP_TRUE@W32_MHD_LIB_LDFLAGS = -Wl,--output-def,$(lt_cv_objdir)/libmicrohttpd.def -XCClinker -static-libgcc
+libmicrohttpd_la_SOURCES = connection.c connection.h reason_phrase.c \
+	reason_phrase.h daemon.c internal.c internal.h memorypool.c \
+	memorypool.h response.c response.h $(am__append_7) \
+	$(am__append_8) $(am__append_9) $(am__append_10) \
+	$(am__append_11)
+libmicrohttpd_la_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(MHD_LIB_CPPFLAGS) \
+  -DBUILDING_MHD_LIB=1
+
+libmicrohttpd_la_CFLAGS = \
+  $(AM_CFLAGS) $(MHD_LIB_CFLAGS)
+
+libmicrohttpd_la_LDFLAGS = \
+  $(MHD_LIB_LDFLAGS) \
+  $(W32_MHD_LIB_LDFLAGS) \
+  -version-info @LIB_VERSION_CURRENT@:@LIB_VERSION_REVISION@:@LIB_VERSION_AGE@
+
+libmicrohttpd_la_LIBADD = $(MHD_W32_LIB) $(MHD_LIBDEPS) \
+	$(am__append_5)
+libmicrohttpd_la_DEPENDENCIES = \
+  $(MHD_W32_LIB)
+
+@HAVE_W32_TRUE@MHD_DLL_RES_SRC = microhttpd_dll_res.rc
+@HAVE_W32_TRUE@MHD_DLL_RES_LO = libmicrohttpd_la-$(MHD_DLL_RES_SRC:.rc=.lo)
+@HAVE_W32_TRUE@EXTRA_libmicrohttpd_la_DEPENDENCIES = $(MHD_DLL_RES_LO)
+TESTS = $(check_PROGRAMS)
+test_daemon_SOURCES = \
+  test_daemon.c
+
+test_daemon_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+test_postprocessor_SOURCES = \
+  test_postprocessor.c
+
+test_postprocessor_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(GNUTLS_CPPFLAGS)
+
+test_postprocessor_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(MHD_W32_LIB)
+
+test_postprocessor_amp_SOURCES = \
+  test_postprocessor_amp.c
+
+test_postprocessor_amp_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(GNUTLS_CPPFLAGS)
+
+test_postprocessor_amp_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+test_postprocessor_large_SOURCES = \
+  test_postprocessor_large.c
+
+test_postprocessor_large_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(GNUTLS_CPPFLAGS)
+
+test_postprocessor_large_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(MHD_W32_LIB)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .log .o .obj .rc .test .test$(EXEEXT) .trs
+$(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) --gnu src/microhttpd/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/microhttpd/Makefile
+.PRECIOUS: 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__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	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):
+microhttpd_dll_res.rc: $(top_builddir)/config.status $(srcdir)/microhttpd_dll_res.rc.in
+	cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+	}
+
+uninstall-libLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+	done
+
+clean-libLTLIBRARIES:
+	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+	@list='$(lib_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}; \
+	}
+
+libmicrohttpd.la: $(libmicrohttpd_la_OBJECTS) $(libmicrohttpd_la_DEPENDENCIES) $(EXTRA_libmicrohttpd_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(libmicrohttpd_la_LINK) -rpath $(libdir) $(libmicrohttpd_la_OBJECTS) $(libmicrohttpd_la_LIBADD) $(LIBS)
+
+clean-checkPROGRAMS:
+	@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+
+test_daemon$(EXEEXT): $(test_daemon_OBJECTS) $(test_daemon_DEPENDENCIES) $(EXTRA_test_daemon_DEPENDENCIES) 
+	@rm -f test_daemon$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_daemon_OBJECTS) $(test_daemon_LDADD) $(LIBS)
+
+test_postprocessor$(EXEEXT): $(test_postprocessor_OBJECTS) $(test_postprocessor_DEPENDENCIES) $(EXTRA_test_postprocessor_DEPENDENCIES) 
+	@rm -f test_postprocessor$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_postprocessor_OBJECTS) $(test_postprocessor_LDADD) $(LIBS)
+
+test_postprocessor_amp$(EXEEXT): $(test_postprocessor_amp_OBJECTS) $(test_postprocessor_amp_DEPENDENCIES) $(EXTRA_test_postprocessor_amp_DEPENDENCIES) 
+	@rm -f test_postprocessor_amp$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_postprocessor_amp_OBJECTS) $(test_postprocessor_amp_LDADD) $(LIBS)
+
+test_postprocessor_large$(EXEEXT): $(test_postprocessor_large_OBJECTS) $(test_postprocessor_large_DEPENDENCIES) $(EXTRA_test_postprocessor_large_DEPENDENCIES) 
+	@rm -f test_postprocessor_large$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_postprocessor_large_OBJECTS) $(test_postprocessor_large_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrohttpd_la-base64.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrohttpd_la-basicauth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrohttpd_la-connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrohttpd_la-connection_https.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrohttpd_la-daemon.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrohttpd_la-digestauth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrohttpd_la-internal.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrohttpd_la-md5.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrohttpd_la-memorypool.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrohttpd_la-postprocessor.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrohttpd_la-reason_phrase.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrohttpd_la-response.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrohttpd_la-tsearch.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_daemon.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_postprocessor-test_postprocessor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_postprocessor_amp-test_postprocessor_amp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_postprocessor_large-test_postprocessor_large.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libmicrohttpd_la-connection.lo: connection.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -MT libmicrohttpd_la-connection.lo -MD -MP -MF $(DEPDIR)/libmicrohttpd_la-connection.Tpo -c -o libmicrohttpd_la-connection.lo `test -f 'connection.c' || echo '$(srcdir)/'`connection.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrohttpd_la-connection.Tpo $(DEPDIR)/libmicrohttpd_la-connection.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='connection.c' object='libmicrohttpd_la-connection.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -c -o libmicrohttpd_la-connection.lo `test -f 'connection.c' || echo '$(srcdir)/'`connection.c
+
+libmicrohttpd_la-reason_phrase.lo: reason_phrase.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -MT libmicrohttpd_la-reason_phrase.lo -MD -MP -MF $(DEPDIR)/libmicrohttpd_la-reason_phrase.Tpo -c -o libmicrohttpd_la-reason_phrase.lo `test -f 'reason_phrase.c' || echo '$(srcdir)/'`reason_phrase.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrohttpd_la-reason_phrase.Tpo $(DEPDIR)/libmicrohttpd_la-reason_phrase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='reason_phrase.c' object='libmicrohttpd_la-reason_phrase.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -c -o libmicrohttpd_la-reason_phrase.lo `test -f 'reason_phrase.c' || echo '$(srcdir)/'`reason_phrase.c
+
+libmicrohttpd_la-daemon.lo: daemon.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -MT libmicrohttpd_la-daemon.lo -MD -MP -MF $(DEPDIR)/libmicrohttpd_la-daemon.Tpo -c -o libmicrohttpd_la-daemon.lo `test -f 'daemon.c' || echo '$(srcdir)/'`daemon.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrohttpd_la-daemon.Tpo $(DEPDIR)/libmicrohttpd_la-daemon.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='daemon.c' object='libmicrohttpd_la-daemon.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -c -o libmicrohttpd_la-daemon.lo `test -f 'daemon.c' || echo '$(srcdir)/'`daemon.c
+
+libmicrohttpd_la-internal.lo: internal.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -MT libmicrohttpd_la-internal.lo -MD -MP -MF $(DEPDIR)/libmicrohttpd_la-internal.Tpo -c -o libmicrohttpd_la-internal.lo `test -f 'internal.c' || echo '$(srcdir)/'`internal.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrohttpd_la-internal.Tpo $(DEPDIR)/libmicrohttpd_la-internal.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='internal.c' object='libmicrohttpd_la-internal.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -c -o libmicrohttpd_la-internal.lo `test -f 'internal.c' || echo '$(srcdir)/'`internal.c
+
+libmicrohttpd_la-memorypool.lo: memorypool.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -MT libmicrohttpd_la-memorypool.lo -MD -MP -MF $(DEPDIR)/libmicrohttpd_la-memorypool.Tpo -c -o libmicrohttpd_la-memorypool.lo `test -f 'memorypool.c' || echo '$(srcdir)/'`memorypool.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrohttpd_la-memorypool.Tpo $(DEPDIR)/libmicrohttpd_la-memorypool.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='memorypool.c' object='libmicrohttpd_la-memorypool.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -c -o libmicrohttpd_la-memorypool.lo `test -f 'memorypool.c' || echo '$(srcdir)/'`memorypool.c
+
+libmicrohttpd_la-response.lo: response.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -MT libmicrohttpd_la-response.lo -MD -MP -MF $(DEPDIR)/libmicrohttpd_la-response.Tpo -c -o libmicrohttpd_la-response.lo `test -f 'response.c' || echo '$(srcdir)/'`response.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrohttpd_la-response.Tpo $(DEPDIR)/libmicrohttpd_la-response.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='response.c' object='libmicrohttpd_la-response.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -c -o libmicrohttpd_la-response.lo `test -f 'response.c' || echo '$(srcdir)/'`response.c
+
+libmicrohttpd_la-tsearch.lo: tsearch.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -MT libmicrohttpd_la-tsearch.lo -MD -MP -MF $(DEPDIR)/libmicrohttpd_la-tsearch.Tpo -c -o libmicrohttpd_la-tsearch.lo `test -f 'tsearch.c' || echo '$(srcdir)/'`tsearch.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrohttpd_la-tsearch.Tpo $(DEPDIR)/libmicrohttpd_la-tsearch.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tsearch.c' object='libmicrohttpd_la-tsearch.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -c -o libmicrohttpd_la-tsearch.lo `test -f 'tsearch.c' || echo '$(srcdir)/'`tsearch.c
+
+libmicrohttpd_la-postprocessor.lo: postprocessor.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -MT libmicrohttpd_la-postprocessor.lo -MD -MP -MF $(DEPDIR)/libmicrohttpd_la-postprocessor.Tpo -c -o libmicrohttpd_la-postprocessor.lo `test -f 'postprocessor.c' || echo '$(srcdir)/'`postprocessor.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrohttpd_la-postprocessor.Tpo $(DEPDIR)/libmicrohttpd_la-postprocessor.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='postprocessor.c' object='libmicrohttpd_la-postprocessor.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -c -o libmicrohttpd_la-postprocessor.lo `test -f 'postprocessor.c' || echo '$(srcdir)/'`postprocessor.c
+
+libmicrohttpd_la-digestauth.lo: digestauth.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -MT libmicrohttpd_la-digestauth.lo -MD -MP -MF $(DEPDIR)/libmicrohttpd_la-digestauth.Tpo -c -o libmicrohttpd_la-digestauth.lo `test -f 'digestauth.c' || echo '$(srcdir)/'`digestauth.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrohttpd_la-digestauth.Tpo $(DEPDIR)/libmicrohttpd_la-digestauth.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='digestauth.c' object='libmicrohttpd_la-digestauth.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -c -o libmicrohttpd_la-digestauth.lo `test -f 'digestauth.c' || echo '$(srcdir)/'`digestauth.c
+
+libmicrohttpd_la-md5.lo: md5.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -MT libmicrohttpd_la-md5.lo -MD -MP -MF $(DEPDIR)/libmicrohttpd_la-md5.Tpo -c -o libmicrohttpd_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrohttpd_la-md5.Tpo $(DEPDIR)/libmicrohttpd_la-md5.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='md5.c' object='libmicrohttpd_la-md5.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -c -o libmicrohttpd_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c
+
+libmicrohttpd_la-basicauth.lo: basicauth.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -MT libmicrohttpd_la-basicauth.lo -MD -MP -MF $(DEPDIR)/libmicrohttpd_la-basicauth.Tpo -c -o libmicrohttpd_la-basicauth.lo `test -f 'basicauth.c' || echo '$(srcdir)/'`basicauth.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrohttpd_la-basicauth.Tpo $(DEPDIR)/libmicrohttpd_la-basicauth.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='basicauth.c' object='libmicrohttpd_la-basicauth.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -c -o libmicrohttpd_la-basicauth.lo `test -f 'basicauth.c' || echo '$(srcdir)/'`basicauth.c
+
+libmicrohttpd_la-base64.lo: base64.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -MT libmicrohttpd_la-base64.lo -MD -MP -MF $(DEPDIR)/libmicrohttpd_la-base64.Tpo -c -o libmicrohttpd_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrohttpd_la-base64.Tpo $(DEPDIR)/libmicrohttpd_la-base64.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='base64.c' object='libmicrohttpd_la-base64.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -c -o libmicrohttpd_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+
+libmicrohttpd_la-connection_https.lo: connection_https.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -MT libmicrohttpd_la-connection_https.lo -MD -MP -MF $(DEPDIR)/libmicrohttpd_la-connection_https.Tpo -c -o libmicrohttpd_la-connection_https.lo `test -f 'connection_https.c' || echo '$(srcdir)/'`connection_https.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrohttpd_la-connection_https.Tpo $(DEPDIR)/libmicrohttpd_la-connection_https.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='connection_https.c' object='libmicrohttpd_la-connection_https.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) $(libmicrohttpd_la_CFLAGS) $(CFLAGS) -c -o libmicrohttpd_la-connection_https.lo `test -f 'connection_https.c' || echo '$(srcdir)/'`connection_https.c
+
+test_postprocessor-test_postprocessor.o: test_postprocessor.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_postprocessor_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_postprocessor-test_postprocessor.o -MD -MP -MF $(DEPDIR)/test_postprocessor-test_postprocessor.Tpo -c -o test_postprocessor-test_postprocessor.o `test -f 'test_postprocessor.c' || echo '$(srcdir)/'`test_postprocessor.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_postprocessor-test_postprocessor.Tpo $(DEPDIR)/test_postprocessor-test_postprocessor.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_postprocessor.c' object='test_postprocessor-test_postprocessor.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_postprocessor_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_postprocessor-test_postprocessor.o `test -f 'test_postprocessor.c' || echo '$(srcdir)/'`test_postprocessor.c
+
+test_postprocessor-test_postprocessor.obj: test_postprocessor.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_postprocessor_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_postprocessor-test_postprocessor.obj -MD -MP -MF $(DEPDIR)/test_postprocessor-test_postprocessor.Tpo -c -o test_postprocessor-test_postprocessor.obj `if test -f 'test_postprocessor.c'; then $(CYGPATH_W) 'test_postprocessor.c'; else $(CYGPATH_W) '$(srcdir)/test_postprocessor.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_postprocessor-test_postprocessor.Tpo $(DEPDIR)/test_postprocessor-test_postprocessor.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_postprocessor.c' object='test_postprocessor-test_postprocessor.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_postprocessor_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_postprocessor-test_postprocessor.obj `if test -f 'test_postprocessor.c'; then $(CYGPATH_W) 'test_postprocessor.c'; else $(CYGPATH_W) '$(srcdir)/test_postprocessor.c'; fi`
+
+test_postprocessor_amp-test_postprocessor_amp.o: test_postprocessor_amp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_postprocessor_amp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_postprocessor_amp-test_postprocessor_amp.o -MD -MP -MF $(DEPDIR)/test_postprocessor_amp-test_postprocessor_amp.Tpo -c -o test_postprocessor_amp-test_postprocessor_amp.o `test -f 'test_postprocessor_amp.c' || echo '$(srcdir)/'`test_postprocessor_amp.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_postprocessor_amp-test_postprocessor_amp.Tpo $(DEPDIR)/test_postprocessor_amp-test_postprocessor_amp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_postprocessor_amp.c' object='test_postprocessor_amp-test_postprocessor_amp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_postprocessor_amp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_postprocessor_amp-test_postprocessor_amp.o `test -f 'test_postprocessor_amp.c' || echo '$(srcdir)/'`test_postprocessor_amp.c
+
+test_postprocessor_amp-test_postprocessor_amp.obj: test_postprocessor_amp.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_postprocessor_amp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_postprocessor_amp-test_postprocessor_amp.obj -MD -MP -MF $(DEPDIR)/test_postprocessor_amp-test_postprocessor_amp.Tpo -c -o test_postprocessor_amp-test_postprocessor_amp.obj `if test -f 'test_postprocessor_amp.c'; then $(CYGPATH_W) 'test_postprocessor_amp.c'; else $(CYGPATH_W) '$(srcdir)/test_postprocessor_amp.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_postprocessor_amp-test_postprocessor_amp.Tpo $(DEPDIR)/test_postprocessor_amp-test_postprocessor_amp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_postprocessor_amp.c' object='test_postprocessor_amp-test_postprocessor_amp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_postprocessor_amp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_postprocessor_amp-test_postprocessor_amp.obj `if test -f 'test_postprocessor_amp.c'; then $(CYGPATH_W) 'test_postprocessor_amp.c'; else $(CYGPATH_W) '$(srcdir)/test_postprocessor_amp.c'; fi`
+
+test_postprocessor_large-test_postprocessor_large.o: test_postprocessor_large.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_postprocessor_large_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_postprocessor_large-test_postprocessor_large.o -MD -MP -MF $(DEPDIR)/test_postprocessor_large-test_postprocessor_large.Tpo -c -o test_postprocessor_large-test_postprocessor_large.o `test -f 'test_postprocessor_large.c' || echo '$(srcdir)/'`test_postprocessor_large.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_postprocessor_large-test_postprocessor_large.Tpo $(DEPDIR)/test_postprocessor_large-test_postprocessor_large.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_postprocessor_large.c' object='test_postprocessor_large-test_postprocessor_large.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_postprocessor_large_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_postprocessor_large-test_postprocessor_large.o `test -f 'test_postprocessor_large.c' || echo '$(srcdir)/'`test_postprocessor_large.c
+
+test_postprocessor_large-test_postprocessor_large.obj: test_postprocessor_large.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_postprocessor_large_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_postprocessor_large-test_postprocessor_large.obj -MD -MP -MF $(DEPDIR)/test_postprocessor_large-test_postprocessor_large.Tpo -c -o test_postprocessor_large-test_postprocessor_large.obj `if test -f 'test_postprocessor_large.c'; then $(CYGPATH_W) 'test_postprocessor_large.c'; else $(CYGPATH_W) '$(srcdir)/test_postprocessor_large.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_postprocessor_large-test_postprocessor_large.Tpo $(DEPDIR)/test_postprocessor_large-test_postprocessor_large.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_postprocessor_large.c' object='test_postprocessor_large-test_postprocessor_large.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_postprocessor_large_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_postprocessor_large-test_postprocessor_large.obj `if test -f 'test_postprocessor_large.c'; then $(CYGPATH_W) 'test_postprocessor_large.c'; else $(CYGPATH_W) '$(srcdir)/test_postprocessor_large.c'; fi`
+
+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
+
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'.  Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+	rm -f $< $@
+	$(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+	@:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+	@$(am__set_TESTS_bases); \
+	am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+	redo_bases=`for i in $$bases; do \
+	              am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+	            done`; \
+	if test -n "$$redo_bases"; then \
+	  redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+	  redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+	  if $(am__make_dryrun); then :; else \
+	    rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
+	  fi; \
+	fi; \
+	if test -n "$$am__remaking_logs"; then \
+	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+	       "recursion detected" >&2; \
+	else \
+	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+	fi; \
+	if $(am__make_dryrun); then :; else \
+	  st=0;  \
+	  errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+	  for i in $$redo_bases; do \
+	    test -f $$i.trs && test -r $$i.trs \
+	      || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+	    test -f $$i.log && test -r $$i.log \
+	      || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+	  done; \
+	  test $$st -eq 0 || exit 1; \
+	fi
+	@$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+	ws='[ 	]'; \
+	results=`for b in $$bases; do echo $$b.trs; done`; \
+	test -n "$$results" || results=/dev/null; \
+	all=`  grep "^$$ws*:test-result:"           $$results | wc -l`; \
+	pass=` grep "^$$ws*:test-result:$$ws*PASS"  $$results | wc -l`; \
+	fail=` grep "^$$ws*:test-result:$$ws*FAIL"  $$results | wc -l`; \
+	skip=` grep "^$$ws*:test-result:$$ws*SKIP"  $$results | wc -l`; \
+	xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+	xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+	error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+	if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+	  success=true; \
+	else \
+	  success=false; \
+	fi; \
+	br='==================='; br=$$br$$br$$br$$br; \
+	result_count () \
+	{ \
+	    if test x"$$1" = x"--maybe-color"; then \
+	      maybe_colorize=yes; \
+	    elif test x"$$1" = x"--no-color"; then \
+	      maybe_colorize=no; \
+	    else \
+	      echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+	    fi; \
+	    shift; \
+	    desc=$$1 count=$$2; \
+	    if test $$maybe_colorize = yes && test $$count -gt 0; then \
+	      color_start=$$3 color_end=$$std; \
+	    else \
+	      color_start= color_end=; \
+	    fi; \
+	    echo "$${color_start}# $$desc $$count$${color_end}"; \
+	}; \
+	create_testsuite_report () \
+	{ \
+	  result_count $$1 "TOTAL:" $$all   "$$brg"; \
+	  result_count $$1 "PASS: " $$pass  "$$grn"; \
+	  result_count $$1 "SKIP: " $$skip  "$$blu"; \
+	  result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+	  result_count $$1 "FAIL: " $$fail  "$$red"; \
+	  result_count $$1 "XPASS:" $$xpass "$$red"; \
+	  result_count $$1 "ERROR:" $$error "$$mgn"; \
+	}; \
+	{								\
+	  echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" |	\
+	    $(am__rst_title);						\
+	  create_testsuite_report --no-color;				\
+	  echo;								\
+	  echo ".. contents:: :depth: 2";				\
+	  echo;								\
+	  for b in $$bases; do echo $$b; done				\
+	    | $(am__create_global_log);					\
+	} >$(TEST_SUITE_LOG).tmp || exit 1;				\
+	mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG);			\
+	if $$success; then						\
+	  col="$$grn";							\
+	 else								\
+	  col="$$red";							\
+	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
+	fi;								\
+	echo "$${col}$$br$${std}"; 					\
+	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
+	echo "$${col}$$br$${std}"; 					\
+	create_testsuite_report --maybe-color;				\
+	echo "$$col$$br$$std";						\
+	if $$success; then :; else					\
+	  echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}";		\
+	  if test -n "$(PACKAGE_BUGREPORT)"; then			\
+	    echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}";	\
+	  fi;								\
+	  echo "$$col$$br$$std";					\
+	fi;								\
+	$$success || exit 1
+
+check-TESTS:
+	@list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
+	@list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+	log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+	exit $$?;
+recheck: all $(check_PROGRAMS)
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	bases=`for i in $$bases; do echo $$i; done \
+	         | $(am__list_recheck_tests)` || exit 1; \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	log_list=`echo $$log_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+	        am__force_recheck=am--force-recheck \
+	        TEST_LOGS="$$log_list"; \
+	exit $$?
+test_daemon.log: test_daemon$(EXEEXT)
+	@p='test_daemon$(EXEEXT)'; \
+	b='test_daemon'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_postprocessor.log: test_postprocessor$(EXEEXT)
+	@p='test_postprocessor$(EXEEXT)'; \
+	b='test_postprocessor'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_postprocessor_large.log: test_postprocessor_large$(EXEEXT)
+	@p='test_postprocessor_large$(EXEEXT)'; \
+	b='test_postprocessor_large'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_postprocessor_amp.log: test_postprocessor_amp$(EXEEXT)
+	@p='test_postprocessor_amp$(EXEEXT)'; \
+	b='test_postprocessor_amp'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+	@p='$<'; \
+	$(am__set_b); \
+	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@	@p='$<'; \
+@am__EXEEXT_TRUE@	$(am__set_b); \
+@am__EXEEXT_TRUE@	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@	--log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@	"$$tst" $(AM_TESTS_FD_REDIRECT)
+
+distdir: $(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
+	$(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(DATA)
+installdirs:
+	for dir in "$(DESTDIR)$(libdir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+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:
+	-test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
+	-test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+	-test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+	-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+
+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-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
+	clean-libtool mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-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-libLTLIBRARIES
+
+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 -rf ./$(DEPDIR)
+	-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: uninstall-libLTLIBRARIES
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \
+	clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
+	clean-libtool 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-libLTLIBRARIES 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 \
+	recheck tags tags-am uninstall uninstall-am \
+	uninstall-libLTLIBRARIES
+
+
+@W32_SHARED_LIB_EXP_TRUE@$(lt_cv_objdir)/libmicrohttpd.def: libmicrohttpd.la
+
+@W32_SHARED_LIB_EXP_TRUE@$(lt_cv_objdir)/libmicrohttpd.exp: $(lt_cv_objdir)/libmicrohttpd.lib
+
+@W32_SHARED_LIB_EXP_TRUE@$(lt_cv_objdir)/libmicrohttpd.lib: $(lt_cv_objdir)/libmicrohttpd.def libmicrohttpd.la $(libmicrohttpd_la_OBJECTS)
+@USE_MS_LIB_TOOL_TRUE@@W32_SHARED_LIB_EXP_TRUE@	@echo Creating $@ and libmicrohttpd.exp by $(MS_LIB_TOOL)... && \
+@USE_MS_LIB_TOOL_TRUE@@W32_SHARED_LIB_EXP_TRUE@	dll_name=`$(EGREP) -o dlname=\'.+\' libmicrohttpd.la` && \
+@USE_MS_LIB_TOOL_TRUE@@W32_SHARED_LIB_EXP_TRUE@	dll_name=$${dll_name#*\'} && dll_name=$${dll_name%\'} && test -n "$$dll_name" && \
+@USE_MS_LIB_TOOL_TRUE@@W32_SHARED_LIB_EXP_TRUE@	echo Creating $$dll_name by $(MS_LIB_TOOL).. && cd "$(lt_cv_objdir)" && \
+@USE_MS_LIB_TOOL_TRUE@@W32_SHARED_LIB_EXP_TRUE@	$(MS_LIB_TOOL) -def:libmicrohttpd.def -name:$$dll_name -out:libmicrohttpd.lib $(libmicrohttpd_la_OBJECTS:.lo=.o) && cd ..
+@USE_MS_LIB_TOOL_FALSE@@W32_SHARED_LIB_EXP_TRUE@	@echo Creating $@ and libmicrohttpd.exp by $(DLLTOOL)... && \
+@USE_MS_LIB_TOOL_FALSE@@W32_SHARED_LIB_EXP_TRUE@	dll_name=`$(EGREP) -o dlname=\'.+\' libmicrohttpd.la` && \
+@USE_MS_LIB_TOOL_FALSE@@W32_SHARED_LIB_EXP_TRUE@	dll_name=$${dll_name#*\'} && dll_name=$${dll_name%\'} && test -n "$$dll_name" && \
+@USE_MS_LIB_TOOL_FALSE@@W32_SHARED_LIB_EXP_TRUE@	echo Creating $$dll_name by $(DLLTOOL).. && cd "$(lt_cv_objdir)" && \
+@USE_MS_LIB_TOOL_FALSE@@W32_SHARED_LIB_EXP_TRUE@	$(DLLTOOL) -d ./libmicrohttpd.def -D $$dll_name -l libmicrohttpd.lib $(libmicrohttpd_la_OBJECTS:.lo=.o) -e ./libmicrohttpd.exp && cd .. &&\
+@USE_MS_LIB_TOOL_FALSE@@W32_SHARED_LIB_EXP_TRUE@	echo Created libmicrohttpd.exp and libmicrohttpd.lib.
+
+@W32_STATIC_LIB_TRUE@$(lt_cv_objdir)/libmicrohttpd-static.lib: libmicrohttpd.la $(libmicrohttpd_la_OBJECTS)
+@USE_MS_LIB_TOOL_TRUE@@W32_STATIC_LIB_TRUE@	$(MS_LIB_TOOL) -out:$@ $(libmicrohttpd_la_OBJECTS:.lo=.o)
+@USE_MS_LIB_TOOL_FALSE@@W32_STATIC_LIB_TRUE@	cp $(lt_cv_objdir)/libmicrohttpd.a $@
+
+# General rule is not required, but keep it just in case
+@HAVE_W32_TRUE@.rc.lo:
+@HAVE_W32_TRUE@	$(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $< -o $@
+
+# To add dll resource only to .dll file and exclude it form static
+# lib, a little trick was used. Allow libtool to create file.lo,
+# file.o and .libs/file.lo, .libs/file.o files, then overwrite file.o
+# by empty object generated from empty c-file. Later libtool will
+# use .libs/file.o for shared lib and empty file.o for static lib.
+# This implementation is based on trick found in liblzma.
+# Note: windres does not understand '-isystem' flag, so all
+# possible '-isystem' flags are replaced by simple '-I' flags.
+@HAVE_W32_TRUE@$(MHD_DLL_RES_LO): $(MHD_DLL_RES_SRC)
+@HAVE_W32_TRUE@	RC_CPP_FLAGS=" $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd_la_CPPFLAGS) $(CPPFLAGS) " && \
+@HAVE_W32_TRUE@	$(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $${RC_CPP_FLAGS// -isystem / -I } $< -o $@ && \
+@HAVE_W32_TRUE@	echo > $@-empty.c && $(CC) $(AM_CFLAGS) $(CFLAGS) -c $@-empty.c -o $(@:.lo=.o) && rm -f $@-empty.c
+
+# 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/src/microhttpd/base64.c b/src/microhttpd/base64.c
new file mode 100644
index 0000000..4fb4755
--- /dev/null
+++ b/src/microhttpd/base64.c
@@ -0,0 +1,62 @@
+/*
+ * This code implements the BASE64 algorithm.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * @file base64.c
+ * @brief This code implements the BASE64 algorithm
+ * @author Matthieu Speder
+ */
+#include "base64.h"
+
+static const char base64_chars[] =
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static const char base64_digits[] =
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+    0, 0, 0, -1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+    14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26,
+    27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+    45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+
+char *
+BASE64Decode(const char* src)
+{
+  size_t in_len = strlen (src);
+  char* dest;
+  char* result;
+
+  if (in_len % 4)
+    {
+      /* Wrong base64 string length */
+      return NULL;
+    }
+  result = dest = malloc(in_len / 4 * 3 + 1);
+  if (result == NULL)
+    return NULL; /* out of memory */
+  while (*src) {
+    char a = base64_digits[(unsigned char)*(src++)];
+    char b = base64_digits[(unsigned char)*(src++)];
+    char c = base64_digits[(unsigned char)*(src++)];
+    char d = base64_digits[(unsigned char)*(src++)];
+    *(dest++) = (a << 2) | ((b & 0x30) >> 4);
+    if (c == (char)-1)
+      break;
+    *(dest++) = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2);
+    if (d == (char)-1)
+      break;
+    *(dest++) = ((c & 0x03) << 6) | d;
+  }
+  *dest = 0;
+  return result;
+}
+
+
+/* end of base64.c */
diff --git a/src/microhttpd/base64.h b/src/microhttpd/base64.h
new file mode 100644
index 0000000..ba96ca0
--- /dev/null
+++ b/src/microhttpd/base64.h
@@ -0,0 +1,17 @@
+/*
+ * This code implements the BASE64 algorithm.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * @file base64.c
+ * @brief This code implements the BASE64 algorithm
+ * @author Matthieu Speder
+ */
+#ifndef BASE64_H
+#define BASE64_H
+
+#include "platform.h"
+
+char *
+BASE64Decode(const char* src);
+
+#endif /* !BASE64_H */
diff --git a/src/microhttpd/basicauth.c b/src/microhttpd/basicauth.c
new file mode 100644
index 0000000..cbe0fc7
--- /dev/null
+++ b/src/microhttpd/basicauth.c
@@ -0,0 +1,148 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2010, 2011, 2012 Daniel Pittman and Christian Grothoff
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+/**
+ * @file basicauth.c
+ * @brief Implements HTTP basic authentication methods
+ * @author Amr Ali
+ * @author Matthieu Speder
+ */
+#include "platform.h"
+#include <limits.h>
+#include "internal.h"
+#include "base64.h"
+
+/**
+ * Beginning string for any valid Basic authentication header.
+ */
+#define _BASIC_BASE		"Basic "
+
+
+/**
+ * Get the username and password from the basic authorization header sent by the client
+ *
+ * @param connection The MHD connection structure
+ * @param password a pointer for the password
+ * @return NULL if no username could be found, a pointer
+ * 			to the username if found
+ * @ingroup authentication
+ */
+char *
+MHD_basic_auth_get_username_password (struct MHD_Connection *connection,
+				      char** password) 
+{
+  const char *header;
+  char *decode;
+  const char *separator;
+  char *user;
+  
+  if ( (NULL == (header = MHD_lookup_connection_value (connection, 
+						       MHD_HEADER_KIND,
+						       MHD_HTTP_HEADER_AUTHORIZATION))) ||
+       (0 != strncmp (header, _BASIC_BASE, strlen(_BASIC_BASE))) )
+    return NULL;
+  header += strlen (_BASIC_BASE);
+  if (NULL == (decode = BASE64Decode (header)))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+		"Error decoding basic authentication\n");
+#endif
+      return NULL;
+    }
+  /* Find user:password pattern */
+  if (NULL == (separator = strchr (decode, ':')))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG(connection->daemon,
+	       "Basic authentication doesn't contain ':' separator\n");
+#endif
+      free (decode);
+      return NULL;
+    }
+  if (NULL == (user = strdup (decode)))
+    {
+      free (decode);
+      return NULL;
+    }
+  user[separator - decode] = '\0'; /* cut off at ':' */
+  if (NULL != password) 
+    {
+      *password = strdup (separator + 1);  
+      if (NULL == *password)
+	{
+#if HAVE_MESSAGES
+	  MHD_DLOG(connection->daemon,
+		   "Failed to allocate memory for password\n");
+#endif
+	  free (decode);
+	  free (user);
+	  return NULL;
+	}
+    }
+  free (decode);
+  return user;
+}
+
+
+/**
+ * Queues a response to request basic authentication from the client.
+ * The given response object is expected to include the payload for
+ * the response; the "WWW-Authenticate" header will be added and the
+ * response queued with the 'UNAUTHORIZED' status code.
+ *
+ * @param connection The MHD connection structure
+ * @param realm the realm presented to the client
+ * @param response response object to modify and queue
+ * @return #MHD_YES on success, #MHD_NO otherwise
+ * @ingroup authentication
+ */
+int 
+MHD_queue_basic_auth_fail_response (struct MHD_Connection *connection,
+				    const char *realm, 
+				    struct MHD_Response *response) 
+{
+  int ret;
+  size_t hlen = strlen(realm) + strlen("Basic realm=\"\"") + 1;
+  char *header;
+  
+  header = (char*)malloc(hlen);
+  if (NULL == header)
+  {
+#if HAVE_MESSAGES
+    MHD_DLOG(connection->daemon,
+		   "Failed to allocate memory for auth header\n");
+#endif /* HAVE_MESSAGES */
+    return MHD_NO;
+  }
+  MHD_snprintf_ (header, 
+	    hlen, 
+	    "Basic realm=\"%s\"", 
+	    realm);
+  ret = MHD_add_response_header (response,
+				 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
+				 header);
+  free(header);
+  if (MHD_YES == ret)
+    ret = MHD_queue_response (connection, 
+			      MHD_HTTP_UNAUTHORIZED, 
+			      response);
+  return ret;
+}
+
+/* end of basicauth.c */
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
new file mode 100644
index 0000000..785fafd
--- /dev/null
+++ b/src/microhttpd/connection.c
@@ -0,0 +1,2919 @@
+/*
+    This file is part of libmicrohttpd
+     Copyright (C) 2007-2015 Daniel Pittman and Christian Grothoff
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file connection.c
+ * @brief  Methods for managing connections
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ */
+
+#include "internal.h"
+#include <limits.h>
+#include "connection.h"
+#include "memorypool.h"
+#include "response.h"
+#include "reason_phrase.h"
+
+#if HAVE_NETINET_TCP_H
+/* for TCP_CORK */
+#include <netinet/tcp.h>
+#endif
+
+#if defined(_WIN32) && defined(MHD_W32_MUTEX_)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif /* _WIN32 && MHD_W32_MUTEX_ */
+
+
+/**
+ * Message to transmit when http 1.1 request is received
+ */
+#define HTTP_100_CONTINUE "HTTP/1.1 100 Continue\r\n\r\n"
+
+/**
+ * Response text used when the request (http header) is too big to
+ * be processed.
+ *
+ * Intentionally empty here to keep our memory footprint
+ * minimal.
+ */
+#if HAVE_MESSAGES
+#define REQUEST_TOO_BIG "<html><head><title>Request too big</title></head><body>Your HTTP header was too big for the memory constraints of this webserver.</body></html>"
+#else
+#define REQUEST_TOO_BIG ""
+#endif
+
+/**
+ * Response text used when the request (http header) does not
+ * contain a "Host:" header and still claims to be HTTP 1.1.
+ *
+ * Intentionally empty here to keep our memory footprint
+ * minimal.
+ */
+#if HAVE_MESSAGES
+#define REQUEST_LACKS_HOST "<html><head><title>&quot;Host:&quot; header required</title></head><body>In HTTP 1.1, requests must include a &quot;Host:&quot; header, and your HTTP 1.1 request lacked such a header.</body></html>"
+#else
+#define REQUEST_LACKS_HOST ""
+#endif
+
+/**
+ * Response text used when the request (http header) is
+ * malformed.
+ *
+ * Intentionally empty here to keep our memory footprint
+ * minimal.
+ */
+#if HAVE_MESSAGES
+#define REQUEST_MALFORMED "<html><head><title>Request malformed</title></head><body>Your HTTP request was syntactically incorrect.</body></html>"
+#else
+#define REQUEST_MALFORMED ""
+#endif
+
+/**
+ * Response text used when there is an internal server error.
+ *
+ * Intentionally empty here to keep our memory footprint
+ * minimal.
+ */
+#if HAVE_MESSAGES
+#define INTERNAL_ERROR "<html><head><title>Internal server error</title></head><body>Some programmer needs to study the manual more carefully.</body></html>"
+#else
+#define INTERNAL_ERROR ""
+#endif
+
+/**
+ * Add extra debug messages with reasons for closing connections
+ * (non-error reasons).
+ */
+#define DEBUG_CLOSE MHD_NO
+
+/**
+ * Should all data send be printed to stderr?
+ */
+#define DEBUG_SEND_DATA MHD_NO
+
+
+/**
+ * Get all of the headers from the request.
+ *
+ * @param connection connection to get values from
+ * @param kind types of values to iterate over
+ * @param iterator callback to call on each header;
+ *        maybe NULL (then just count headers)
+ * @param iterator_cls extra argument to @a iterator
+ * @return number of entries iterated over
+ * @ingroup request
+ */
+int
+MHD_get_connection_values (struct MHD_Connection *connection,
+                           enum MHD_ValueKind kind,
+                           MHD_KeyValueIterator iterator, void *iterator_cls)
+{
+  int ret;
+  struct MHD_HTTP_Header *pos;
+
+  if (NULL == connection)
+    return -1;
+  ret = 0;
+  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
+    if (0 != (pos->kind & kind))
+      {
+	ret++;
+	if ((NULL != iterator) &&
+	    (MHD_YES != iterator (iterator_cls,
+				  kind, pos->header, pos->value)))
+	  return ret;
+      }
+  return ret;
+}
+
+
+/**
+ * This function can be used to add an entry to the HTTP headers of a
+ * connection (so that the #MHD_get_connection_values function will
+ * return them -- and the `struct MHD_PostProcessor` will also see
+ * them).  This maybe required in certain situations (see Mantis
+ * #1399) where (broken) HTTP implementations fail to supply values
+ * needed by the post processor (or other parts of the application).
+ *
+ * This function MUST only be called from within the
+ * #MHD_AccessHandlerCallback (otherwise, access maybe improperly
+ * synchronized).  Furthermore, the client must guarantee that the key
+ * and value arguments are 0-terminated strings that are NOT freed
+ * until the connection is closed.  (The easiest way to do this is by
+ * passing only arguments to permanently allocated strings.).
+ *
+ * @param connection the connection for which a
+ *  value should be set
+ * @param kind kind of the value
+ * @param key key for the value
+ * @param value the value itself
+ * @return #MHD_NO if the operation could not be
+ *         performed due to insufficient memory;
+ *         #MHD_YES on success
+ * @ingroup request
+ */
+int
+MHD_set_connection_value (struct MHD_Connection *connection,
+                          enum MHD_ValueKind kind,
+                          const char *key, const char *value)
+{
+  struct MHD_HTTP_Header *pos;
+
+  pos = MHD_pool_allocate (connection->pool,
+                           sizeof (struct MHD_HTTP_Header), MHD_YES);
+  if (NULL == pos)
+    return MHD_NO;
+  pos->header = (char *) key;
+  pos->value = (char *) value;
+  pos->kind = kind;
+  pos->next = NULL;
+  /* append 'pos' to the linked list of headers */
+  if (NULL == connection->headers_received_tail)
+    {
+      connection->headers_received = pos;
+      connection->headers_received_tail = pos;
+    }
+  else
+    {
+      connection->headers_received_tail->next = pos;
+      connection->headers_received_tail = pos;
+    }
+  return MHD_YES;
+}
+
+
+/**
+ * Get a particular header value.  If multiple
+ * values match the kind, return any one of them.
+ *
+ * @param connection connection to get values from
+ * @param kind what kind of value are we looking for
+ * @param key the header to look for, NULL to lookup 'trailing' value without a key
+ * @return NULL if no such item was found
+ * @ingroup request
+ */
+const char *
+MHD_lookup_connection_value (struct MHD_Connection *connection,
+                             enum MHD_ValueKind kind, const char *key)
+{
+  struct MHD_HTTP_Header *pos;
+
+  if (NULL == connection)
+    return NULL;
+  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
+    if ((0 != (pos->kind & kind)) &&
+	( (key == pos->header) ||
+	  ( (NULL != pos->header) &&
+	    (NULL != key) &&
+        (MHD_str_equal_caseless_(key, pos->header)))))
+      return pos->value;
+  return NULL;
+}
+
+
+/**
+ * Do we (still) need to send a 100 continue
+ * message for this connection?
+ *
+ * @param connection connection to test
+ * @return 0 if we don't need 100 CONTINUE, 1 if we do
+ */
+static int
+need_100_continue (struct MHD_Connection *connection)
+{
+  const char *expect;
+
+  return ( (NULL == connection->response) &&
+	   (NULL != connection->version) &&
+       (MHD_str_equal_caseless_(connection->version,
+			     MHD_HTTP_VERSION_1_1)) &&
+	   (NULL != (expect = MHD_lookup_connection_value (connection,
+							   MHD_HEADER_KIND,
+							   MHD_HTTP_HEADER_EXPECT))) &&
+	   (MHD_str_equal_caseless_(expect, "100-continue")) &&
+	   (connection->continue_message_write_offset <
+	    strlen (HTTP_100_CONTINUE)) );
+}
+
+
+/**
+ * Close the given connection and give the
+ * specified termination code to the user.
+ *
+ * @param connection connection to close
+ * @param termination_code termination reason to give
+ */
+void
+MHD_connection_close (struct MHD_Connection *connection,
+                      enum MHD_RequestTerminationCode termination_code)
+{
+  struct MHD_Daemon *daemon;
+
+  daemon = connection->daemon;
+  if (0 == (connection->daemon->options & MHD_USE_EPOLL_TURBO))
+    shutdown (connection->socket_fd,
+	      (MHD_YES == connection->read_closed) ? SHUT_WR : SHUT_RDWR);
+  connection->state = MHD_CONNECTION_CLOSED;
+  connection->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
+  if ( (NULL != daemon->notify_completed) &&
+       (MHD_YES == connection->client_aware) )
+    daemon->notify_completed (daemon->notify_completed_cls,
+			      connection,
+			      &connection->client_context,
+			      termination_code);
+  connection->client_aware = MHD_NO;
+}
+
+
+/**
+ * A serious error occured, close the
+ * connection (and notify the application).
+ *
+ * @param connection connection to close with error
+ * @param emsg error message (can be NULL)
+ */
+static void
+connection_close_error (struct MHD_Connection *connection,
+			const char *emsg)
+{
+#if HAVE_MESSAGES
+  if (NULL != emsg)
+    MHD_DLOG (connection->daemon, emsg);
+#endif
+  MHD_connection_close (connection, MHD_REQUEST_TERMINATED_WITH_ERROR);
+}
+
+
+/**
+ * Macro to only include error message in call to
+ * "connection_close_error" if we have HAVE_MESSAGES.
+ */
+#if HAVE_MESSAGES
+#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, emsg)
+#else
+#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, NULL)
+#endif
+
+
+/**
+ * Prepare the response buffer of this connection for
+ * sending.  Assumes that the response mutex is
+ * already held.  If the transmission is complete,
+ * this function may close the socket (and return
+ * #MHD_NO).
+ *
+ * @param connection the connection
+ * @return #MHD_NO if readying the response failed (the
+ *  lock on the response will have been released already
+ *  in this case).
+ */
+static int
+try_ready_normal_body (struct MHD_Connection *connection)
+{
+  ssize_t ret;
+  struct MHD_Response *response;
+
+  response = connection->response;
+  if (NULL == response->crc)
+    return MHD_YES;
+  if (0 == response->total_size)
+    return MHD_YES; /* 0-byte response is always ready */
+  if ( (response->data_start <=
+	connection->response_write_position) &&
+       (response->data_size + response->data_start >
+	connection->response_write_position) )
+    return MHD_YES; /* response already ready */
+#if LINUX
+  if ( (MHD_INVALID_SOCKET != response->fd) &&
+       (0 == (connection->daemon->options & MHD_USE_SSL)) )
+    {
+      /* will use sendfile, no need to bother response crc */
+      return MHD_YES;
+    }
+#endif
+
+  ret = response->crc (response->crc_cls,
+                       connection->response_write_position,
+                       response->data,
+                       MHD_MIN (response->data_buffer_size,
+                                response->total_size -
+                                connection->response_write_position));
+  if ( (((ssize_t) MHD_CONTENT_READER_END_OF_STREAM) == ret) ||
+       (((ssize_t) MHD_CONTENT_READER_END_WITH_ERROR) == ret) )
+    {
+      /* either error or http 1.0 transfer, close socket! */
+      response->total_size = connection->response_write_position;
+      if (NULL != response->crc)
+        (void) MHD_mutex_unlock_ (&response->mutex);
+      if ( ((ssize_t)MHD_CONTENT_READER_END_OF_STREAM) == ret)
+	MHD_connection_close (connection, MHD_REQUEST_TERMINATED_COMPLETED_OK);
+      else
+	CONNECTION_CLOSE_ERROR (connection,
+				"Closing connection (stream error)\n");
+      return MHD_NO;
+    }
+  response->data_start = connection->response_write_position;
+  response->data_size = ret;
+  if (0 == ret)
+    {
+      connection->state = MHD_CONNECTION_NORMAL_BODY_UNREADY;
+      if (NULL != response->crc)
+        (void) MHD_mutex_unlock_ (&response->mutex);
+      return MHD_NO;
+    }
+  return MHD_YES;
+}
+
+
+/**
+ * Prepare the response buffer of this connection for sending.
+ * Assumes that the response mutex is already held.  If the
+ * transmission is complete, this function may close the socket (and
+ * return MHD_NO).
+ *
+ * @param connection the connection
+ * @return #MHD_NO if readying the response failed
+ */
+static int
+try_ready_chunked_body (struct MHD_Connection *connection)
+{
+  ssize_t ret;
+  char *buf;
+  struct MHD_Response *response;
+  size_t size;
+  char cbuf[10];                /* 10: max strlen of "%x\r\n" */
+  size_t cblen;
+
+  response = connection->response;
+  if (0 == connection->write_buffer_size)
+    {
+      size = connection->daemon->pool_size;
+      do
+        {
+          size /= 2;
+          if (size < 128)
+            {
+              /* not enough memory */
+              CONNECTION_CLOSE_ERROR (connection,
+				      "Closing connection (out of memory)\n");
+              return MHD_NO;
+            }
+          buf = MHD_pool_allocate (connection->pool, size, MHD_NO);
+        }
+      while (NULL == buf);
+      connection->write_buffer_size = size;
+      connection->write_buffer = buf;
+    }
+
+  if ( (response->data_start <=
+	connection->response_write_position) &&
+       (response->data_size + response->data_start >
+	connection->response_write_position) )
+    {
+      /* buffer already ready, use what is there for the chunk */
+      ret = response->data_size + response->data_start - connection->response_write_position;
+      if ( (ret > 0) &&
+           (((size_t) ret) > connection->write_buffer_size - sizeof (cbuf) - 2) )
+	ret = connection->write_buffer_size - sizeof (cbuf) - 2;
+      memcpy (&connection->write_buffer[sizeof (cbuf)],
+	      &response->data[connection->response_write_position - response->data_start],
+	      ret);
+    }
+  else
+    {
+      /* buffer not in range, try to fill it */
+      if (0 == response->total_size)
+	ret = 0; /* response must be empty, don't bother calling crc */
+      else
+	ret = response->crc (response->crc_cls,
+			     connection->response_write_position,
+			     &connection->write_buffer[sizeof (cbuf)],
+			     connection->write_buffer_size - sizeof (cbuf) - 2);
+    }
+  if ( ((ssize_t) MHD_CONTENT_READER_END_WITH_ERROR) == ret)
+    {
+      /* error, close socket! */
+      response->total_size = connection->response_write_position;
+      CONNECTION_CLOSE_ERROR (connection,
+			      "Closing connection (error generating response)\n");
+      return MHD_NO;
+    }
+  if ( (((ssize_t) MHD_CONTENT_READER_END_OF_STREAM) == ret) ||
+       (0 == response->total_size) )
+    {
+      /* end of message, signal other side! */
+      strcpy (connection->write_buffer, "0\r\n");
+      connection->write_buffer_append_offset = 3;
+      connection->write_buffer_send_offset = 0;
+      response->total_size = connection->response_write_position;
+      return MHD_YES;
+    }
+  if (0 == ret)
+    {
+      connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
+      return MHD_NO;
+    }
+  if (ret > 0xFFFFFF)
+    ret = 0xFFFFFF;
+  MHD_snprintf_ (cbuf,
+	    sizeof (cbuf),
+	    "%X\r\n", (unsigned int) ret);
+  cblen = strlen (cbuf);
+  EXTRA_CHECK (cblen <= sizeof (cbuf));
+  memcpy (&connection->write_buffer[sizeof (cbuf) - cblen], cbuf, cblen);
+  memcpy (&connection->write_buffer[sizeof (cbuf) + ret], "\r\n", 2);
+  connection->response_write_position += ret;
+  connection->write_buffer_send_offset = sizeof (cbuf) - cblen;
+  connection->write_buffer_append_offset = sizeof (cbuf) + ret + 2;
+  return MHD_YES;
+}
+
+
+/**
+ * Are we allowed to keep the given connection alive?  We can use the
+ * TCP stream for a second request if the connection is HTTP 1.1 and
+ * the "Connection" header either does not exist or is not set to
+ * "close", or if the connection is HTTP 1.0 and the "Connection"
+ * header is explicitly set to "keep-alive".  If no HTTP version is
+ * specified (or if it is not 1.0 or 1.1), we definitively close the
+ * connection.  If the "Connection" header is not exactly "close" or
+ * "keep-alive", we proceed to use the default for the respective HTTP
+ * version (which is conservative for HTTP 1.0, but might be a bit
+ * optimistic for HTTP 1.1).
+ *
+ * @param connection the connection to check for keepalive
+ * @return #MHD_YES if (based on the request), a keepalive is
+ *        legal
+ */
+static int
+keepalive_possible (struct MHD_Connection *connection)
+{
+  const char *end;
+
+  if (NULL == connection->version)
+    return MHD_NO;
+  if ( (NULL != connection->response) &&
+       (0 != (connection->response->flags & MHD_RF_HTTP_VERSION_1_0_ONLY) ) )
+    return MHD_NO;
+  end = MHD_lookup_connection_value (connection,
+                                     MHD_HEADER_KIND,
+                                     MHD_HTTP_HEADER_CONNECTION);
+  if (MHD_str_equal_caseless_(connection->version,
+                       MHD_HTTP_VERSION_1_1))
+  {
+    if (NULL == end)
+      return MHD_YES;
+    if ( (MHD_str_equal_caseless_ (end, "close")) ||
+         (MHD_str_equal_caseless_ (end, "upgrade")) )
+      return MHD_NO;
+   return MHD_YES;
+  }
+  if (MHD_str_equal_caseless_(connection->version,
+                       MHD_HTTP_VERSION_1_0))
+  {
+    if (NULL == end)
+      return MHD_NO;
+    if (MHD_str_equal_caseless_(end, "Keep-Alive"))
+      return MHD_YES;
+    return MHD_NO;
+  }
+  return MHD_NO;
+}
+
+
+/**
+ * Produce HTTP "Date:" header.
+ *
+ * @param date where to write the header, with
+ *        at least 128 bytes available space.
+ */
+static void
+get_date_string (char *date)
+{
+  static const char *const days[] =
+    { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+  static const char *const mons[] =
+    { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
+    "Nov", "Dec"
+  };
+  struct tm now;
+  time_t t;
+#if defined(_WIN32) && !defined(HAVE_GMTIME_S) && !defined(__CYGWIN__)
+  struct tm* pNow;
+#endif
+
+  date[0] = 0;
+  time (&t);
+#if !defined(_WIN32)
+  if (NULL != gmtime_r (&t, &now))
+    {
+#elif defined(HAVE_GMTIME_S)
+  if (0 == gmtime_s (&now, &t))
+    {
+#elif defined(__CYGWIN__)
+  if (NULL != gmtime_r (&t, &now))
+    {
+#else
+  pNow = gmtime(&t);
+  if (NULL != pNow)
+    {
+      now = *pNow;
+#endif
+      sprintf (date,
+             "Date: %3s, %02u %3s %04u %02u:%02u:%02u GMT\r\n",
+             days[now.tm_wday % 7],
+             (unsigned int) now.tm_mday,
+             mons[now.tm_mon % 12],
+             (unsigned int) (1900 + now.tm_year),
+             (unsigned int) now.tm_hour,
+             (unsigned int) now.tm_min,
+             (unsigned int) now.tm_sec);
+    }
+}
+
+
+/**
+ * Try growing the read buffer.  We initially claim half the
+ * available buffer space for the read buffer (the other half
+ * being left for management data structures; the write
+ * buffer can in the end take virtually everything as the
+ * read buffer can be reduced to the minimum necessary at that
+ * point.
+ *
+ * @param connection the connection
+ * @return #MHD_YES on success, #MHD_NO on failure
+ */
+static int
+try_grow_read_buffer (struct MHD_Connection *connection)
+{
+  void *buf;
+  size_t new_size;
+
+  if (0 == connection->read_buffer_size)
+    new_size = connection->daemon->pool_size / 2;
+  else
+    new_size = connection->read_buffer_size + MHD_BUF_INC_SIZE;
+  buf = MHD_pool_reallocate (connection->pool,
+                             connection->read_buffer,
+                             connection->read_buffer_size,
+                             new_size);
+  if (NULL == buf)
+    return MHD_NO;
+  /* we can actually grow the buffer, do it! */
+  connection->read_buffer = buf;
+  connection->read_buffer_size = new_size;
+  return MHD_YES;
+}
+
+
+/**
+ * Allocate the connection's write buffer and fill it with all of the
+ * headers (or footers, if we have already sent the body) from the
+ * HTTPd's response.  If headers are missing in the response supplied
+ * by the application, additional headers may be added here.
+ *
+ * @param connection the connection
+ * @return #MHD_YES on success, #MHD_NO on failure (out of memory)
+ */
+static int
+build_header_response (struct MHD_Connection *connection)
+{
+  size_t size;
+  size_t off;
+  struct MHD_HTTP_Header *pos;
+  char code[256];
+  char date[128];
+  char content_length_buf[128];
+  size_t content_length_len;
+  char *data;
+  enum MHD_ValueKind kind;
+  const char *reason_phrase;
+  uint32_t rc;
+  const char *client_requested_close;
+  const char *response_has_close;
+  const char *response_has_keepalive;
+  const char *have_encoding;
+  const char *have_content_length;
+  int must_add_close;
+  int must_add_chunked_encoding;
+  int must_add_keep_alive;
+  int must_add_content_length;
+
+  EXTRA_CHECK (NULL != connection->version);
+  if (0 == strlen (connection->version))
+    {
+      data = MHD_pool_allocate (connection->pool, 0, MHD_YES);
+      connection->write_buffer = data;
+      connection->write_buffer_append_offset = 0;
+      connection->write_buffer_send_offset = 0;
+      connection->write_buffer_size = 0;
+      return MHD_YES;
+    }
+  if (MHD_CONNECTION_FOOTERS_RECEIVED == connection->state)
+    {
+      rc = connection->responseCode & (~MHD_ICY_FLAG);
+      reason_phrase = MHD_get_reason_phrase_for (rc);
+      sprintf (code,
+               "%s %u %s\r\n",
+	       (0 != (connection->responseCode & MHD_ICY_FLAG))
+	       ? "ICY"
+	       : ( (MHD_str_equal_caseless_ (MHD_HTTP_VERSION_1_0,
+				     connection->version))
+		   ? MHD_HTTP_VERSION_1_0
+		   : MHD_HTTP_VERSION_1_1),
+	       rc,
+	       reason_phrase);
+      off = strlen (code);
+      /* estimate size */
+      size = off + 2;           /* +2 for extra "\r\n" at the end */
+      kind = MHD_HEADER_KIND;
+      if ( (0 == (connection->daemon->options & MHD_SUPPRESS_DATE_NO_CLOCK)) &&
+	   (NULL == MHD_get_response_header (connection->response,
+					     MHD_HTTP_HEADER_DATE)) )
+        get_date_string (date);
+      else
+        date[0] = '\0';
+      size += strlen (date);
+    }
+  else
+    {
+      /* 2 bytes for final CRLF of a Chunked-Body */
+      size = 2;
+      kind = MHD_FOOTER_KIND;
+      off = 0;
+    }
+
+  /* calculate extra headers we need to add, such as 'Connection: close',
+     first see what was explicitly requested by the application */
+  must_add_close = MHD_NO;
+  must_add_chunked_encoding = MHD_NO;
+  must_add_keep_alive = MHD_NO;
+  must_add_content_length = MHD_NO;
+  switch (connection->state)
+    {
+    case MHD_CONNECTION_FOOTERS_RECEIVED:
+      response_has_close = MHD_get_response_header (connection->response,
+                                                    MHD_HTTP_HEADER_CONNECTION);
+      response_has_keepalive = response_has_close;
+      if ( (NULL != response_has_close) &&
+           (!MHD_str_equal_caseless_ (response_has_close, "close")) )
+        response_has_close = NULL;
+      if ( (NULL != response_has_keepalive) &&
+           (!MHD_str_equal_caseless_ (response_has_keepalive, "Keep-Alive")) )
+        response_has_keepalive = NULL;
+      client_requested_close = MHD_lookup_connection_value (connection,
+                                                            MHD_HEADER_KIND,
+                                                            MHD_HTTP_HEADER_CONNECTION);
+      if ( (NULL != client_requested_close) &&
+           (!MHD_str_equal_caseless_ (client_requested_close, "close")) )
+        client_requested_close = NULL;
+
+      /* now analyze chunked encoding situation */
+      connection->have_chunked_upload = MHD_NO;
+
+      if ( (MHD_SIZE_UNKNOWN == connection->response->total_size) &&
+           (NULL == response_has_close) &&
+           (NULL == client_requested_close) )
+        {
+          /* size is unknown, and close was not explicitly requested;
+             need to either to HTTP 1.1 chunked encoding or
+             close the connection */
+          /* 'close' header doesn't exist yet, see if we need to add one;
+             if the client asked for a close, no need to start chunk'ing */
+          if ( (MHD_YES == keepalive_possible (connection)) &&
+               (MHD_str_equal_caseless_ (MHD_HTTP_VERSION_1_1,
+                                         connection->version) ) )
+            {
+              have_encoding = MHD_get_response_header (connection->response,
+                                                       MHD_HTTP_HEADER_TRANSFER_ENCODING);
+              if (NULL == have_encoding)
+                {
+                  must_add_chunked_encoding = MHD_YES;
+                  connection->have_chunked_upload = MHD_YES;
+                }
+              else if (MHD_str_equal_caseless_(have_encoding, "identity"))
+                {
+                  /* application forced identity encoding, can't do 'chunked' */
+                  must_add_close = MHD_YES;
+                }
+              else
+                {
+                  connection->have_chunked_upload = MHD_YES;
+                }
+            }
+          else
+            {
+              /* Keep alive or chunking not possible
+                 => set close header if not present */
+              if (NULL == response_has_close)
+                must_add_close = MHD_YES;
+            }
+        }
+
+      /* check for other reasons to add 'close' header */
+      if ( ( (NULL != client_requested_close) ||
+             (MHD_YES == connection->read_closed) ) &&
+           (NULL == response_has_close) &&
+           (0 == (connection->response->flags & MHD_RF_HTTP_VERSION_1_0_ONLY) ) )
+        must_add_close = MHD_YES;
+
+      /* check if we should add a 'content length' header */
+      have_content_length = MHD_get_response_header (connection->response,
+                                                     MHD_HTTP_HEADER_CONTENT_LENGTH);
+
+      if ( (MHD_SIZE_UNKNOWN != connection->response->total_size) &&
+           (NULL == have_content_length) &&
+           ( (NULL == connection->method) ||
+             (! MHD_str_equal_caseless_ (connection->method,
+                                         MHD_HTTP_METHOD_CONNECT)) ) )
+        {
+          /*
+            Here we add a content-length if one is missing; however,
+            for 'connect' methods, the responses MUST NOT include a
+            content-length header *if* the response code is 2xx (in
+            which case we expect there to be no body).  Still,
+            as we don't know the response code here in some cases, we
+            simply only force adding a content-length header if this
+            is not a 'connect' or if the response is not empty
+            (which is kind of more sane, because if some crazy
+            application did return content with a 2xx status code,
+            then having a content-length might again be a good idea).
+
+            Note that the change from 'SHOULD NOT' to 'MUST NOT' is
+            a recent development of the HTTP 1.1 specification.
+          */
+          content_length_len
+            = sprintf (content_length_buf,
+                       MHD_HTTP_HEADER_CONTENT_LENGTH ": " MHD_UNSIGNED_LONG_LONG_PRINTF "\r\n",
+                       (MHD_UNSIGNED_LONG_LONG) connection->response->total_size);
+          must_add_content_length = MHD_YES;
+        }
+
+      /* check for adding keep alive */
+      if ( (NULL == response_has_keepalive) &&
+           (NULL == response_has_close) &&
+           (MHD_NO == must_add_close) &&
+           (0 == (connection->response->flags & MHD_RF_HTTP_VERSION_1_0_ONLY) ) &&
+           (MHD_YES == keepalive_possible (connection)) )
+        must_add_keep_alive = MHD_YES;
+      break;
+    case MHD_CONNECTION_BODY_SENT:
+      break;
+    default:
+      EXTRA_CHECK (0);
+    }
+
+  if (must_add_close)
+    size += strlen ("Connection: close\r\n");
+  if (must_add_keep_alive)
+    size += strlen ("Connection: Keep-Alive\r\n");
+  if (must_add_chunked_encoding)
+    size += strlen ("Transfer-Encoding: chunked\r\n");
+  if (must_add_content_length)
+    size += content_length_len;
+  EXTRA_CHECK (! (must_add_close && must_add_keep_alive) );
+  EXTRA_CHECK (! (must_add_chunked_encoding && must_add_content_length) );
+
+  for (pos = connection->response->first_header; NULL != pos; pos = pos->next)
+    if ( (pos->kind == kind) &&
+         (! ( (MHD_YES == must_add_close) &&
+              (pos->value == response_has_keepalive) &&
+              (MHD_str_equal_caseless_(pos->header,
+                                MHD_HTTP_HEADER_CONNECTION) ) ) ) )
+      size += strlen (pos->header) + strlen (pos->value) + 4; /* colon, space, linefeeds */
+  /* produce data */
+  data = MHD_pool_allocate (connection->pool, size + 1, MHD_NO);
+  if (NULL == data)
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+                "Not enough memory for write!\n");
+#endif
+      return MHD_NO;
+    }
+  if (MHD_CONNECTION_FOOTERS_RECEIVED == connection->state)
+    {
+      memcpy (data, code, off);
+    }
+  if (must_add_close)
+    {
+      /* we must add the 'Connection: close' header */
+      memcpy (&data[off],
+              "Connection: close\r\n",
+	      strlen ("Connection: close\r\n"));
+      off += strlen ("Connection: close\r\n");
+    }
+  if (must_add_keep_alive)
+    {
+      /* we must add the 'Connection: Keep-Alive' header */
+      memcpy (&data[off],
+              "Connection: Keep-Alive\r\n",
+	      strlen ("Connection: Keep-Alive\r\n"));
+      off += strlen ("Connection: Keep-Alive\r\n");
+    }
+  if (must_add_chunked_encoding)
+    {
+      /* we must add the 'Transfer-Encoding: chunked' header */
+      memcpy (&data[off],
+              "Transfer-Encoding: chunked\r\n",
+	      strlen ("Transfer-Encoding: chunked\r\n"));
+      off += strlen ("Transfer-Encoding: chunked\r\n");
+    }
+  if (must_add_content_length)
+    {
+      /* we must add the 'Content-Length' header */
+      memcpy (&data[off],
+              content_length_buf,
+	      content_length_len);
+      off += content_length_len;
+    }
+  for (pos = connection->response->first_header; NULL != pos; pos = pos->next)
+    if ( (pos->kind == kind) &&
+         (! ( (pos->value == response_has_keepalive) &&
+              (MHD_YES == must_add_close) &&
+              (MHD_str_equal_caseless_(pos->header,
+                                MHD_HTTP_HEADER_CONNECTION) ) ) ) )
+      off += sprintf (&data[off],
+		      "%s: %s\r\n",
+		      pos->header,
+		      pos->value);
+  if (MHD_CONNECTION_FOOTERS_RECEIVED == connection->state)
+    {
+      strcpy (&data[off], date);
+      off += strlen (date);
+    }
+  memcpy (&data[off], "\r\n", 2);
+  off += 2;
+
+  if (off != size)
+    mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL);
+  connection->write_buffer = data;
+  connection->write_buffer_append_offset = size;
+  connection->write_buffer_send_offset = 0;
+  connection->write_buffer_size = size + 1;
+  return MHD_YES;
+}
+
+
+/**
+ * We encountered an error processing the request.
+ * Handle it properly by stopping to read data
+ * and sending the indicated response code and message.
+ *
+ * @param connection the connection
+ * @param status_code the response code to send (400, 413 or 414)
+ * @param message the error message to send
+ */
+static void
+transmit_error_response (struct MHD_Connection *connection,
+                         unsigned int status_code,
+			 const char *message)
+{
+  struct MHD_Response *response;
+
+  if (NULL == connection->version)
+    {
+      /* we were unable to process the full header line, so we don't
+	 really know what version the client speaks; assume 1.0 */
+      connection->version = MHD_HTTP_VERSION_1_0;
+    }
+  connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+  connection->read_closed = MHD_YES;
+#if HAVE_MESSAGES
+  MHD_DLOG (connection->daemon,
+            "Error %u (`%s') processing request, closing connection.\n",
+            status_code, message);
+#endif
+  EXTRA_CHECK (NULL == connection->response);
+  response = MHD_create_response_from_buffer (strlen (message),
+					      (void *) message,
+					      MHD_RESPMEM_PERSISTENT);
+  MHD_queue_response (connection, status_code, response);
+  EXTRA_CHECK (NULL != connection->response);
+  MHD_destroy_response (response);
+  if (MHD_NO == build_header_response (connection))
+    {
+      /* oops - close! */
+      CONNECTION_CLOSE_ERROR (connection,
+			      "Closing connection (failed to create response header)\n");
+    }
+  else
+    {
+      connection->state = MHD_CONNECTION_HEADERS_SENDING;
+    }
+}
+
+
+/**
+ * Update the 'event_loop_info' field of this connection based on the state
+ * that the connection is now in.  May also close the connection or
+ * perform other updates to the connection if needed to prepare for
+ * the next round of the event loop.
+ *
+ * @param connection connetion to get poll set for
+ */
+static void
+MHD_connection_update_event_loop_info (struct MHD_Connection *connection)
+{
+  while (1)
+    {
+#if DEBUG_STATES
+      MHD_DLOG (connection->daemon,
+                "%s: state: %s\n",
+                __FUNCTION__,
+                MHD_state_to_string (connection->state));
+#endif
+      switch (connection->state)
+        {
+#if HTTPS_SUPPORT
+	case MHD_TLS_CONNECTION_INIT:
+	  if (0 == gnutls_record_get_direction (connection->tls_session))
+            connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
+	  else
+            connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
+	  break;
+#endif
+        case MHD_CONNECTION_INIT:
+        case MHD_CONNECTION_URL_RECEIVED:
+        case MHD_CONNECTION_HEADER_PART_RECEIVED:
+          /* while reading headers, we always grow the
+             read buffer if needed, no size-check required */
+          if ( (connection->read_buffer_offset == connection->read_buffer_size) &&
+	       (MHD_NO == try_grow_read_buffer (connection)) )
+            {
+              transmit_error_response (connection,
+                                       (connection->url != NULL)
+                                       ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE
+                                       : MHD_HTTP_REQUEST_URI_TOO_LONG,
+                                       REQUEST_TOO_BIG);
+              continue;
+            }
+	  if (MHD_NO == connection->read_closed)
+	    connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
+	  else
+	    connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
+          break;
+        case MHD_CONNECTION_HEADERS_RECEIVED:
+          EXTRA_CHECK (0);
+          break;
+        case MHD_CONNECTION_HEADERS_PROCESSED:
+          EXTRA_CHECK (0);
+          break;
+        case MHD_CONNECTION_CONTINUE_SENDING:
+          connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
+          break;
+        case MHD_CONNECTION_CONTINUE_SENT:
+          if (connection->read_buffer_offset == connection->read_buffer_size)
+            {
+              if ((MHD_YES != try_grow_read_buffer (connection)) &&
+                  (0 != (connection->daemon->options &
+                         (MHD_USE_SELECT_INTERNALLY |
+                          MHD_USE_THREAD_PER_CONNECTION))))
+                {
+                  /* failed to grow the read buffer, and the
+                     client which is supposed to handle the
+                     received data in a *blocking* fashion
+                     (in this mode) did not handle the data as
+                     it was supposed to!
+                     => we would either have to do busy-waiting
+                     (on the client, which would likely fail),
+                     or if we do nothing, we would just timeout
+                     on the connection (if a timeout is even
+                     set!).
+                     Solution: we kill the connection with an error */
+                  transmit_error_response (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           INTERNAL_ERROR);
+                  continue;
+                }
+            }
+          if ( (connection->read_buffer_offset < connection->read_buffer_size) &&
+	       (MHD_NO == connection->read_closed) )
+	    connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
+	  else
+	    connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
+          break;
+        case MHD_CONNECTION_BODY_RECEIVED:
+        case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+          /* while reading footers, we always grow the
+             read buffer if needed, no size-check required */
+          if (MHD_YES == connection->read_closed)
+            {
+	      CONNECTION_CLOSE_ERROR (connection,
+				      NULL);
+              continue;
+            }
+	  connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
+          /* transition to FOOTERS_RECEIVED
+             happens in read handler */
+          break;
+        case MHD_CONNECTION_FOOTERS_RECEIVED:
+	  connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
+          break;
+        case MHD_CONNECTION_HEADERS_SENDING:
+          /* headers in buffer, keep writing */
+	  connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
+          break;
+        case MHD_CONNECTION_HEADERS_SENT:
+          EXTRA_CHECK (0);
+          break;
+        case MHD_CONNECTION_NORMAL_BODY_READY:
+	  connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
+          break;
+        case MHD_CONNECTION_NORMAL_BODY_UNREADY:
+	  connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
+          break;
+        case MHD_CONNECTION_CHUNKED_BODY_READY:
+	  connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
+          break;
+        case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
+	  connection->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK;
+          break;
+        case MHD_CONNECTION_BODY_SENT:
+          EXTRA_CHECK (0);
+          break;
+        case MHD_CONNECTION_FOOTERS_SENDING:
+	  connection->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE;
+          break;
+        case MHD_CONNECTION_FOOTERS_SENT:
+          EXTRA_CHECK (0);
+          break;
+        case MHD_CONNECTION_CLOSED:
+	  connection->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
+          return;       /* do nothing, not even reading */
+        default:
+          EXTRA_CHECK (0);
+        }
+      break;
+    }
+}
+
+
+/**
+ * Parse a single line of the HTTP header.  Advance
+ * read_buffer (!) appropriately.  If the current line does not
+ * fit, consider growing the buffer.  If the line is
+ * far too long, close the connection.  If no line is
+ * found (incomplete, buffer too small, line too long),
+ * return NULL.  Otherwise return a pointer to the line.
+ *
+ * @param connection connection we're processing
+ * @return NULL if no full line is available
+ */
+static char *
+get_next_header_line (struct MHD_Connection *connection)
+{
+  char *rbuf;
+  size_t pos;
+
+  if (0 == connection->read_buffer_offset)
+    return NULL;
+  pos = 0;
+  rbuf = connection->read_buffer;
+  while ((pos < connection->read_buffer_offset - 1) &&
+         ('\r' != rbuf[pos]) && ('\n' != rbuf[pos]))
+    pos++;
+  if ( (pos == connection->read_buffer_offset - 1) &&
+       ('\n' != rbuf[pos]) )
+    {
+      /* not found, consider growing... */
+      if ( (connection->read_buffer_offset == connection->read_buffer_size) &&
+	   (MHD_NO ==
+	    try_grow_read_buffer (connection)) )
+	{
+	  transmit_error_response (connection,
+				   (NULL != connection->url)
+				   ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE
+				   : MHD_HTTP_REQUEST_URI_TOO_LONG,
+				   REQUEST_TOO_BIG);
+	}
+      return NULL;
+    }
+  /* found, check if we have proper LFCR */
+  if (('\r' == rbuf[pos]) && ('\n' == rbuf[pos + 1]))
+    rbuf[pos++] = '\0';         /* skip both r and n */
+  rbuf[pos++] = '\0';
+  connection->read_buffer += pos;
+  connection->read_buffer_size -= pos;
+  connection->read_buffer_offset -= pos;
+  return rbuf;
+}
+
+
+/**
+ * Add an entry to the HTTP headers of a connection.  If this fails,
+ * transmit an error response (request too big).
+ *
+ * @param connection the connection for which a
+ *  value should be set
+ * @param kind kind of the value
+ * @param key key for the value
+ * @param value the value itself
+ * @return #MHD_NO on failure (out of memory), #MHD_YES for success
+ */
+static int
+connection_add_header (struct MHD_Connection *connection,
+                       char *key, char *value, enum MHD_ValueKind kind)
+{
+  if (MHD_NO == MHD_set_connection_value (connection,
+					  kind,
+					  key, value))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+                "Not enough memory to allocate header record!\n");
+#endif
+      transmit_error_response (connection, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
+                               REQUEST_TOO_BIG);
+      return MHD_NO;
+    }
+  return MHD_YES;
+}
+
+
+/**
+ * Parse and unescape the arguments given by the client as part
+ * of the HTTP request URI.
+ *
+ * @param kind header kind to use for adding to the connection
+ * @param connection connection to add headers to
+ * @param args argument URI string (after "?" in URI)
+ * @return #MHD_NO on failure (out of memory), #MHD_YES for success
+ */
+static int
+parse_arguments (enum MHD_ValueKind kind,
+                 struct MHD_Connection *connection,
+		 char *args)
+{
+  char *equals;
+  char *amper;
+
+  while (NULL != args)
+    {
+      equals = strchr (args, '=');
+      amper = strchr (args, '&');
+      if (NULL == amper)
+	{
+	  /* last argument */
+	  if (NULL == equals)
+	    {
+	      /* got 'foo', add key 'foo' with NULL for value */
+              MHD_unescape_plus (args);
+	      connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
+						     connection,
+						     args);
+	      return connection_add_header (connection,
+					    args,
+					    NULL,
+					    kind);
+	    }
+	  /* got 'foo=bar' */
+	  equals[0] = '\0';
+	  equals++;
+          MHD_unescape_plus (args);
+	  connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
+						 connection,
+						 args);
+          MHD_unescape_plus (equals);
+	  connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
+						 connection,
+						 equals);
+	  return connection_add_header (connection, args, equals, kind);
+	}
+      /* amper is non-NULL here */
+      amper[0] = '\0';
+      amper++;
+      if ( (NULL == equals) ||
+	   (equals >= amper) )
+	{
+	  /* got 'foo&bar' or 'foo&bar=val', add key 'foo' with NULL for value */
+          MHD_unescape_plus (args);
+	  connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
+						 connection,
+						 args);
+	  if (MHD_NO ==
+	      connection_add_header (connection,
+				     args,
+				     NULL,
+				     kind))
+	    return MHD_NO;
+	  /* continue with 'bar' */
+	  args = amper;
+	  continue;
+
+	}
+      /* equals and amper are non-NULL here, and equals < amper,
+	 so we got regular 'foo=value&bar...'-kind of argument */
+      equals[0] = '\0';
+      equals++;
+      MHD_unescape_plus (args);
+      connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
+					     connection,
+					     args);
+      MHD_unescape_plus (equals);
+      connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
+					     connection,
+					     equals);
+      if (MHD_NO == connection_add_header (connection, args, equals, kind))
+        return MHD_NO;
+      args = amper;
+    }
+  return MHD_YES;
+}
+
+
+/**
+ * Parse the cookie header (see RFC 2109).
+ *
+ * @return #MHD_YES for success, #MHD_NO for failure (malformed, out of memory)
+ */
+static int
+parse_cookie_header (struct MHD_Connection *connection)
+{
+  const char *hdr;
+  char *cpy;
+  char *pos;
+  char *sce;
+  char *semicolon;
+  char *equals;
+  char *ekill;
+  char old;
+  int quotes;
+
+  hdr = MHD_lookup_connection_value (connection,
+				     MHD_HEADER_KIND,
+				     MHD_HTTP_HEADER_COOKIE);
+  if (NULL == hdr)
+    return MHD_YES;
+  cpy = MHD_pool_allocate (connection->pool, strlen (hdr) + 1, MHD_YES);
+  if (NULL == cpy)
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+                "Not enough memory to parse cookies!\n");
+#endif
+      transmit_error_response (connection, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
+                               REQUEST_TOO_BIG);
+      return MHD_NO;
+    }
+  memcpy (cpy, hdr, strlen (hdr) + 1);
+  pos = cpy;
+  while (NULL != pos)
+    {
+      while (' ' == *pos)
+        pos++;                  /* skip spaces */
+
+      sce = pos;
+      while (((*sce) != '\0') &&
+             ((*sce) != ',') && ((*sce) != ';') && ((*sce) != '='))
+        sce++;
+      /* remove tailing whitespace (if any) from key */
+      ekill = sce - 1;
+      while ((*ekill == ' ') && (ekill >= pos))
+        *(ekill--) = '\0';
+      old = *sce;
+      *sce = '\0';
+      if (old != '=')
+        {
+          /* value part omitted, use empty string... */
+          if (MHD_NO ==
+              connection_add_header (connection, pos, "", MHD_COOKIE_KIND))
+            return MHD_NO;
+          if (old == '\0')
+            break;
+          pos = sce + 1;
+          continue;
+        }
+      equals = sce + 1;
+      quotes = 0;
+      semicolon = equals;
+      while ( ('\0' != semicolon[0]) &&
+              ( (0 != quotes) ||
+                ( (';' != semicolon[0]) &&
+                  (',' != semicolon[0]) ) ) )
+        {
+          if ('"' == semicolon[0])
+            quotes = (quotes + 1) & 1;
+          semicolon++;
+        }
+      if ('\0' == semicolon[0])
+        semicolon = NULL;
+      if (NULL != semicolon)
+        {
+          semicolon[0] = '\0';
+          semicolon++;
+        }
+      /* remove quotes */
+      if ( ('"' == equals[0]) &&
+           ('"' == equals[strlen (equals) - 1]) )
+        {
+          equals[strlen (equals) - 1] = '\0';
+          equals++;
+        }
+      if (MHD_NO == connection_add_header (connection,
+                                           pos, equals, MHD_COOKIE_KIND))
+        return MHD_NO;
+      pos = semicolon;
+    }
+  return MHD_YES;
+}
+
+
+/**
+ * Parse the first line of the HTTP HEADER.
+ *
+ * @param connection the connection (updated)
+ * @param line the first line
+ * @return #MHD_YES if the line is ok, #MHD_NO if it is malformed
+ */
+static int
+parse_initial_message_line (struct MHD_Connection *connection,
+                            char *line)
+{
+  char *uri;
+  char *http_version;
+  char *args;
+
+  if (NULL == (uri = strchr (line, ' ')))
+    return MHD_NO;              /* serious error */
+  uri[0] = '\0';
+  connection->method = line;
+  uri++;
+  while (' ' == uri[0])
+    uri++;
+  http_version = strchr (uri, ' ');
+  if (NULL != http_version)
+    {
+      http_version[0] = '\0';
+      http_version++;
+    }
+  if (NULL != connection->daemon->uri_log_callback)
+    connection->client_context
+      = connection->daemon->uri_log_callback (connection->daemon->uri_log_callback_cls,
+					      uri,
+					      connection);
+  args = strchr (uri, '?');
+  if (NULL != args)
+    {
+      args[0] = '\0';
+      args++;
+      parse_arguments (MHD_GET_ARGUMENT_KIND, connection, args);
+    }
+  connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
+					 connection,
+					 uri);
+  connection->url = uri;
+  if (NULL == http_version)
+    connection->version = "";
+  else
+    connection->version = http_version;
+  return MHD_YES;
+}
+
+
+/**
+ * Call the handler of the application for this
+ * connection.  Handles chunking of the upload
+ * as well as normal uploads.
+ *
+ * @param connection connection we're processing
+ */
+static void
+call_connection_handler (struct MHD_Connection *connection)
+{
+  size_t processed;
+
+  if (NULL != connection->response)
+    return;                     /* already queued a response */
+  processed = 0;
+  connection->client_aware = MHD_YES;
+  if (MHD_NO ==
+      connection->daemon->default_handler (connection->daemon-> default_handler_cls,
+					   connection,
+                                           connection->url,
+					   connection->method,
+					   connection->version,
+					   NULL, &processed,
+					   &connection->client_context))
+    {
+      /* serious internal error, close connection */
+      CONNECTION_CLOSE_ERROR (connection,
+			      "Internal application error, closing connection.\n");
+      return;
+    }
+}
+
+
+
+/**
+ * Call the handler of the application for this
+ * connection.  Handles chunking of the upload
+ * as well as normal uploads.
+ *
+ * @param connection connection we're processing
+ */
+static void
+process_request_body (struct MHD_Connection *connection)
+{
+  size_t processed;
+  size_t available;
+  size_t used;
+  size_t i;
+  int instant_retry;
+  int malformed;
+  char *buffer_head;
+  char *end;
+
+  if (NULL != connection->response)
+    return;                     /* already queued a response */
+
+  buffer_head = connection->read_buffer;
+  available = connection->read_buffer_offset;
+  do
+    {
+      instant_retry = MHD_NO;
+      if ( (MHD_YES == connection->have_chunked_upload) &&
+           (MHD_SIZE_UNKNOWN == connection->remaining_upload_size) )
+        {
+          if ( (connection->current_chunk_offset == connection->current_chunk_size) &&
+               (0 != connection->current_chunk_offset) &&
+               (available >= 2) )
+            {
+              /* skip new line at the *end* of a chunk */
+              i = 0;
+              if ((buffer_head[i] == '\r') || (buffer_head[i] == '\n'))
+                i++;            /* skip 1st part of line feed */
+              if ((buffer_head[i] == '\r') || (buffer_head[i] == '\n'))
+                i++;            /* skip 2nd part of line feed */
+              if (i == 0)
+                {
+                  /* malformed encoding */
+                  CONNECTION_CLOSE_ERROR (connection,
+					  "Received malformed HTTP request (bad chunked encoding), closing connection.\n");
+                  return;
+                }
+              available -= i;
+              buffer_head += i;
+              connection->current_chunk_offset = 0;
+              connection->current_chunk_size = 0;
+            }
+          if (connection->current_chunk_offset <
+              connection->current_chunk_size)
+            {
+              /* we are in the middle of a chunk, give
+                 as much as possible to the client (without
+                 crossing chunk boundaries) */
+              processed =
+                connection->current_chunk_size -
+                connection->current_chunk_offset;
+              if (processed > available)
+                processed = available;
+              if (available > processed)
+                instant_retry = MHD_YES;
+            }
+          else
+            {
+              /* we need to read chunk boundaries */
+              i = 0;
+              while (i < available)
+                {
+                  if ((buffer_head[i] == '\r') || (buffer_head[i] == '\n'))
+                    break;
+                  i++;
+                  if (i >= 6)
+                    break;
+                }
+              /* take '\n' into account; if '\n'
+                 is the unavailable character, we
+                 will need to wait until we have it
+                 before going further */
+              if ((i + 1 >= available) &&
+                  !((i == 1) && (available == 2) && (buffer_head[0] == '0')))
+                break;          /* need more data... */
+              malformed = (i >= 6);
+              if (!malformed)
+                {
+                  buffer_head[i] = '\0';
+		  connection->current_chunk_size = strtoul (buffer_head, &end, 16);
+                  malformed = ('\0' != *end);
+                }
+              if (malformed)
+                {
+                  /* malformed encoding */
+                  CONNECTION_CLOSE_ERROR (connection,
+					  "Received malformed HTTP request (bad chunked encoding), closing connection.\n");
+                  return;
+                }
+              i++;
+              if ((i < available) &&
+                  ((buffer_head[i] == '\r') || (buffer_head[i] == '\n')))
+                i++;            /* skip 2nd part of line feed */
+
+              buffer_head += i;
+              available -= i;
+              connection->current_chunk_offset = 0;
+
+              if (available > 0)
+                instant_retry = MHD_YES;
+              if (0 == connection->current_chunk_size)
+                {
+                  connection->remaining_upload_size = 0;
+                  break;
+                }
+              continue;
+            }
+        }
+      else
+        {
+          /* no chunked encoding, give all to the client */
+          if ( (0 != connection->remaining_upload_size) &&
+	       (MHD_SIZE_UNKNOWN != connection->remaining_upload_size) &&
+	       (connection->remaining_upload_size < available) )
+	    {
+              processed = connection->remaining_upload_size;
+	    }
+          else
+	    {
+              /**
+               * 1. no chunked encoding, give all to the client
+               * 2. client may send large chunked data, but only a smaller part is available at one time.
+               */
+              processed = available;
+	    }
+        }
+      used = processed;
+      connection->client_aware = MHD_YES;
+      if (MHD_NO ==
+          connection->daemon->default_handler (connection->daemon->default_handler_cls,
+                                               connection,
+                                               connection->url,
+                                               connection->method,
+                                               connection->version,
+                                               buffer_head,
+                                               &processed,
+                                               &connection->client_context))
+        {
+          /* serious internal error, close connection */
+	  CONNECTION_CLOSE_ERROR (connection,
+				  "Internal application error, closing connection.\n");
+          return;
+        }
+      if (processed > used)
+        mhd_panic (mhd_panic_cls, __FILE__, __LINE__
+#if HAVE_MESSAGES
+		   , "API violation"
+#else
+		   , NULL
+#endif
+		   );
+      if (0 != processed)
+        instant_retry = MHD_NO; /* client did not process everything */
+      used -= processed;
+      if (connection->have_chunked_upload == MHD_YES)
+        connection->current_chunk_offset += used;
+      /* dh left "processed" bytes in buffer for next time... */
+      buffer_head += used;
+      available -= used;
+      if (connection->remaining_upload_size != MHD_SIZE_UNKNOWN)
+        connection->remaining_upload_size -= used;
+    }
+  while (MHD_YES == instant_retry);
+  if (available > 0)
+    memmove (connection->read_buffer, buffer_head, available);
+  connection->read_buffer_offset = available;
+}
+
+
+/**
+ * Try reading data from the socket into the
+ * read buffer of the connection.
+ *
+ * @param connection connection we're processing
+ * @return #MHD_YES if something changed,
+ *         #MHD_NO if we were interrupted or if
+ *                no space was available
+ */
+static int
+do_read (struct MHD_Connection *connection)
+{
+  int bytes_read;
+
+  if (connection->read_buffer_size == connection->read_buffer_offset)
+    return MHD_NO;
+  bytes_read = connection->recv_cls (connection,
+                                     &connection->read_buffer
+                                     [connection->read_buffer_offset],
+                                     connection->read_buffer_size -
+                                     connection->read_buffer_offset);
+  if (bytes_read < 0)
+    {
+      const int err = MHD_socket_errno_;
+      if ((EINTR == err) || (EAGAIN == err) || (EWOULDBLOCK == err))
+	  return MHD_NO;
+      if (ECONNRESET == err)
+        {
+           CONNECTION_CLOSE_ERROR (connection, NULL);
+	   return MHD_NO;
+	}
+      CONNECTION_CLOSE_ERROR (connection, NULL);
+      return MHD_YES;
+    }
+  if (0 == bytes_read)
+    {
+      /* other side closed connection; RFC 2616, section 8.1.4 suggests
+	 we should then shutdown ourselves as well. */
+      connection->read_closed = MHD_YES;
+      MHD_connection_close (connection,
+			    MHD_REQUEST_TERMINATED_CLIENT_ABORT);
+      return MHD_YES;
+    }
+  connection->read_buffer_offset += bytes_read;
+  return MHD_YES;
+}
+
+
+/**
+ * Try writing data to the socket from the
+ * write buffer of the connection.
+ *
+ * @param connection connection we're processing
+ * @return #MHD_YES if something changed,
+ *         #MHD_NO if we were interrupted
+ */
+static int
+do_write (struct MHD_Connection *connection)
+{
+  ssize_t ret;
+  size_t max;
+
+  max = connection->write_buffer_append_offset - connection->write_buffer_send_offset;
+  ret = connection->send_cls (connection,
+                              &connection->write_buffer
+                              [connection->write_buffer_send_offset],
+                              max);
+
+  if (ret < 0)
+    {
+      const int err = MHD_socket_errno_;
+      if ((EINTR == err) || (EAGAIN == err) || (EWOULDBLOCK == err))
+        return MHD_NO;
+      CONNECTION_CLOSE_ERROR (connection, NULL);
+      return MHD_YES;
+    }
+#if DEBUG_SEND_DATA
+  fprintf (stderr,
+           "Sent response: `%.*s'\n",
+           ret,
+           &connection->write_buffer[connection->write_buffer_send_offset]);
+#endif
+  /* only increment if this wasn't a "sendfile" transmission without
+     buffer involvement! */
+  if (0 != max)
+    connection->write_buffer_send_offset += ret;
+  return MHD_YES;
+}
+
+
+/**
+ * Check if we are done sending the write-buffer.
+ * If so, transition into "next_state".
+ *
+ * @param connection connection to check write status for
+ * @param next_state the next state to transition to
+ * @return #MHD_NO if we are not done, #MHD_YES if we are
+ */
+static int
+check_write_done (struct MHD_Connection *connection,
+                  enum MHD_CONNECTION_STATE next_state)
+{
+  if (connection->write_buffer_append_offset !=
+      connection->write_buffer_send_offset)
+    return MHD_NO;
+  connection->write_buffer_append_offset = 0;
+  connection->write_buffer_send_offset = 0;
+  connection->state = next_state;
+  MHD_pool_reallocate (connection->pool,
+		       connection->write_buffer,
+                       connection->write_buffer_size, 0);
+  connection->write_buffer = NULL;
+  connection->write_buffer_size = 0;
+  return MHD_YES;
+}
+
+
+/**
+ * We have received (possibly the beginning of) a line in the
+ * header (or footer).  Validate (check for ":") and prepare
+ * to process.
+ *
+ * @param connection connection we're processing
+ * @param line line from the header to process
+ * @return #MHD_YES on success, #MHD_NO on error (malformed @a line)
+ */
+static int
+process_header_line (struct MHD_Connection *connection, char *line)
+{
+  char *colon;
+
+  /* line should be normal header line, find colon */
+  colon = strchr (line, ':');
+  if (NULL == colon)
+    {
+      /* error in header line, die hard */
+      CONNECTION_CLOSE_ERROR (connection,
+			      "Received malformed line (no colon), closing connection.\n");
+      return MHD_NO;
+    }
+  /* zero-terminate header */
+  colon[0] = '\0';
+  colon++;                      /* advance to value */
+  while ((colon[0] != '\0') && ((colon[0] == ' ') || (colon[0] == '\t')))
+    colon++;
+  /* we do the actual adding of the connection
+     header at the beginning of the while
+     loop since we need to be able to inspect
+     the *next* header line (in case it starts
+     with a space...) */
+  connection->last = line;
+  connection->colon = colon;
+  return MHD_YES;
+}
+
+
+/**
+ * Process a header value that spans multiple lines.
+ * The previous line(s) are in connection->last.
+ *
+ * @param connection connection we're processing
+ * @param line the current input line
+ * @param kind if the line is complete, add a header
+ *        of the given kind
+ * @return #MHD_YES if the line was processed successfully
+ */
+static int
+process_broken_line (struct MHD_Connection *connection,
+                     char *line, enum MHD_ValueKind kind)
+{
+  char *last;
+  char *tmp;
+  size_t last_len;
+  size_t tmp_len;
+
+  last = connection->last;
+  if ((line[0] == ' ') || (line[0] == '\t'))
+    {
+      /* value was continued on the next line, see
+         http://www.jmarshall.com/easy/http/ */
+      last_len = strlen (last);
+      /* skip whitespace at start of 2nd line */
+      tmp = line;
+      while ((tmp[0] == ' ') || (tmp[0] == '\t'))
+        tmp++;
+      tmp_len = strlen (tmp);
+      /* FIXME: we might be able to do this better (faster!), as most
+	 likely 'last' and 'line' should already be adjacent in
+	 memory; however, doing this right gets tricky if we have a
+	 value continued over multiple lines (in which case we need to
+	 record how often we have done this so we can check for
+	 adjaency); also, in the case where these are not adjacent
+	 (not sure how it can happen!), we would want to allocate from
+	 the end of the pool, so as to not destroy the read-buffer's
+	 ability to grow nicely. */
+      last = MHD_pool_reallocate (connection->pool,
+                                  last,
+                                  last_len + 1,
+                                  last_len + tmp_len + 1);
+      if (NULL == last)
+        {
+          transmit_error_response (connection,
+                                   MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
+                                   REQUEST_TOO_BIG);
+          return MHD_NO;
+        }
+      memcpy (&last[last_len], tmp, tmp_len + 1);
+      connection->last = last;
+      return MHD_YES;           /* possibly more than 2 lines... */
+    }
+  EXTRA_CHECK ((NULL != last) && (NULL != connection->colon));
+  if ((MHD_NO == connection_add_header (connection,
+                                        last, connection->colon, kind)))
+    {
+      transmit_error_response (connection, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
+                               REQUEST_TOO_BIG);
+      return MHD_NO;
+    }
+  /* we still have the current line to deal with... */
+  if (0 != strlen (line))
+    {
+      if (MHD_NO == process_header_line (connection, line))
+        {
+          transmit_error_response (connection,
+                                   MHD_HTTP_BAD_REQUEST, REQUEST_MALFORMED);
+          return MHD_NO;
+        }
+    }
+  return MHD_YES;
+}
+
+
+/**
+ * Parse the various headers; figure out the size
+ * of the upload and make sure the headers follow
+ * the protocol.  Advance to the appropriate state.
+ *
+ * @param connection connection we're processing
+ */
+static void
+parse_connection_headers (struct MHD_Connection *connection)
+{
+  const char *clen;
+  MHD_UNSIGNED_LONG_LONG cval;
+  struct MHD_Response *response;
+  const char *enc;
+  char *end;
+
+  parse_cookie_header (connection);
+  if ( (0 != (MHD_USE_PEDANTIC_CHECKS & connection->daemon->options)) &&
+       (NULL != connection->version) &&
+       (MHD_str_equal_caseless_(MHD_HTTP_VERSION_1_1, connection->version)) &&
+       (NULL ==
+        MHD_lookup_connection_value (connection,
+                                     MHD_HEADER_KIND,
+                                     MHD_HTTP_HEADER_HOST)) )
+    {
+      /* die, http 1.1 request without host and we are pedantic */
+      connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+      connection->read_closed = MHD_YES;
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+                "Received `%s' request without `%s' header.\n",
+                MHD_HTTP_VERSION_1_1, MHD_HTTP_HEADER_HOST);
+#endif
+      EXTRA_CHECK (NULL == connection->response);
+      response =
+        MHD_create_response_from_buffer (strlen (REQUEST_LACKS_HOST),
+					 REQUEST_LACKS_HOST,
+					 MHD_RESPMEM_PERSISTENT);
+      MHD_queue_response (connection, MHD_HTTP_BAD_REQUEST, response);
+      MHD_destroy_response (response);
+      return;
+    }
+
+  connection->remaining_upload_size = 0;
+  enc = MHD_lookup_connection_value (connection,
+				     MHD_HEADER_KIND,
+				     MHD_HTTP_HEADER_TRANSFER_ENCODING);
+  if (NULL != enc)
+    {
+      connection->remaining_upload_size = MHD_SIZE_UNKNOWN;
+      if (MHD_str_equal_caseless_(enc, "chunked"))
+        connection->have_chunked_upload = MHD_YES;
+    }
+  else
+    {
+      clen = MHD_lookup_connection_value (connection,
+					  MHD_HEADER_KIND,
+					  MHD_HTTP_HEADER_CONTENT_LENGTH);
+      if (NULL != clen)
+        {
+          cval = strtoul (clen, &end, 10);
+          if ( ('\0' != *end) ||
+	     ( (LONG_MAX == cval) && (errno == ERANGE) ) )
+            {
+#if HAVE_MESSAGES
+              MHD_DLOG (connection->daemon,
+                        "Failed to parse `%s' header `%s', closing connection.\n",
+                        MHD_HTTP_HEADER_CONTENT_LENGTH,
+                        clen);
+#endif
+	      CONNECTION_CLOSE_ERROR (connection, NULL);
+              return;
+            }
+          connection->remaining_upload_size = cval;
+        }
+    }
+}
+
+
+/**
+ * Update the 'last_activity' field of the connection to the current time
+ * and move the connection to the head of the 'normal_timeout' list if
+ * the timeout for the connection uses the default value.
+ *
+ * @param connection the connection that saw some activity
+ */
+static void
+update_last_activity (struct MHD_Connection *connection)
+{
+  struct MHD_Daemon *daemon = connection->daemon;
+
+  connection->last_activity = MHD_monotonic_time();
+  if (connection->connection_timeout != daemon->connection_timeout)
+    return; /* custom timeout, no need to move it in "normal" DLL */
+
+  /* move connection to head of timeout list (by remove + add operation) */
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to acquire cleanup mutex\n");
+  XDLL_remove (daemon->normal_timeout_head,
+	       daemon->normal_timeout_tail,
+	       connection);
+  XDLL_insert (daemon->normal_timeout_head,
+	       daemon->normal_timeout_tail,
+	       connection);
+  if  ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+	(MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to release cleanup mutex\n");
+}
+
+
+/**
+ * This function handles a particular connection when it has been
+ * determined that there is data to be read off a socket.
+ *
+ * @param connection connection to handle
+ * @return always #MHD_YES (we should continue to process the
+ *         connection)
+ */
+int
+MHD_connection_handle_read (struct MHD_Connection *connection)
+{
+  update_last_activity (connection);
+  if (MHD_CONNECTION_CLOSED == connection->state)
+    return MHD_YES;
+  /* make sure "read" has a reasonable number of bytes
+     in buffer to use per system call (if possible) */
+  if (connection->read_buffer_offset + connection->daemon->pool_increment >
+      connection->read_buffer_size)
+    try_grow_read_buffer (connection);
+  if (MHD_NO == do_read (connection))
+    return MHD_YES;
+  while (1)
+    {
+#if DEBUG_STATES
+      MHD_DLOG (connection->daemon, "%s: state: %s\n",
+                __FUNCTION__,
+                MHD_state_to_string (connection->state));
+#endif
+      switch (connection->state)
+        {
+        case MHD_CONNECTION_INIT:
+        case MHD_CONNECTION_URL_RECEIVED:
+        case MHD_CONNECTION_HEADER_PART_RECEIVED:
+        case MHD_CONNECTION_HEADERS_RECEIVED:
+        case MHD_CONNECTION_HEADERS_PROCESSED:
+        case MHD_CONNECTION_CONTINUE_SENDING:
+        case MHD_CONNECTION_CONTINUE_SENT:
+        case MHD_CONNECTION_BODY_RECEIVED:
+        case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+          /* nothing to do but default action */
+          if (MHD_YES == connection->read_closed)
+            {
+	      MHD_connection_close (connection,
+				    MHD_REQUEST_TERMINATED_READ_ERROR);
+              continue;
+            }
+          break;
+        case MHD_CONNECTION_CLOSED:
+          return MHD_YES;
+        default:
+          /* shrink read buffer to how much is actually used */
+          MHD_pool_reallocate (connection->pool,
+                               connection->read_buffer,
+                               connection->read_buffer_size + 1,
+                               connection->read_buffer_offset);
+          break;
+        }
+      break;
+    }
+  return MHD_YES;
+}
+
+
+/**
+ * This function was created to handle writes to sockets when it has
+ * been determined that the socket can be written to.
+ *
+ * @param connection connection to handle
+ * @return always #MHD_YES (we should continue to process the
+ *         connection)
+ */
+int
+MHD_connection_handle_write (struct MHD_Connection *connection)
+{
+  struct MHD_Response *response;
+  ssize_t ret;
+
+  update_last_activity (connection);
+  while (1)
+    {
+#if DEBUG_STATES
+      MHD_DLOG (connection->daemon, "%s: state: %s\n",
+                __FUNCTION__,
+                MHD_state_to_string (connection->state));
+#endif
+      switch (connection->state)
+        {
+        case MHD_CONNECTION_INIT:
+        case MHD_CONNECTION_URL_RECEIVED:
+        case MHD_CONNECTION_HEADER_PART_RECEIVED:
+        case MHD_CONNECTION_HEADERS_RECEIVED:
+          EXTRA_CHECK (0);
+          break;
+        case MHD_CONNECTION_HEADERS_PROCESSED:
+          break;
+        case MHD_CONNECTION_CONTINUE_SENDING:
+          ret = connection->send_cls (connection,
+                                      &HTTP_100_CONTINUE
+                                      [connection->continue_message_write_offset],
+                                      strlen (HTTP_100_CONTINUE) -
+                                      connection->continue_message_write_offset);
+          if (ret < 0)
+            {
+              const int err = MHD_socket_errno_;
+              if ((err == EINTR) || (err == EAGAIN) || (EWOULDBLOCK == err))
+                break;
+#if HAVE_MESSAGES
+              MHD_DLOG (connection->daemon,
+                        "Failed to send data: %s\n",
+                        MHD_socket_last_strerr_ ());
+#endif
+	      CONNECTION_CLOSE_ERROR (connection, NULL);
+              return MHD_YES;
+            }
+#if DEBUG_SEND_DATA
+          fprintf (stderr,
+                   "Sent 100 continue response: `%.*s'\n",
+                   (int) ret,
+                   &HTTP_100_CONTINUE[connection->continue_message_write_offset]);
+#endif
+          connection->continue_message_write_offset += ret;
+          break;
+        case MHD_CONNECTION_CONTINUE_SENT:
+        case MHD_CONNECTION_BODY_RECEIVED:
+        case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+        case MHD_CONNECTION_FOOTERS_RECEIVED:
+          EXTRA_CHECK (0);
+          break;
+        case MHD_CONNECTION_HEADERS_SENDING:
+          do_write (connection);
+	  if (connection->state != MHD_CONNECTION_HEADERS_SENDING)
+ 	     break;
+          check_write_done (connection, MHD_CONNECTION_HEADERS_SENT);
+          break;
+        case MHD_CONNECTION_HEADERS_SENT:
+          EXTRA_CHECK (0);
+          break;
+        case MHD_CONNECTION_NORMAL_BODY_READY:
+          response = connection->response;
+          if (NULL != response->crc)
+            (void) MHD_mutex_lock_ (&response->mutex);
+          if (MHD_YES != try_ready_normal_body (connection))
+	    break;
+	  ret = connection->send_cls (connection,
+				      &response->data
+				      [connection->response_write_position
+				       - response->data_start],
+				      response->data_size -
+				      (connection->response_write_position
+				       - response->data_start));
+	  const int err = MHD_socket_errno_;
+#if DEBUG_SEND_DATA
+          if (ret > 0)
+            fprintf (stderr,
+                     "Sent DATA response: `%.*s'\n",
+                     (int) ret,
+                     &response->data[connection->response_write_position -
+                                     response->data_start]);
+#endif
+          if (NULL != response->crc)
+            (void) MHD_mutex_unlock_ (&response->mutex);
+          if (ret < 0)
+            {
+              if ((err == EINTR) || (err == EAGAIN) || (EWOULDBLOCK == err))
+                return MHD_YES;
+#if HAVE_MESSAGES
+              MHD_DLOG (connection->daemon,
+                        "Failed to send data: %s\n",
+                        MHD_socket_last_strerr_ ());
+#endif
+	      CONNECTION_CLOSE_ERROR (connection, NULL);
+              return MHD_YES;
+            }
+          connection->response_write_position += ret;
+          if (connection->response_write_position ==
+              connection->response->total_size)
+            connection->state = MHD_CONNECTION_FOOTERS_SENT; /* have no footers */
+          break;
+        case MHD_CONNECTION_NORMAL_BODY_UNREADY:
+          EXTRA_CHECK (0);
+          break;
+        case MHD_CONNECTION_CHUNKED_BODY_READY:
+          do_write (connection);
+	  if (MHD_CONNECTION_CHUNKED_BODY_READY != connection->state)
+	     break;
+          check_write_done (connection,
+                            (connection->response->total_size ==
+                             connection->response_write_position) ?
+                            MHD_CONNECTION_BODY_SENT :
+                            MHD_CONNECTION_CHUNKED_BODY_UNREADY);
+          break;
+        case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
+        case MHD_CONNECTION_BODY_SENT:
+          EXTRA_CHECK (0);
+          break;
+        case MHD_CONNECTION_FOOTERS_SENDING:
+          do_write (connection);
+	  if (connection->state != MHD_CONNECTION_FOOTERS_SENDING)
+	    break;
+          check_write_done (connection, MHD_CONNECTION_FOOTERS_SENT);
+          break;
+        case MHD_CONNECTION_FOOTERS_SENT:
+          EXTRA_CHECK (0);
+          break;
+        case MHD_CONNECTION_CLOSED:
+          return MHD_YES;
+        case MHD_TLS_CONNECTION_INIT:
+          EXTRA_CHECK (0);
+          break;
+        default:
+          EXTRA_CHECK (0);
+	  CONNECTION_CLOSE_ERROR (connection,
+                                  "Internal error\n");
+          return MHD_YES;
+        }
+      break;
+    }
+  return MHD_YES;
+}
+
+
+/**
+ * Clean up the state of the given connection and move it into the
+ * clean up queue for final disposal.
+ *
+ * @param connection handle for the connection to clean up
+ */
+static void
+cleanup_connection (struct MHD_Connection *connection)
+{
+  struct MHD_Daemon *daemon = connection->daemon;
+
+  if (NULL != connection->response)
+    {
+      MHD_destroy_response (connection->response);
+      connection->response = NULL;
+    }
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to acquire cleanup mutex\n");
+  if (connection->connection_timeout == daemon->connection_timeout)
+    XDLL_remove (daemon->normal_timeout_head,
+		 daemon->normal_timeout_tail,
+		 connection);
+  else
+    XDLL_remove (daemon->manual_timeout_head,
+		 daemon->manual_timeout_tail,
+		 connection);
+  if (MHD_YES == connection->suspended)
+    DLL_remove (daemon->suspended_connections_head,
+                daemon->suspended_connections_tail,
+                connection);
+  else
+    DLL_remove (daemon->connections_head,
+                daemon->connections_tail,
+                connection);
+  DLL_insert (daemon->cleanup_head,
+	      daemon->cleanup_tail,
+	      connection);
+  connection->suspended = MHD_NO;
+  connection->resuming = MHD_NO;
+  connection->in_idle = MHD_NO;
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_unlock_(&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to release cleanup mutex\n");
+}
+
+
+/**
+ * This function was created to handle per-connection processing that
+ * has to happen even if the socket cannot be read or written to.
+ *
+ * @param connection connection to handle
+ * @return #MHD_YES if we should continue to process the
+ *         connection (not dead yet), #MHD_NO if it died
+ */
+int
+MHD_connection_handle_idle (struct MHD_Connection *connection)
+{
+  struct MHD_Daemon *daemon = connection->daemon;
+  unsigned int timeout;
+  const char *end;
+  char *line;
+  int client_close;
+
+  connection->in_idle = MHD_YES;
+  while (1)
+    {
+#if DEBUG_STATES
+      MHD_DLOG (daemon,
+                "%s: state: %s\n",
+                __FUNCTION__,
+                MHD_state_to_string (connection->state));
+#endif
+      switch (connection->state)
+        {
+        case MHD_CONNECTION_INIT:
+          line = get_next_header_line (connection);
+          if (NULL == line)
+            {
+              if (MHD_CONNECTION_INIT != connection->state)
+                continue;
+              if (MHD_YES == connection->read_closed)
+                {
+		  CONNECTION_CLOSE_ERROR (connection,
+					  NULL);
+                  continue;
+                }
+              break;
+            }
+          if (MHD_NO == parse_initial_message_line (connection, line))
+            CONNECTION_CLOSE_ERROR (connection, NULL);
+          else
+            connection->state = MHD_CONNECTION_URL_RECEIVED;
+          continue;
+        case MHD_CONNECTION_URL_RECEIVED:
+          line = get_next_header_line (connection);
+          if (NULL == line)
+            {
+              if (MHD_CONNECTION_URL_RECEIVED != connection->state)
+                continue;
+              if (MHD_YES == connection->read_closed)
+                {
+		  CONNECTION_CLOSE_ERROR (connection,
+					  NULL);
+                  continue;
+                }
+              break;
+            }
+          if (strlen (line) == 0)
+            {
+              connection->state = MHD_CONNECTION_HEADERS_RECEIVED;
+              continue;
+            }
+          if (MHD_NO == process_header_line (connection, line))
+            {
+              transmit_error_response (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       REQUEST_MALFORMED);
+              break;
+            }
+          connection->state = MHD_CONNECTION_HEADER_PART_RECEIVED;
+          continue;
+        case MHD_CONNECTION_HEADER_PART_RECEIVED:
+          line = get_next_header_line (connection);
+          if (NULL == line)
+            {
+              if (connection->state != MHD_CONNECTION_HEADER_PART_RECEIVED)
+                continue;
+              if (MHD_YES == connection->read_closed)
+                {
+		  CONNECTION_CLOSE_ERROR (connection,
+					  NULL);
+                  continue;
+                }
+              break;
+            }
+          if (MHD_NO ==
+              process_broken_line (connection, line, MHD_HEADER_KIND))
+            continue;
+          if (0 == strlen (line))
+            {
+              connection->state = MHD_CONNECTION_HEADERS_RECEIVED;
+              continue;
+            }
+          continue;
+        case MHD_CONNECTION_HEADERS_RECEIVED:
+          parse_connection_headers (connection);
+          if (MHD_CONNECTION_CLOSED == connection->state)
+            continue;
+          connection->state = MHD_CONNECTION_HEADERS_PROCESSED;
+          continue;
+        case MHD_CONNECTION_HEADERS_PROCESSED:
+          call_connection_handler (connection); /* first call */
+          if (MHD_CONNECTION_CLOSED == connection->state)
+            continue;
+          if (need_100_continue (connection))
+            {
+              connection->state = MHD_CONNECTION_CONTINUE_SENDING;
+              break;
+            }
+          if ( (NULL != connection->response) &&
+	       ( (MHD_str_equal_caseless_ (connection->method,
+				   MHD_HTTP_METHOD_POST)) ||
+		 (MHD_str_equal_caseless_ (connection->method,
+				   MHD_HTTP_METHOD_PUT))) )
+            {
+              /* we refused (no upload allowed!) */
+              connection->remaining_upload_size = 0;
+              /* force close, in case client still tries to upload... */
+              connection->read_closed = MHD_YES;
+            }
+          connection->state = (0 == connection->remaining_upload_size)
+            ? MHD_CONNECTION_FOOTERS_RECEIVED : MHD_CONNECTION_CONTINUE_SENT;
+          continue;
+        case MHD_CONNECTION_CONTINUE_SENDING:
+          if (connection->continue_message_write_offset ==
+              strlen (HTTP_100_CONTINUE))
+            {
+              connection->state = MHD_CONNECTION_CONTINUE_SENT;
+              continue;
+            }
+          break;
+        case MHD_CONNECTION_CONTINUE_SENT:
+          if (0 != connection->read_buffer_offset)
+            {
+              process_request_body (connection);     /* loop call */
+              if (MHD_CONNECTION_CLOSED == connection->state)
+                continue;
+            }
+          if ((0 == connection->remaining_upload_size) ||
+              ((connection->remaining_upload_size == MHD_SIZE_UNKNOWN) &&
+               (0 == connection->read_buffer_offset) &&
+               (MHD_YES == connection->read_closed)))
+            {
+              if ((MHD_YES == connection->have_chunked_upload) &&
+                  (MHD_NO == connection->read_closed))
+                connection->state = MHD_CONNECTION_BODY_RECEIVED;
+              else
+                connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+              continue;
+            }
+          break;
+        case MHD_CONNECTION_BODY_RECEIVED:
+          line = get_next_header_line (connection);
+          if (NULL == line)
+            {
+              if (connection->state != MHD_CONNECTION_BODY_RECEIVED)
+                continue;
+              if (MHD_YES == connection->read_closed)
+                {
+		  CONNECTION_CLOSE_ERROR (connection,
+					  NULL);
+                  continue;
+                }
+              break;
+            }
+          if (0 == strlen (line))
+            {
+              connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+              continue;
+            }
+          if (MHD_NO == process_header_line (connection, line))
+            {
+              transmit_error_response (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       REQUEST_MALFORMED);
+              break;
+            }
+          connection->state = MHD_CONNECTION_FOOTER_PART_RECEIVED;
+          continue;
+        case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+          line = get_next_header_line (connection);
+          if (NULL == line)
+            {
+              if (connection->state != MHD_CONNECTION_FOOTER_PART_RECEIVED)
+                continue;
+              if (MHD_YES == connection->read_closed)
+                {
+		  CONNECTION_CLOSE_ERROR (connection,
+					  NULL);
+                  continue;
+                }
+              break;
+            }
+          if (MHD_NO ==
+              process_broken_line (connection, line, MHD_FOOTER_KIND))
+            continue;
+          if (0 == strlen (line))
+            {
+              connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+              continue;
+            }
+          continue;
+        case MHD_CONNECTION_FOOTERS_RECEIVED:
+          call_connection_handler (connection); /* "final" call */
+          if (connection->state == MHD_CONNECTION_CLOSED)
+            continue;
+          if (NULL == connection->response)
+            break;              /* try again next time */
+          if (MHD_NO == build_header_response (connection))
+            {
+              /* oops - close! */
+	      CONNECTION_CLOSE_ERROR (connection,
+				      "Closing connection (failed to create response header)\n");
+              continue;
+            }
+          connection->state = MHD_CONNECTION_HEADERS_SENDING;
+
+#if HAVE_DECL_TCP_CORK
+          /* starting header send, set TCP cork */
+          {
+            const int val = 1;
+            setsockopt (connection->socket_fd, IPPROTO_TCP, TCP_CORK, &val,
+                        sizeof (val));
+          }
+#endif
+          break;
+        case MHD_CONNECTION_HEADERS_SENDING:
+          /* no default action */
+          break;
+        case MHD_CONNECTION_HEADERS_SENT:
+          if (connection->have_chunked_upload)
+            connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
+          else
+            connection->state = MHD_CONNECTION_NORMAL_BODY_UNREADY;
+          continue;
+        case MHD_CONNECTION_NORMAL_BODY_READY:
+          /* nothing to do here */
+          break;
+        case MHD_CONNECTION_NORMAL_BODY_UNREADY:
+          if (NULL != connection->response->crc)
+            (void) MHD_mutex_lock_ (&connection->response->mutex);
+          if (0 == connection->response->total_size)
+            {
+              if (NULL != connection->response->crc)
+                (void) MHD_mutex_unlock_ (&connection->response->mutex);
+              connection->state = MHD_CONNECTION_BODY_SENT;
+              continue;
+            }
+          if (MHD_YES == try_ready_normal_body (connection))
+            {
+	      if (NULL != connection->response->crc)
+	        (void) MHD_mutex_unlock_ (&connection->response->mutex);
+              connection->state = MHD_CONNECTION_NORMAL_BODY_READY;
+              break;
+            }
+          /* not ready, no socket action */
+          break;
+        case MHD_CONNECTION_CHUNKED_BODY_READY:
+          /* nothing to do here */
+          break;
+        case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
+          if (NULL != connection->response->crc)
+            (void) MHD_mutex_lock_ (&connection->response->mutex);
+          if (0 == connection->response->total_size)
+            {
+              if (NULL != connection->response->crc)
+                (void) MHD_mutex_unlock_ (&connection->response->mutex);
+              connection->state = MHD_CONNECTION_BODY_SENT;
+              continue;
+            }
+          if (MHD_YES == try_ready_chunked_body (connection))
+            {
+              if (NULL != connection->response->crc)
+                (void) MHD_mutex_unlock_ (&connection->response->mutex);
+              connection->state = MHD_CONNECTION_CHUNKED_BODY_READY;
+              continue;
+            }
+          if (NULL != connection->response->crc)
+            (void) MHD_mutex_unlock_ (&connection->response->mutex);
+          break;
+        case MHD_CONNECTION_BODY_SENT:
+          if (MHD_NO == build_header_response (connection))
+            {
+              /* oops - close! */
+	      CONNECTION_CLOSE_ERROR (connection,
+				      "Closing connection (failed to create response header)\n");
+              continue;
+            }
+          if ( (MHD_NO == connection->have_chunked_upload) ||
+               (connection->write_buffer_send_offset ==
+                connection->write_buffer_append_offset) )
+            connection->state = MHD_CONNECTION_FOOTERS_SENT;
+          else
+            connection->state = MHD_CONNECTION_FOOTERS_SENDING;
+          continue;
+        case MHD_CONNECTION_FOOTERS_SENDING:
+          /* no default action */
+          break;
+        case MHD_CONNECTION_FOOTERS_SENT:
+#if HAVE_DECL_TCP_CORK
+          /* done sending, uncork */
+          {
+            const int val = 0;
+            setsockopt (connection->socket_fd, IPPROTO_TCP, TCP_CORK, &val,
+                        sizeof (val));
+          }
+#endif
+          end =
+            MHD_get_response_header (connection->response,
+				     MHD_HTTP_HEADER_CONNECTION);
+          client_close = ((NULL != end) && (MHD_str_equal_caseless_(end, "close")));
+          MHD_destroy_response (connection->response);
+          connection->response = NULL;
+          if ( (NULL != daemon->notify_completed) &&
+               (MHD_YES == connection->client_aware) )
+          {
+	    daemon->notify_completed (daemon->notify_completed_cls,
+				      connection,
+				      &connection->client_context,
+						  MHD_REQUEST_TERMINATED_COMPLETED_OK);
+            connection->client_aware = MHD_NO;
+          }
+          end =
+            MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
+                                         MHD_HTTP_HEADER_CONNECTION);
+          if ( (MHD_YES == connection->read_closed) ||
+               (client_close) ||
+               ((NULL != end) && (MHD_str_equal_caseless_ (end, "close"))) )
+            {
+              connection->read_closed = MHD_YES;
+              connection->read_buffer_offset = 0;
+            }
+          if (((MHD_YES == connection->read_closed) &&
+               (0 == connection->read_buffer_offset)) ||
+              (MHD_NO == keepalive_possible (connection)))
+            {
+              /* have to close for some reason */
+              MHD_connection_close (connection,
+                                    MHD_REQUEST_TERMINATED_COMPLETED_OK);
+              MHD_pool_destroy (connection->pool);
+              connection->pool = NULL;
+              connection->read_buffer = NULL;
+              connection->read_buffer_size = 0;
+              connection->read_buffer_offset = 0;
+            }
+          else
+            {
+              /* can try to keep-alive */
+              connection->version = NULL;
+              connection->state = MHD_CONNECTION_INIT;
+              connection->read_buffer
+                = MHD_pool_reset (connection->pool,
+                                  connection->read_buffer,
+                                  connection->read_buffer_size);
+            }
+	  connection->client_aware = MHD_NO;
+          connection->client_context = NULL;
+          connection->continue_message_write_offset = 0;
+          connection->responseCode = 0;
+          connection->headers_received = NULL;
+	  connection->headers_received_tail = NULL;
+          connection->response_write_position = 0;
+          connection->have_chunked_upload = MHD_NO;
+          connection->method = NULL;
+          connection->url = NULL;
+          connection->write_buffer = NULL;
+          connection->write_buffer_size = 0;
+          connection->write_buffer_send_offset = 0;
+          connection->write_buffer_append_offset = 0;
+          continue;
+        case MHD_CONNECTION_CLOSED:
+	  cleanup_connection (connection);
+	  return MHD_NO;
+        default:
+          EXTRA_CHECK (0);
+          break;
+        }
+      break;
+    }
+  timeout = connection->connection_timeout;
+  if ( (0 != timeout) &&
+       (timeout <= (MHD_monotonic_time() - connection->last_activity)) )
+    {
+      MHD_connection_close (connection, MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
+      connection->in_idle = MHD_NO;
+      return MHD_YES;
+    }
+  MHD_connection_update_event_loop_info (connection);
+#if EPOLL_SUPPORT
+  switch (connection->event_loop_info)
+    {
+    case MHD_EVENT_LOOP_INFO_READ:
+      if ( (0 != (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) &&
+           (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
+	   (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) )
+	{
+	  EDLL_insert (daemon->eready_head,
+		       daemon->eready_tail,
+		       connection);
+	  connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
+	}
+      break;
+    case MHD_EVENT_LOOP_INFO_WRITE:
+      if ( (0 != (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY)) &&
+           (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
+	   (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) )
+	{
+	  EDLL_insert (daemon->eready_head,
+		       daemon->eready_tail,
+		       connection);
+	  connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
+	}
+      break;
+    case MHD_EVENT_LOOP_INFO_BLOCK:
+      /* we should look at this connection again in the next iteration
+	 of the event loop, as we're waiting on the application */
+      if ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) &&
+           (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED))) )
+	{
+	  EDLL_insert (daemon->eready_head,
+		       daemon->eready_tail,
+		       connection);
+	  connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
+	}
+      break;
+    case MHD_EVENT_LOOP_INFO_CLEANUP:
+      /* This connection is finished, nothing left to do */
+      break;
+    }
+  return MHD_connection_epoll_update_ (connection);
+#else
+  return MHD_YES;
+#endif
+}
+
+
+#if EPOLL_SUPPORT
+/**
+ * Perform epoll() processing, possibly moving the connection back into
+ * the epoll() set if needed.
+ *
+ * @param connection connection to process
+ * @return #MHD_YES if we should continue to process the
+ *         connection (not dead yet), #MHD_NO if it died
+ */
+int
+MHD_connection_epoll_update_ (struct MHD_Connection *connection)
+{
+  struct MHD_Daemon *daemon = connection->daemon;
+
+  if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
+       (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) &&
+       (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
+       ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY)) ||
+	 ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) &&
+	   ( (MHD_EVENT_LOOP_INFO_READ == connection->event_loop_info) ||
+	     (connection->read_buffer_size > connection->read_buffer_offset) ) &&
+	   (MHD_NO == connection->read_closed) ) ) )
+    {
+      /* add to epoll set */
+      struct epoll_event event;
+
+      event.events = EPOLLIN | EPOLLOUT | EPOLLET;
+      event.data.ptr = connection;
+      if (0 != epoll_ctl (daemon->epoll_fd,
+			  EPOLL_CTL_ADD,
+			  connection->socket_fd,
+			  &event))
+	{
+#if HAVE_MESSAGES
+	  if (0 != (daemon->options & MHD_USE_DEBUG))
+	    MHD_DLOG (daemon,
+		      "Call to epoll_ctl failed: %s\n",
+		      MHD_socket_last_strerr_ ());
+#endif
+	  connection->state = MHD_CONNECTION_CLOSED;
+	  cleanup_connection (connection);
+	  return MHD_NO;
+	}
+      connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
+    }
+  connection->in_idle = MHD_NO;
+  return MHD_YES;
+}
+#endif
+
+
+/**
+ * Set callbacks for this connection to those for HTTP.
+ *
+ * @param connection connection to initialize
+ */
+void
+MHD_set_http_callbacks_ (struct MHD_Connection *connection)
+{
+  connection->read_handler = &MHD_connection_handle_read;
+  connection->write_handler = &MHD_connection_handle_write;
+  connection->idle_handler = &MHD_connection_handle_idle;
+}
+
+
+/**
+ * Obtain information about the given connection.
+ *
+ * @param connection what connection to get information about
+ * @param info_type what information is desired?
+ * @param ... depends on @a info_type
+ * @return NULL if this information is not available
+ *         (or if the @a info_type is unknown)
+ * @ingroup specialized
+ */
+const union MHD_ConnectionInfo *
+MHD_get_connection_info (struct MHD_Connection *connection,
+                         enum MHD_ConnectionInfoType info_type, ...)
+{
+  switch (info_type)
+    {
+#if HTTPS_SUPPORT
+    case MHD_CONNECTION_INFO_CIPHER_ALGO:
+      if (connection->tls_session == NULL)
+	return NULL;
+      connection->cipher = gnutls_cipher_get (connection->tls_session);
+      return (const union MHD_ConnectionInfo *) &connection->cipher;
+    case MHD_CONNECTION_INFO_PROTOCOL:
+      if (connection->tls_session == NULL)
+	return NULL;
+      connection->protocol = gnutls_protocol_get_version (connection->tls_session);
+      return (const union MHD_ConnectionInfo *) &connection->protocol;
+    case MHD_CONNECTION_INFO_GNUTLS_SESSION:
+      if (connection->tls_session == NULL)
+	return NULL;
+      return (const union MHD_ConnectionInfo *) &connection->tls_session;
+#endif
+    case MHD_CONNECTION_INFO_CLIENT_ADDRESS:
+      return (const union MHD_ConnectionInfo *) &connection->addr;
+    case MHD_CONNECTION_INFO_DAEMON:
+      return (const union MHD_ConnectionInfo *) &connection->daemon;
+    case MHD_CONNECTION_INFO_CONNECTION_FD:
+      return (const union MHD_ConnectionInfo *) &connection->socket_fd;
+    case MHD_CONNECTION_INFO_SOCKET_CONTEXT:
+      return (const union MHD_ConnectionInfo *) &connection->socket_context;
+    default:
+      return NULL;
+    };
+}
+
+
+/**
+ * Set a custom option for the given connection, overriding defaults.
+ *
+ * @param connection connection to modify
+ * @param option option to set
+ * @param ... arguments to the option, depending on the option type
+ * @return #MHD_YES on success, #MHD_NO if setting the option failed
+ * @ingroup specialized
+ */
+int
+MHD_set_connection_option (struct MHD_Connection *connection,
+			   enum MHD_CONNECTION_OPTION option,
+			   ...)
+{
+  va_list ap;
+  struct MHD_Daemon *daemon;
+
+  daemon = connection->daemon;
+  switch (option)
+    {
+    case MHD_CONNECTION_OPTION_TIMEOUT:
+      if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+	   (MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
+	MHD_PANIC ("Failed to acquire cleanup mutex\n");
+      if (MHD_YES != connection->suspended)
+        {
+          if (connection->connection_timeout == daemon->connection_timeout)
+            XDLL_remove (daemon->normal_timeout_head,
+                         daemon->normal_timeout_tail,
+                         connection);
+          else
+            XDLL_remove (daemon->manual_timeout_head,
+                         daemon->manual_timeout_tail,
+                         connection);
+        }
+      va_start (ap, option);
+      connection->connection_timeout = va_arg (ap, unsigned int);
+      va_end (ap);
+      if (MHD_YES != connection->suspended)
+        {
+          if (connection->connection_timeout == daemon->connection_timeout)
+            XDLL_insert (daemon->normal_timeout_head,
+                         daemon->normal_timeout_tail,
+                         connection);
+          else
+            XDLL_insert (daemon->manual_timeout_head,
+                         daemon->manual_timeout_tail,
+                         connection);
+        }
+      if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+	   (MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
+	MHD_PANIC ("Failed to release cleanup mutex\n");
+      return MHD_YES;
+    default:
+      return MHD_NO;
+    }
+}
+
+
+/**
+ * Queue a response to be transmitted to the client (as soon as
+ * possible but after #MHD_AccessHandlerCallback returns).
+ *
+ * @param connection the connection identifying the client
+ * @param status_code HTTP status code (i.e. #MHD_HTTP_OK)
+ * @param response response to transmit
+ * @return #MHD_NO on error (i.e. reply already sent),
+ *         #MHD_YES on success or if message has been queued
+ * @ingroup response
+ */
+int
+MHD_queue_response (struct MHD_Connection *connection,
+                    unsigned int status_code,
+                    struct MHD_Response *response)
+{
+  if ( (NULL == connection) ||
+       (NULL == response) ||
+       (NULL != connection->response) ||
+       ( (MHD_CONNECTION_HEADERS_PROCESSED != connection->state) &&
+	 (MHD_CONNECTION_FOOTERS_RECEIVED != connection->state) ) )
+    return MHD_NO;
+  MHD_increment_response_rc (response);
+  connection->response = response;
+  connection->responseCode = status_code;
+  if ( (NULL != connection->method) &&
+       (MHD_str_equal_caseless_ (connection->method, MHD_HTTP_METHOD_HEAD)) )
+    {
+      /* if this is a "HEAD" request, pretend that we
+         have already sent the full message body */
+      connection->response_write_position = response->total_size;
+    }
+  if ( (MHD_CONNECTION_HEADERS_PROCESSED == connection->state) &&
+       (NULL != connection->method) &&
+       ( (MHD_str_equal_caseless_ (connection->method,
+			   MHD_HTTP_METHOD_POST)) ||
+	 (MHD_str_equal_caseless_ (connection->method,
+			   MHD_HTTP_METHOD_PUT))) )
+    {
+      /* response was queued "early", refuse to read body / footers or
+         further requests! */
+      connection->read_closed = MHD_YES;
+      connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+    }
+  if (MHD_NO == connection->in_idle)
+    (void) MHD_connection_handle_idle (connection);
+  return MHD_YES;
+}
+
+
+/* end of connection.c */
diff --git a/src/microhttpd/connection.h b/src/microhttpd/connection.h
new file mode 100644
index 0000000..07feec2
--- /dev/null
+++ b/src/microhttpd/connection.h
@@ -0,0 +1,110 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Daniel Pittman and Christian Grothoff
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file connection.h
+ * @brief  Methods for managing connections
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ */
+
+#ifndef CONNECTION_H
+#define CONNECTION_H
+
+#include "internal.h"
+
+
+/**
+ * Set callbacks for this connection to those for HTTP.
+ *
+ * @param connection connection to initialize
+ */
+void
+MHD_set_http_callbacks_ (struct MHD_Connection *connection);
+
+
+/**
+ * This function handles a particular connection when it has been
+ * determined that there is data to be read off a socket. All
+ * implementations (multithreaded, external select, internal select)
+ * call this function to handle reads.
+ *
+ * @param connection connection to handle
+ * @return always MHD_YES (we should continue to process the
+ *         connection)
+ */
+int
+MHD_connection_handle_read (struct MHD_Connection *connection);
+
+
+/**
+ * This function was created to handle writes to sockets when it has
+ * been determined that the socket can be written to. All
+ * implementations (multithreaded, external select, internal select)
+ * call this function
+ *
+ * @param connection connection to handle
+ * @return always MHD_YES (we should continue to process the
+ *         connection)
+ */
+int
+MHD_connection_handle_write (struct MHD_Connection *connection);
+
+
+/**
+ * This function was created to handle per-connection processing that
+ * has to happen even if the socket cannot be read or written to.  All
+ * implementations (multithreaded, external select, internal select)
+ * call this function.
+ *
+ * @param connection connection to handle
+ * @return MHD_YES if we should continue to process the
+ *         connection (not dead yet), MHD_NO if it died
+ */
+int
+MHD_connection_handle_idle (struct MHD_Connection *connection);
+
+
+/**
+ * Close the given connection and give the
+ * specified termination code to the user.
+ *
+ * @param connection connection to close
+ * @param termination_code termination reason to give
+ */
+void
+MHD_connection_close (struct MHD_Connection *connection,
+		      enum MHD_RequestTerminationCode termination_code);
+
+
+#if EPOLL_SUPPORT
+/**
+ * Perform epoll processing, possibly moving the connection back into
+ * the epoll set if needed.
+ *
+ * @param connection connection to process
+ * @return MHD_YES if we should continue to process the
+ *         connection (not dead yet), MHD_NO if it died
+ */
+int
+MHD_connection_epoll_update_ (struct MHD_Connection *connection);
+#endif
+
+
+#endif
diff --git a/src/microhttpd/connection_https.c b/src/microhttpd/connection_https.c
new file mode 100644
index 0000000..834d52e
--- /dev/null
+++ b/src/microhttpd/connection_https.c
@@ -0,0 +1,183 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2008, 2010 Daniel Pittman and Christian Grothoff
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file connection_https.c
+ * @brief  Methods for managing SSL/TLS connections. This file is only
+ *         compiled if ENABLE_HTTPS is set.
+ * @author Sagie Amir
+ * @author Christian Grothoff
+ */
+
+#include "internal.h"
+#include "connection.h"
+#include "memorypool.h"
+#include "response.h"
+#include "reason_phrase.h"
+#include <gnutls/gnutls.h>
+
+
+/**
+ * Give gnuTLS chance to work on the TLS handshake.
+ *
+ * @param connection connection to handshake on
+ * @return #MHD_YES on error or if the handshake is progressing
+ *         #MHD_NO if the handshake has completed successfully
+ *         and we should start to read/write data
+ */
+static int
+run_tls_handshake (struct MHD_Connection *connection)
+{
+  int ret;
+
+  connection->last_activity = MHD_monotonic_time();
+  if (connection->state == MHD_TLS_CONNECTION_INIT)
+    {
+      ret = gnutls_handshake (connection->tls_session);
+      if (ret == GNUTLS_E_SUCCESS)
+	{
+	  /* set connection state to enable HTTP processing */
+	  connection->state = MHD_CONNECTION_INIT;
+	  return MHD_YES;
+	}
+      if ( (ret == GNUTLS_E_AGAIN) ||
+	   (ret == GNUTLS_E_INTERRUPTED) )
+	{
+	  /* handshake not done */
+	  return MHD_YES;
+	}
+      /* handshake failed */
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+		"Error: received handshake message out of context\n");
+#endif
+      MHD_connection_close (connection,
+			    MHD_REQUEST_TERMINATED_WITH_ERROR);
+      return MHD_YES;
+    }
+  return MHD_NO;
+}
+
+
+/**
+ * This function handles a particular SSL/TLS connection when
+ * it has been determined that there is data to be read off a
+ * socket. Message processing is done by message type which is
+ * determined by peeking into the first message type byte of the
+ * stream.
+ *
+ * Error message handling: all fatal level messages cause the
+ * connection to be terminated.
+ *
+ * Application data is forwarded to the underlying daemon for
+ * processing.
+ *
+ * @param connection the source connection
+ * @return always #MHD_YES (we should continue to process the connection)
+ */
+static int
+MHD_tls_connection_handle_read (struct MHD_Connection *connection)
+{
+  if (MHD_YES == run_tls_handshake (connection))
+    return MHD_YES;
+  return MHD_connection_handle_read (connection);
+}
+
+
+/**
+ * This function was created to handle writes to sockets when it has
+ * been determined that the socket can be written to. This function
+ * will forward all write requests to the underlying daemon unless
+ * the connection has been marked for closing.
+ *
+ * @return always #MHD_YES (we should continue to process the connection)
+ */
+static int
+MHD_tls_connection_handle_write (struct MHD_Connection *connection)
+{
+  if (MHD_YES == run_tls_handshake (connection))
+    return MHD_YES;
+  return MHD_connection_handle_write (connection);
+}
+
+
+/**
+ * This function was created to handle per-connection processing that
+ * has to happen even if the socket cannot be read or written to.  All
+ * implementations (multithreaded, external select, internal select)
+ * call this function.
+ *
+ * @param connection being handled
+ * @return #MHD_YES if we should continue to process the
+ *         connection (not dead yet), #MHD_NO if it died
+ */
+static int
+MHD_tls_connection_handle_idle (struct MHD_Connection *connection)
+{
+  unsigned int timeout;
+
+#if DEBUG_STATES
+  MHD_DLOG (connection->daemon,
+            "%s: state: %s\n",
+            __FUNCTION__,
+            MHD_state_to_string (connection->state));
+#endif
+  timeout = connection->connection_timeout;
+  if ( (timeout != 0) && (timeout <= (MHD_monotonic_time() - connection->last_activity)))
+    MHD_connection_close (connection,
+			  MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
+  switch (connection->state)
+    {
+      /* on newly created connections we might reach here before any reply has been received */
+    case MHD_TLS_CONNECTION_INIT:
+      break;
+      /* close connection if necessary */
+    case MHD_CONNECTION_CLOSED:
+      gnutls_bye (connection->tls_session, GNUTLS_SHUT_RDWR);
+      return MHD_connection_handle_idle (connection);
+    default:
+      if ( (0 != gnutls_record_check_pending (connection->tls_session)) &&
+	   (MHD_YES != MHD_tls_connection_handle_read (connection)) )
+	return MHD_YES;
+      return MHD_connection_handle_idle (connection);
+    }
+#if EPOLL_SUPPORT
+  return MHD_connection_epoll_update_ (connection);
+#else
+  return MHD_YES;
+#endif
+}
+
+
+/**
+ * Set connection callback function to be used through out
+ * the processing of this secure connection.
+ *
+ * @param connection which callbacks should be modified
+ */
+void
+MHD_set_https_callbacks (struct MHD_Connection *connection)
+{
+  connection->read_handler = &MHD_tls_connection_handle_read;
+  connection->write_handler = &MHD_tls_connection_handle_write;
+  connection->idle_handler = &MHD_tls_connection_handle_idle;
+}
+
+/* end of connection_https.c */
diff --git a/src/microhttpd/connection_https.h b/src/microhttpd/connection_https.h
new file mode 100644
index 0000000..02ffb52
--- /dev/null
+++ b/src/microhttpd/connection_https.h
@@ -0,0 +1,42 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2008 Daniel Pittman and Christian Grothoff
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file connection_https.h
+ * @brief  Methods for managing connections
+ * @author Christian Grothoff
+ */
+
+#ifndef CONNECTION_HTTPS_H
+#define CONNECTION_HTTPS_H
+
+#include "internal.h"
+
+#if HTTPS_SUPPORT
+/**
+ * Set connection callback function to be used through out
+ * the processing of this secure connection.
+ *
+ * @param connection which callbacks should be modified
+ */
+void 
+MHD_set_https_callbacks (struct MHD_Connection *connection);
+#endif
+
+#endif
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
new file mode 100644
index 0000000..1aa7d8d
--- /dev/null
+++ b/src/microhttpd/daemon.c
@@ -0,0 +1,4844 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2007-2014 Daniel Pittman and Christian Grothoff
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file microhttpd/daemon.c
+ * @brief  A minimal-HTTP server library
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+/* override small default value */
+#define FD_SETSIZE 1024
+#define MHD_DEFAULT_FD_SETSIZE 64
+#else
+#define MHD_DEFAULT_FD_SETSIZE FD_SETSIZE
+#endif
+#include "platform.h"
+#include "internal.h"
+#include "response.h"
+#include "connection.h"
+#include "memorypool.h"
+#include <limits.h>
+#include "autoinit_funcs.h"
+
+#if HAVE_SEARCH_H
+#include <search.h>
+#else
+#include "tsearch.h"
+#endif
+
+#if HTTPS_SUPPORT
+#include "connection_https.h"
+#include <gcrypt.h>
+#endif
+
+#if defined(HAVE_POLL_H) && defined(HAVE_POLL)
+#include <poll.h>
+#endif
+
+#ifdef LINUX
+#include <sys/sendfile.h>
+#endif
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#include <process.h>
+#endif
+
+#ifndef HAVE_ACCEPT4
+#define HAVE_ACCEPT4 0
+#endif
+
+/**
+ * Default connection limit.
+ */
+#ifndef WINDOWS
+#define MHD_MAX_CONNECTIONS_DEFAULT FD_SETSIZE - 4
+#else
+#define MHD_MAX_CONNECTIONS_DEFAULT FD_SETSIZE
+#endif
+
+/**
+ * Default memory allowed per connection.
+ */
+#define MHD_POOL_SIZE_DEFAULT (32 * 1024)
+
+#ifdef TCP_FASTOPEN
+/**
+ * Default TCP fastopen queue size.
+ */
+#define MHD_TCP_FASTOPEN_QUEUE_SIZE_DEFAULT 10
+#endif
+
+/**
+ * Print extra messages with reasons for closing
+ * sockets? (only adds non-error messages).
+ */
+#define DEBUG_CLOSE MHD_NO
+
+/**
+ * Print extra messages when establishing
+ * connections? (only adds non-error messages).
+ */
+#define DEBUG_CONNECT MHD_NO
+
+#ifndef LINUX
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+#endif
+
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0
+#endif
+
+#ifndef EPOLL_CLOEXEC
+#define EPOLL_CLOEXEC 0
+#endif
+
+
+/**
+ * Default implementation of the panic function,
+ * prints an error message and aborts.
+ *
+ * @param cls unused
+ * @param file name of the file with the problem
+ * @param line line number with the problem
+ * @param reason error message with details
+ */
+static void
+mhd_panic_std (void *cls,
+	       const char *file,
+	       unsigned int line,
+	       const char *reason)
+{
+#if HAVE_MESSAGES
+  fprintf (stderr, "Fatal error in GNU libmicrohttpd %s:%u: %s\n",
+	   file, line, reason);
+#endif
+  abort ();
+}
+
+
+/**
+ * Handler for fatal errors.
+ */
+MHD_PanicCallback mhd_panic;
+
+/**
+ * Closure argument for "mhd_panic".
+ */
+void *mhd_panic_cls;
+
+#ifdef _WIN32
+/**
+ * Track initialization of winsock
+ */
+static int mhd_winsock_inited_ = 0;
+#endif
+
+/**
+ * Trace up to and return master daemon. If the supplied daemon
+ * is a master, then return the daemon itself.
+ *
+ * @param daemon handle to a daemon
+ * @return master daemon handle
+ */
+static struct MHD_Daemon*
+MHD_get_master (struct MHD_Daemon *daemon)
+{
+  while (NULL != daemon->master)
+    daemon = daemon->master;
+  return daemon;
+}
+
+
+/**
+ * Maintain connection count for single address.
+ */
+struct MHD_IPCount
+{
+  /**
+   * Address family. AF_INET or AF_INET6 for now.
+   */
+  int family;
+
+  /**
+   * Actual address.
+   */
+  union
+  {
+    /**
+     * IPv4 address.
+     */
+    struct in_addr ipv4;
+#if HAVE_INET6
+    /**
+     * IPv6 address.
+     */
+    struct in6_addr ipv6;
+#endif
+  } addr;
+
+  /**
+   * Counter.
+   */
+  unsigned int count;
+};
+
+
+/**
+ * Lock shared structure for IP connection counts and connection DLLs.
+ *
+ * @param daemon handle to daemon where lock is
+ */
+static void
+MHD_ip_count_lock (struct MHD_Daemon *daemon)
+{
+  if (MHD_YES != MHD_mutex_lock_(&daemon->per_ip_connection_mutex))
+    {
+      MHD_PANIC ("Failed to acquire IP connection limit mutex\n");
+    }
+}
+
+
+/**
+ * Unlock shared structure for IP connection counts and connection DLLs.
+ *
+ * @param daemon handle to daemon where lock is
+ */
+static void
+MHD_ip_count_unlock (struct MHD_Daemon *daemon)
+{
+  if (MHD_YES != MHD_mutex_unlock_(&daemon->per_ip_connection_mutex))
+    {
+      MHD_PANIC ("Failed to release IP connection limit mutex\n");
+    }
+}
+
+
+/**
+ * Tree comparison function for IP addresses (supplied to tsearch() family).
+ * We compare everything in the struct up through the beginning of the
+ * 'count' field.
+ *
+ * @param a1 first address to compare
+ * @param a2 second address to compare
+ * @return -1, 0 or 1 depending on result of compare
+ */
+static int
+MHD_ip_addr_compare (const void *a1, const void *a2)
+{
+  return memcmp (a1, a2, offsetof (struct MHD_IPCount, count));
+}
+
+
+/**
+ * Parse address and initialize 'key' using the address.
+ *
+ * @param addr address to parse
+ * @param addrlen number of bytes in addr
+ * @param key where to store the parsed address
+ * @return #MHD_YES on success and #MHD_NO otherwise (e.g., invalid address type)
+ */
+static int
+MHD_ip_addr_to_key (const struct sockaddr *addr,
+		    socklen_t addrlen,
+		    struct MHD_IPCount *key)
+{
+  memset(key, 0, sizeof(*key));
+
+  /* IPv4 addresses */
+  if (sizeof (struct sockaddr_in) == addrlen)
+    {
+      const struct sockaddr_in *addr4 = (const struct sockaddr_in*) addr;
+      key->family = AF_INET;
+      memcpy (&key->addr.ipv4, &addr4->sin_addr, sizeof(addr4->sin_addr));
+      return MHD_YES;
+    }
+
+#if HAVE_INET6
+  /* IPv6 addresses */
+  if (sizeof (struct sockaddr_in6) == addrlen)
+    {
+      const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*) addr;
+      key->family = AF_INET6;
+      memcpy (&key->addr.ipv6, &addr6->sin6_addr, sizeof(addr6->sin6_addr));
+      return MHD_YES;
+    }
+#endif
+
+  /* Some other address */
+  return MHD_NO;
+}
+
+
+/**
+ * Check if IP address is over its limit.
+ *
+ * @param daemon handle to daemon where connection counts are tracked
+ * @param addr address to add (or increment counter)
+ * @param addrlen number of bytes in addr
+ * @return Return #MHD_YES if IP below limit, #MHD_NO if IP has surpassed limit.
+ *   Also returns #MHD_NO if fails to allocate memory.
+ */
+static int
+MHD_ip_limit_add (struct MHD_Daemon *daemon,
+		  const struct sockaddr *addr,
+		  socklen_t addrlen)
+{
+  struct MHD_IPCount *key;
+  void **nodep;
+  void *node;
+  int result;
+
+  daemon = MHD_get_master (daemon);
+  /* Ignore if no connection limit assigned */
+  if (0 == daemon->per_ip_connection_limit)
+    return MHD_YES;
+
+  if (NULL == (key = malloc (sizeof(*key))))
+    return MHD_NO;
+
+  /* Initialize key */
+  if (MHD_NO == MHD_ip_addr_to_key (addr, addrlen, key))
+    {
+      /* Allow unhandled address types through */
+      free (key);
+      return MHD_YES;
+    }
+  MHD_ip_count_lock (daemon);
+
+  /* Search for the IP address */
+  if (NULL == (nodep = tsearch (key,
+				&daemon->per_ip_connection_count,
+				&MHD_ip_addr_compare)))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"Failed to add IP connection count node\n");
+#endif
+      MHD_ip_count_unlock (daemon);
+      free (key);
+      return MHD_NO;
+    }
+  node = *nodep;
+  /* If we got an existing node back, free the one we created */
+  if (node != key)
+    free(key);
+  key = (struct MHD_IPCount *) node;
+  /* Test if there is room for another connection; if so,
+   * increment count */
+  result = (key->count < daemon->per_ip_connection_limit);
+  if (MHD_YES == result)
+    ++key->count;
+
+  MHD_ip_count_unlock (daemon);
+  return result;
+}
+
+
+/**
+ * Decrement connection count for IP address, removing from table
+ * count reaches 0.
+ *
+ * @param daemon handle to daemon where connection counts are tracked
+ * @param addr address to remove (or decrement counter)
+ * @param addrlen number of bytes in @a addr
+ */
+static void
+MHD_ip_limit_del (struct MHD_Daemon *daemon,
+		  const struct sockaddr *addr,
+		  socklen_t addrlen)
+{
+  struct MHD_IPCount search_key;
+  struct MHD_IPCount *found_key;
+  void **nodep;
+
+  daemon = MHD_get_master (daemon);
+  /* Ignore if no connection limit assigned */
+  if (0 == daemon->per_ip_connection_limit)
+    return;
+  /* Initialize search key */
+  if (MHD_NO == MHD_ip_addr_to_key (addr, addrlen, &search_key))
+    return;
+
+  MHD_ip_count_lock (daemon);
+
+  /* Search for the IP address */
+  if (NULL == (nodep = tfind (&search_key,
+			      &daemon->per_ip_connection_count,
+			      &MHD_ip_addr_compare)))
+    {
+      /* Something's wrong if we couldn't find an IP address
+       * that was previously added */
+      MHD_PANIC ("Failed to find previously-added IP address\n");
+    }
+  found_key = (struct MHD_IPCount *) *nodep;
+  /* Validate existing count for IP address */
+  if (0 == found_key->count)
+    {
+      MHD_PANIC ("Previously-added IP address had 0 count\n");
+    }
+  /* Remove the node entirely if count reduces to 0 */
+  if (0 == --found_key->count)
+    {
+      tdelete (found_key,
+	       &daemon->per_ip_connection_count,
+	       &MHD_ip_addr_compare);
+      free (found_key);
+    }
+
+  MHD_ip_count_unlock (daemon);
+}
+
+
+#if HTTPS_SUPPORT
+/**
+ * Callback for receiving data from the socket.
+ *
+ * @param connection the MHD_Connection structure
+ * @param other where to write received data to
+ * @param i maximum size of other (in bytes)
+ * @return number of bytes actually received
+ */
+static ssize_t
+recv_tls_adapter (struct MHD_Connection *connection, void *other, size_t i)
+{
+  int res;
+
+  if (MHD_YES == connection->tls_read_ready)
+    {
+      connection->daemon->num_tls_read_ready--;
+      connection->tls_read_ready = MHD_NO;
+    }
+  res = gnutls_record_recv (connection->tls_session, other, i);
+  if ( (GNUTLS_E_AGAIN == res) ||
+       (GNUTLS_E_INTERRUPTED == res) )
+    {
+      MHD_set_socket_errno_ (EINTR);
+#if EPOLL_SUPPORT
+      connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY;
+#endif
+      return -1;
+    }
+  if (res < 0)
+    {
+      /* Likely 'GNUTLS_E_INVALID_SESSION' (client communication
+	 disrupted); set errno to something caller will interpret
+	 correctly as a hard error */
+      MHD_set_socket_errno_ (ECONNRESET);
+      return res;
+    }
+  if (res == i)
+    {
+      connection->tls_read_ready = MHD_YES;
+      connection->daemon->num_tls_read_ready++;
+    }
+  return res;
+}
+
+
+/**
+ * Callback for writing data to the socket.
+ *
+ * @param connection the MHD connection structure
+ * @param other data to write
+ * @param i number of bytes to write
+ * @return actual number of bytes written
+ */
+static ssize_t
+send_tls_adapter (struct MHD_Connection *connection,
+                  const void *other, size_t i)
+{
+  int res;
+
+  res = gnutls_record_send (connection->tls_session, other, i);
+  if ( (GNUTLS_E_AGAIN == res) ||
+       (GNUTLS_E_INTERRUPTED == res) )
+    {
+      MHD_set_socket_errno_ (EINTR);
+#if EPOLL_SUPPORT
+      connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+#endif
+      return -1;
+    }
+  if (res < 0)
+    {
+      /* some other GNUTLS error, should set 'errno'; as we do not
+         really understand the error (not listed in GnuTLS
+         documentation explicitly), we set 'errno' to something that
+         will cause the connection to fail. */
+      MHD_set_socket_errno_ (ECONNRESET);
+      return -1;
+    }
+  return res;
+}
+
+
+/**
+ * Read and setup our certificate and key.
+ *
+ * @param daemon handle to daemon to initialize
+ * @return 0 on success
+ */
+static int
+MHD_init_daemon_certificate (struct MHD_Daemon *daemon)
+{
+  gnutls_datum_t key;
+  gnutls_datum_t cert;
+  int ret;
+
+#if GNUTLS_VERSION_MAJOR >= 3
+  if (NULL != daemon->cert_callback)
+    {
+      gnutls_certificate_set_retrieve_function2 (daemon->x509_cred,
+                                                 daemon->cert_callback);
+    }
+#endif
+  if (NULL != daemon->https_mem_trust)
+    {
+      cert.data = (unsigned char *) daemon->https_mem_trust;
+      cert.size = strlen (daemon->https_mem_trust);
+      if (gnutls_certificate_set_x509_trust_mem (daemon->x509_cred, &cert,
+						 GNUTLS_X509_FMT_PEM) < 0)
+	{
+#if HAVE_MESSAGES
+	  MHD_DLOG(daemon,
+		   "Bad trust certificate format\n");
+#endif
+	  return -1;
+	}
+    }
+
+  if (MHD_YES == daemon->have_dhparams)
+    {
+      gnutls_certificate_set_dh_params (daemon->x509_cred,
+                                        daemon->https_mem_dhparams);
+    }
+  /* certificate & key loaded from memory */
+  if ( (NULL != daemon->https_mem_cert) &&
+       (NULL != daemon->https_mem_key) )
+    {
+      key.data = (unsigned char *) daemon->https_mem_key;
+      key.size = strlen (daemon->https_mem_key);
+      cert.data = (unsigned char *) daemon->https_mem_cert;
+      cert.size = strlen (daemon->https_mem_cert);
+
+      if (NULL != daemon->https_key_password) {
+#if GNUTLS_VERSION_NUMBER >= 0x030111
+        ret = gnutls_certificate_set_x509_key_mem2 (daemon->x509_cred,
+                                                    &cert, &key,
+                                                    GNUTLS_X509_FMT_PEM,
+                                                    daemon->https_key_password,
+                                                    0);
+#else
+#if HAVE_MESSAGES
+	MHD_DLOG (daemon,
+                  "Failed to setup x509 certificate/key: pre 3.X.X version " \
+		  "of GnuTLS does not support setting key password");
+#endif
+	return -1;
+#endif
+      }
+      else
+        ret = gnutls_certificate_set_x509_key_mem (daemon->x509_cred,
+                                                   &cert, &key,
+                                                   GNUTLS_X509_FMT_PEM);
+#if HAVE_MESSAGES
+      if (0 != ret)
+        MHD_DLOG (daemon,
+                  "GnuTLS failed to setup x509 certificate/key: %s\n",
+                  gnutls_strerror (ret));
+#endif
+      return ret;
+    }
+#if GNUTLS_VERSION_MAJOR >= 3
+  if (NULL != daemon->cert_callback)
+    return 0;
+#endif
+#if HAVE_MESSAGES
+  MHD_DLOG (daemon,
+            "You need to specify a certificate and key location\n");
+#endif
+  return -1;
+}
+
+
+/**
+ * Initialize security aspects of the HTTPS daemon
+ *
+ * @param daemon handle to daemon to initialize
+ * @return 0 on success
+ */
+static int
+MHD_TLS_init (struct MHD_Daemon *daemon)
+{
+  switch (daemon->cred_type)
+    {
+    case GNUTLS_CRD_CERTIFICATE:
+      if (0 !=
+          gnutls_certificate_allocate_credentials (&daemon->x509_cred))
+        return GNUTLS_E_MEMORY_ERROR;
+      return MHD_init_daemon_certificate (daemon);
+    default:
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                "Error: invalid credentials type %d specified.\n",
+                daemon->cred_type);
+#endif
+      return -1;
+    }
+}
+#endif
+
+
+/**
+ * Add @a fd to the @a set.  If @a fd is
+ * greater than @a max_fd, set @a max_fd to @a fd.
+ *
+ * @param fd file descriptor to add to the @a set
+ * @param set set to modify
+ * @param max_fd maximum value to potentially update
+ * @param fd_setsize value of FD_SETSIZE
+ * @return #MHD_YES on success, #MHD_NO otherwise
+ */
+static int
+add_to_fd_set (MHD_socket fd,
+	       fd_set *set,
+	       MHD_socket *max_fd,
+	       unsigned int fd_setsize)
+{
+  if (NULL == set)
+    return MHD_NO;
+#ifdef MHD_WINSOCK_SOCKETS
+  if (set->fd_count >= fd_setsize)
+    {
+      if (FD_ISSET(fd, set))
+        return MHD_YES;
+      else
+        return MHD_NO;
+    }
+#else  // ! MHD_WINSOCK_SOCKETS
+  if (fd >= fd_setsize)
+    return MHD_NO;
+#endif // ! MHD_WINSOCK_SOCKETS
+  FD_SET (fd, set);
+  if ( (NULL != max_fd) && (MHD_INVALID_SOCKET != fd) &&
+       ((fd > *max_fd) || (MHD_INVALID_SOCKET == *max_fd)) )
+    *max_fd = fd;
+
+  return MHD_YES;
+}
+
+#undef MHD_get_fdset
+
+/**
+ * Obtain the `select()` sets for this daemon.
+ * Daemon's FDs will be added to fd_sets. To get only
+ * daemon FDs in fd_sets, call FD_ZERO for each fd_set
+ * before calling this function. FD_SETSIZE is assumed
+ * to be platform's default.
+ *
+ * @param daemon daemon to get sets from
+ * @param read_fd_set read set
+ * @param write_fd_set write set
+ * @param except_fd_set except set
+ * @param max_fd increased to largest FD added (if larger
+ *               than existing value); can be NULL
+ * @return #MHD_YES on success, #MHD_NO if this
+ *         daemon was not started with the right
+ *         options for this call or any FD didn't
+ *         fit fd_set.
+ * @ingroup event
+ */
+int
+MHD_get_fdset (struct MHD_Daemon *daemon,
+               fd_set *read_fd_set,
+               fd_set *write_fd_set,
+	       fd_set *except_fd_set,
+	       MHD_socket *max_fd)
+{
+  return MHD_get_fdset2(daemon, read_fd_set,
+      write_fd_set, except_fd_set,
+      max_fd, MHD_DEFAULT_FD_SETSIZE);
+}
+
+/**
+ * Obtain the `select()` sets for this daemon.
+ * Daemon's FDs will be added to fd_sets. To get only
+ * daemon FDs in fd_sets, call FD_ZERO for each fd_set
+ * before calling this function. Passing custom FD_SETSIZE
+ * as @a fd_setsize allow usage of larger/smaller than
+ * platform's default fd_sets.
+ *
+ * @param daemon daemon to get sets from
+ * @param read_fd_set read set
+ * @param write_fd_set write set
+ * @param except_fd_set except set
+ * @param max_fd increased to largest FD added (if larger
+ *               than existing value); can be NULL
+ * @param fd_setsize value of FD_SETSIZE
+ * @return #MHD_YES on success, #MHD_NO if this
+ *         daemon was not started with the right
+ *         options for this call or any FD didn't
+ *         fit fd_set.
+ * @ingroup event
+ */
+int
+MHD_get_fdset2 (struct MHD_Daemon *daemon,
+               fd_set *read_fd_set,
+               fd_set *write_fd_set,
+               fd_set *except_fd_set,
+               MHD_socket *max_fd,
+               unsigned int fd_setsize)
+{
+  struct MHD_Connection *pos;
+
+  if ( (NULL == daemon)
+       || (NULL == read_fd_set)
+       || (NULL == write_fd_set)
+       || (MHD_YES == daemon->shutdown)
+       || (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+       || (0 != (daemon->options & MHD_USE_POLL)))
+    return MHD_NO;
+#if EPOLL_SUPPORT
+  if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
+    {
+      /* we're in epoll mode, use the epoll FD as a stand-in for
+	 the entire event set */
+
+      return add_to_fd_set (daemon->epoll_fd, read_fd_set, max_fd, fd_setsize);
+    }
+#endif
+  if (MHD_INVALID_SOCKET != daemon->socket_fd &&
+      MHD_YES != add_to_fd_set (daemon->socket_fd, read_fd_set, max_fd, fd_setsize))
+    return MHD_NO;
+
+  for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
+    {
+      switch (pos->event_loop_info)
+	{
+	case MHD_EVENT_LOOP_INFO_READ:
+	  if (MHD_YES != add_to_fd_set (pos->socket_fd, read_fd_set, max_fd, fd_setsize))
+	    return MHD_NO;
+	  break;
+	case MHD_EVENT_LOOP_INFO_WRITE:
+	  if (MHD_YES != add_to_fd_set (pos->socket_fd, write_fd_set, max_fd, fd_setsize))
+	    return MHD_NO;
+	  if (pos->read_buffer_size > pos->read_buffer_offset &&
+	      MHD_YES != add_to_fd_set (pos->socket_fd, read_fd_set, max_fd, fd_setsize))
+            return MHD_NO;
+	  break;
+	case MHD_EVENT_LOOP_INFO_BLOCK:
+	  if (pos->read_buffer_size > pos->read_buffer_offset &&
+	      MHD_YES != add_to_fd_set (pos->socket_fd, read_fd_set, max_fd, fd_setsize))
+            return MHD_NO;
+	  break;
+	case MHD_EVENT_LOOP_INFO_CLEANUP:
+	  /* this should never happen */
+	  break;
+	}
+    }
+#if DEBUG_CONNECT
+#if HAVE_MESSAGES
+  if (NULL != max_fd)
+    MHD_DLOG (daemon,
+              "Maximum socket in select set: %d\n",
+              *max_fd);
+#endif
+#endif
+  return MHD_YES;
+}
+
+
+/**
+ * Main function of the thread that handles an individual
+ * connection when #MHD_USE_THREAD_PER_CONNECTION is set.
+ *
+ * @param data the `struct MHD_Connection` this thread will handle
+ * @return always 0
+ */
+static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
+MHD_handle_connection (void *data)
+{
+  struct MHD_Connection *con = data;
+  int num_ready;
+  fd_set rs;
+  fd_set ws;
+  MHD_socket max;
+  struct timeval tv;
+  struct timeval *tvp;
+  unsigned int timeout;
+  time_t now;
+#if WINDOWS
+  MHD_pipe spipe = con->daemon->wpipe[0];
+  char tmp;
+#ifdef HAVE_POLL
+  int extra_slot;
+#endif /* HAVE_POLL */
+#define EXTRA_SLOTS 1
+#else  /* !WINDOWS */
+#define EXTRA_SLOTS 0
+#endif /* !WINDOWS */
+#ifdef HAVE_POLL
+  struct pollfd p[1 + EXTRA_SLOTS];
+#endif
+
+  timeout = con->daemon->connection_timeout;
+  while ( (MHD_YES != con->daemon->shutdown) &&
+	  (MHD_CONNECTION_CLOSED != con->state) )
+    {
+      tvp = NULL;
+      if (timeout > 0)
+	{
+	  now = MHD_monotonic_time();
+	  if (now - con->last_activity > timeout)
+	    tv.tv_sec = 0;
+	  else
+	    tv.tv_sec = timeout - (now - con->last_activity);
+	  tv.tv_usec = 0;
+	  tvp = &tv;
+	}
+#if HTTPS_SUPPORT
+      if (MHD_YES == con->tls_read_ready)
+	{
+	  /* do not block (more data may be inside of TLS buffers waiting for us) */
+	  tv.tv_sec = 0;
+	  tv.tv_usec = 0;
+	  tvp = &tv;
+	}
+#endif
+      if (0 == (con->daemon->options & MHD_USE_POLL))
+	{
+	  /* use select */
+	  int err_state = 0;
+	  FD_ZERO (&rs);
+	  FD_ZERO (&ws);
+	  max = 0;
+	  switch (con->event_loop_info)
+	    {
+	    case MHD_EVENT_LOOP_INFO_READ:
+	      if (MHD_YES !=
+                  add_to_fd_set (con->socket_fd, &rs, &max, FD_SETSIZE))
+	        err_state = 1;
+	      break;
+	    case MHD_EVENT_LOOP_INFO_WRITE:
+	      if (MHD_YES !=
+                  add_to_fd_set (con->socket_fd, &ws, &max, FD_SETSIZE))
+                err_state = 1;
+	      if ( (con->read_buffer_size > con->read_buffer_offset) &&
+                   (MHD_YES !=
+                    add_to_fd_set (con->socket_fd, &rs, &max, FD_SETSIZE)) )
+	        err_state = 1;
+	      break;
+	    case MHD_EVENT_LOOP_INFO_BLOCK:
+	      if ( (con->read_buffer_size > con->read_buffer_offset) &&
+                   (MHD_YES !=
+                    add_to_fd_set (con->socket_fd, &rs, &max, FD_SETSIZE)) )
+	        err_state = 1;
+	      tv.tv_sec = 0;
+	      tv.tv_usec = 0;
+	      tvp = &tv;
+	      break;
+	    case MHD_EVENT_LOOP_INFO_CLEANUP:
+	      /* how did we get here!? */
+	      goto exit;
+	    }
+#if WINDOWS
+          if (MHD_INVALID_PIPE_ != spipe)
+            {
+              if (MHD_YES !=
+                  add_to_fd_set (spipe, &rs, &max, FD_SETSIZE))
+                err_state = 1;
+            }
+#endif
+            if (0 != err_state)
+              {
+#if HAVE_MESSAGES
+                MHD_DLOG (con->daemon,
+                          "Can't add FD to fd_set\n");
+#endif
+                goto exit;
+              }
+
+	  num_ready = MHD_SYS_select_ (max + 1, &rs, &ws, NULL, tvp);
+	  if (num_ready < 0)
+	    {
+	      if (EINTR == MHD_socket_errno_)
+		continue;
+#if HAVE_MESSAGES
+	      MHD_DLOG (con->daemon,
+			"Error during select (%d): `%s'\n",
+			MHD_socket_errno_,
+			MHD_socket_last_strerr_ ());
+#endif
+	      break;
+	    }
+#if WINDOWS
+          /* drain signaling pipe */
+          if ( (MHD_INVALID_PIPE_ != spipe) &&
+               (FD_ISSET (spipe, &rs)) )
+            (void) MHD_pipe_read_ (spipe, &tmp, sizeof (tmp));
+#endif
+	  /* call appropriate connection handler if necessary */
+	  if ( (FD_ISSET (con->socket_fd, &rs))
+#if HTTPS_SUPPORT
+	       || (MHD_YES == con->tls_read_ready)
+#endif
+	       )
+	    con->read_handler (con);
+	  if (FD_ISSET (con->socket_fd, &ws))
+	    con->write_handler (con);
+	  if (MHD_NO == con->idle_handler (con))
+	    goto exit;
+	}
+#ifdef HAVE_POLL
+      else
+	{
+	  /* use poll */
+	  memset (&p, 0, sizeof (p));
+	  p[0].fd = con->socket_fd;
+	  switch (con->event_loop_info)
+	    {
+	    case MHD_EVENT_LOOP_INFO_READ:
+	      p[0].events |= POLLIN;
+	      break;
+	    case MHD_EVENT_LOOP_INFO_WRITE:
+	      p[0].events |= POLLOUT;
+	      if (con->read_buffer_size > con->read_buffer_offset)
+		p[0].events |= POLLIN;
+	      break;
+	    case MHD_EVENT_LOOP_INFO_BLOCK:
+	      if (con->read_buffer_size > con->read_buffer_offset)
+		p[0].events |= POLLIN;
+	      tv.tv_sec = 0;
+	      tv.tv_usec = 0;
+	      tvp = &tv;
+	      break;
+	    case MHD_EVENT_LOOP_INFO_CLEANUP:
+	      /* how did we get here!? */
+	      goto exit;
+	    }
+#if WINDOWS
+          extra_slot = 0;
+          if (MHD_INVALID_PIPE_ != spipe)
+            {
+              p[1].events |= POLLIN;
+              p[1].fd = spipe;
+              p[1].revents = 0;
+              extra_slot = 1;
+            }
+#endif
+	  if (MHD_sys_poll_ (p,
+#if WINDOWS
+                    1 + extra_slot,
+#else
+                    1,
+#endif
+		    (NULL == tvp) ? -1 : tv.tv_sec * 1000) < 0)
+	    {
+	      if (EINTR == MHD_socket_errno_)
+		continue;
+#if HAVE_MESSAGES
+	      MHD_DLOG (con->daemon,
+                        "Error during poll: `%s'\n",
+			MHD_socket_last_strerr_ ());
+#endif
+	      break;
+	    }
+#if WINDOWS
+          /* drain signaling pipe */
+          if ( (MHD_INVALID_PIPE_ != spipe) &&
+               (0 != (p[1].revents & (POLLERR | POLLHUP))) )
+            (void) MHD_pipe_read_ (spipe, &tmp, sizeof (tmp));
+#endif
+	  if ( (0 != (p[0].revents & POLLIN))
+#if HTTPS_SUPPORT
+	       || (MHD_YES == con->tls_read_ready)
+#endif
+	       )
+	    con->read_handler (con);
+	  if (0 != (p[0].revents & POLLOUT))
+	    con->write_handler (con);
+	  if (0 != (p[0].revents & (POLLERR | POLLHUP)))
+	    MHD_connection_close (con, MHD_REQUEST_TERMINATED_WITH_ERROR);
+	  if (MHD_NO == con->idle_handler (con))
+	    goto exit;
+	}
+#endif
+    }
+  if (MHD_CONNECTION_IN_CLEANUP != con->state)
+    {
+#if DEBUG_CLOSE
+#if HAVE_MESSAGES
+      MHD_DLOG (con->daemon,
+                "Processing thread terminating, closing connection\n");
+#endif
+#endif
+      if (MHD_CONNECTION_CLOSED != con->state)
+	MHD_connection_close (con,
+			      MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
+      con->idle_handler (con);
+    }
+exit:
+  if (NULL != con->response)
+    {
+      MHD_destroy_response (con->response);
+      con->response = NULL;
+    }
+
+  if (NULL != con->daemon->notify_connection)
+    con->daemon->notify_connection (con->daemon->notify_connection_cls,
+                                    con,
+                                    &con->socket_context,
+                                    MHD_CONNECTION_NOTIFY_CLOSED);
+
+  return (MHD_THRD_RTRN_TYPE_)0;
+}
+
+
+/**
+ * Callback for receiving data from the socket.
+ *
+ * @param connection the MHD connection structure
+ * @param other where to write received data to
+ * @param i maximum size of other (in bytes)
+ * @return number of bytes actually received
+ */
+static ssize_t
+recv_param_adapter (struct MHD_Connection *connection,
+		    void *other,
+		    size_t i)
+{
+  ssize_t ret;
+
+  if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
+       (MHD_CONNECTION_CLOSED == connection->state) )
+    {
+      MHD_set_socket_errno_ (ENOTCONN);
+      return -1;
+    }
+  ret = recv (connection->socket_fd, other, i, MSG_NOSIGNAL);
+#if EPOLL_SUPPORT
+  if (ret < (ssize_t) i)
+    {
+      /* partial read --- no longer read-ready */
+      connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY;
+    }
+#endif
+  return ret;
+}
+
+
+/**
+ * Callback for writing data to the socket.
+ *
+ * @param connection the MHD connection structure
+ * @param other data to write
+ * @param i number of bytes to write
+ * @return actual number of bytes written
+ */
+static ssize_t
+send_param_adapter (struct MHD_Connection *connection,
+                    const void *other,
+		    size_t i)
+{
+  ssize_t ret;
+#if LINUX
+  MHD_socket fd;
+  off_t offset;
+  off_t left;
+#endif
+
+  if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
+       (MHD_CONNECTION_CLOSED == connection->state) )
+    {
+      MHD_set_socket_errno_ (ENOTCONN);
+      return -1;
+    }
+  if (0 != (connection->daemon->options & MHD_USE_SSL))
+    return send (connection->socket_fd, other, i, MSG_NOSIGNAL);
+#if LINUX
+  if ( (connection->write_buffer_append_offset ==
+	connection->write_buffer_send_offset) &&
+       (NULL != connection->response) &&
+       (MHD_INVALID_SOCKET != (fd = connection->response->fd)) )
+    {
+      /* can use sendfile */
+      offset = (off_t) connection->response_write_position + connection->response->fd_off;
+      left = connection->response->total_size - connection->response_write_position;
+      if (left > SSIZE_MAX)
+	left = SSIZE_MAX; /* cap at return value limit */
+      if (-1 != (ret = sendfile (connection->socket_fd,
+				 fd,
+				 &offset,
+				 (size_t) left)))
+	{
+#if EPOLL_SUPPORT
+	  if (ret < left)
+	    {
+	      /* partial write --- no longer write-ready */
+	      connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+	    }
+#endif
+	  return ret;
+	}
+      const int err = MHD_socket_errno_;
+      if ( (EINTR == err) || (EAGAIN == err) || (EWOULDBLOCK == err) )
+	return 0;
+      if ( (EINVAL == err) || (EBADF == err) )
+	return -1;
+      /* None of the 'usual' sendfile errors occurred, so we should try
+	 to fall back to 'SEND'; see also this thread for info on
+	 odd libc/Linux behavior with sendfile:
+	 http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html */
+    }
+#endif
+  ret = send (connection->socket_fd, other, i, MSG_NOSIGNAL);
+#if EPOLL_SUPPORT
+  if (ret < (ssize_t) i)
+    {
+      /* partial write --- no longer write-ready */
+      connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
+    }
+#endif
+  /* Handle broken kernel / libc, returning -1 but not setting errno;
+     kill connection as that should be safe; reported on mailinglist here:
+     http://lists.gnu.org/archive/html/libmicrohttpd/2014-10/msg00023.html */
+  if ( (-1 == ret) && (0 == errno) )
+    errno = ECONNRESET;
+  return ret;
+}
+
+
+/**
+ * Signature of main function for a thread.
+ *
+ * @param cls closure argument for the function
+ * @return termination code from the thread
+ */
+typedef MHD_THRD_RTRN_TYPE_ (MHD_THRD_CALL_SPEC_ *ThreadStartRoutine)(void *cls);
+
+
+/**
+ * Create a thread and set the attributes according to our options.
+ *
+ * @param thread handle to initialize
+ * @param daemon daemon with options
+ * @param start_routine main function of thread
+ * @param arg argument for start_routine
+ * @return 0 on success
+ */
+static int
+create_thread (MHD_thread_handle_ *thread,
+	       const struct MHD_Daemon *daemon,
+	       ThreadStartRoutine start_routine,
+	       void *arg)
+{
+#if defined(MHD_USE_POSIX_THREADS)
+  pthread_attr_t attr;
+  pthread_attr_t *pattr;
+  int ret;
+
+  if (0 != daemon->thread_stack_size)
+    {
+      if (0 != (ret = pthread_attr_init (&attr)))
+	goto ERR;
+      if (0 != (ret = pthread_attr_setstacksize (&attr, daemon->thread_stack_size)))
+	{
+	  pthread_attr_destroy (&attr);
+	  goto ERR;
+	}
+      pattr = &attr;
+    }
+  else
+    {
+      pattr = NULL;
+    }
+  ret = pthread_create (thread, pattr,
+			start_routine, arg);
+#ifdef HAVE_PTHREAD_SETNAME_NP
+  (void) pthread_setname_np (*thread, "libmicrohttpd");
+#endif /* HAVE_PTHREAD_SETNAME_NP */
+  if (0 != daemon->thread_stack_size)
+    pthread_attr_destroy (&attr);
+  return ret;
+ ERR:
+#if HAVE_MESSAGES
+  MHD_DLOG (daemon,
+	    "Failed to set thread stack size\n");
+#endif
+  errno = EINVAL;
+  return ret;
+#elif defined(MHD_USE_W32_THREADS)
+  unsigned threadID;
+  *thread = (HANDLE)_beginthreadex(NULL, (unsigned)daemon->thread_stack_size, start_routine,
+                          arg, 0, &threadID);
+  if (NULL == (*thread))
+    return errno;
+
+  W32_SetThreadName(threadID, "libmicrohttpd");
+
+  return 0;
+#endif
+}
+
+
+/**
+ * Add another client connection to the set of connections
+ * managed by MHD.  This API is usually not needed (since
+ * MHD will accept inbound connections on the server socket).
+ * Use this API in special cases, for example if your HTTP
+ * server is behind NAT and needs to connect out to the
+ * HTTP client.
+ *
+ * The given client socket will be managed (and closed!) by MHD after
+ * this call and must no longer be used directly by the application
+ * afterwards.
+ *
+ * Per-IP connection limits are ignored when using this API.
+ *
+ * @param daemon daemon that manages the connection
+ * @param client_socket socket to manage (MHD will expect
+ *        to receive an HTTP request from this socket next).
+ * @param addr IP address of the client
+ * @param addrlen number of bytes in @a addr
+ * @param external_add perform additional operations needed due
+ *        to the application calling us directly
+ * @return #MHD_YES on success, #MHD_NO if this daemon could
+ *        not handle the connection (i.e. malloc failed, etc).
+ *        The socket will be closed in any case; 'errno' is
+ *        set to indicate further details about the error.
+ */
+static int
+internal_add_connection (struct MHD_Daemon *daemon,
+			 MHD_socket client_socket,
+			 const struct sockaddr *addr,
+			 socklen_t addrlen,
+			 int external_add)
+{
+  struct MHD_Connection *connection;
+  int res_thread_create;
+  unsigned int i;
+  int eno;
+  struct MHD_Daemon *worker;
+#if OSX
+  static int on = 1;
+#endif
+
+  if (NULL != daemon->worker_pool)
+    {
+      /* have a pool, try to find a pool with capacity; we use the
+	 socket as the initial offset into the pool for load
+	 balancing */
+      for (i=0;i<daemon->worker_pool_size;i++)
+        {
+          worker = &daemon->worker_pool[(i + client_socket) % daemon->worker_pool_size];
+          if (worker->connections < worker->connection_limit)
+            return internal_add_connection (worker,
+                                            client_socket,
+                                            addr, addrlen,
+                                            external_add);
+        }
+      /* all pools are at their connection limit, must refuse */
+      if (0 != MHD_socket_close_ (client_socket))
+	MHD_PANIC ("close failed\n");
+#if ENFILE
+      errno = ENFILE;
+#endif
+      return MHD_NO;
+    }
+
+#ifndef WINDOWS
+  if ( (client_socket >= FD_SETSIZE) &&
+       (0 == (daemon->options & (MHD_USE_POLL | MHD_USE_EPOLL_LINUX_ONLY))) )
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"Socket descriptor larger than FD_SETSIZE: %d > %d\n",
+		client_socket,
+		FD_SETSIZE);
+#endif
+      if (0 != MHD_socket_close_ (client_socket))
+	MHD_PANIC ("close failed\n");
+#if EINVAL
+      errno = EINVAL;
+#endif
+      return MHD_NO;
+    }
+#endif
+
+
+#if HAVE_MESSAGES
+#if DEBUG_CONNECT
+  MHD_DLOG (daemon,
+            "Accepted connection on socket %d\n",
+            client_socket);
+#endif
+#endif
+  if ( (daemon->connections == daemon->connection_limit) ||
+       (MHD_NO == MHD_ip_limit_add (daemon, addr, addrlen)) )
+    {
+      /* above connection limit - reject */
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                "Server reached connection limit (closing inbound connection)\n");
+#endif
+      if (0 != MHD_socket_close_ (client_socket))
+	MHD_PANIC ("close failed\n");
+#if ENFILE
+      errno = ENFILE;
+#endif
+      return MHD_NO;
+    }
+
+  /* apply connection acceptance policy if present */
+  if ( (NULL != daemon->apc) &&
+       (MHD_NO == daemon->apc (daemon->apc_cls,
+			       addr, addrlen)) )
+    {
+#if DEBUG_CLOSE
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                "Connection rejected, closing connection\n");
+#endif
+#endif
+      if (0 != MHD_socket_close_ (client_socket))
+	MHD_PANIC ("close failed\n");
+      MHD_ip_limit_del (daemon, addr, addrlen);
+#if EACCESS
+      errno = EACCESS;
+#endif
+      return MHD_NO;
+    }
+
+#if OSX
+#ifdef SOL_SOCKET
+#ifdef SO_NOSIGPIPE
+  setsockopt (client_socket,
+	      SOL_SOCKET, SO_NOSIGPIPE,
+	      &on, sizeof (on));
+#endif
+#endif
+#endif
+
+  if (NULL == (connection = malloc (sizeof (struct MHD_Connection))))
+    {
+      eno = errno;
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"Error allocating memory: %s\n",
+		MHD_strerror_ (errno));
+#endif
+      if (0 != MHD_socket_close_ (client_socket))
+	MHD_PANIC ("close failed\n");
+      MHD_ip_limit_del (daemon, addr, addrlen);
+      errno = eno;
+      return MHD_NO;
+    }
+  memset (connection,
+          0,
+          sizeof (struct MHD_Connection));
+  connection->pool = MHD_pool_create (daemon->pool_size);
+  if (NULL == connection->pool)
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"Error allocating memory: %s\n",
+		MHD_strerror_ (errno));
+#endif
+      if (0 != MHD_socket_close_ (client_socket))
+	MHD_PANIC ("close failed\n");
+      MHD_ip_limit_del (daemon, addr, addrlen);
+      free (connection);
+#if ENOMEM
+      errno = ENOMEM;
+#endif
+      return MHD_NO;
+    }
+
+  connection->connection_timeout = daemon->connection_timeout;
+  if (NULL == (connection->addr = malloc (addrlen)))
+    {
+      eno = errno;
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"Error allocating memory: %s\n",
+		MHD_strerror_ (errno));
+#endif
+      if (0 != MHD_socket_close_ (client_socket))
+	MHD_PANIC ("close failed\n");
+      MHD_ip_limit_del (daemon, addr, addrlen);
+      MHD_pool_destroy (connection->pool);
+      free (connection);
+      errno = eno;
+      return MHD_NO;
+    }
+  memcpy (connection->addr, addr, addrlen);
+  connection->addr_len = addrlen;
+  connection->socket_fd = client_socket;
+  connection->daemon = daemon;
+  connection->last_activity = MHD_monotonic_time();
+
+  /* set default connection handlers  */
+  MHD_set_http_callbacks_ (connection);
+  connection->recv_cls = &recv_param_adapter;
+  connection->send_cls = &send_param_adapter;
+
+  if (0 == (connection->daemon->options & MHD_USE_EPOLL_TURBO))
+    {
+      /* non-blocking sockets are required on most systems and for GNUtls;
+	 however, they somehow cause serious problems on CYGWIN (#1824);
+	 in turbo mode, we assume that non-blocking was already set
+	 by 'accept4' or whoever calls 'MHD_add_connection' */
+#ifdef CYGWIN
+      if (0 != (daemon->options & MHD_USE_SSL))
+#endif
+	{
+	  /* make socket non-blocking */
+#if !defined(WINDOWS) || defined(CYGWIN)
+	  int flags = fcntl (connection->socket_fd, F_GETFL);
+	  if ( (-1 == flags) ||
+	       (0 != fcntl (connection->socket_fd, F_SETFL, flags | O_NONBLOCK)) )
+	    {
+#if HAVE_MESSAGES
+	      MHD_DLOG (daemon,
+			"Failed to make socket non-blocking: %s\n",
+			MHD_socket_last_strerr_ ());
+#endif
+	    }
+#else
+	  unsigned long flags = 1;
+	  if (0 != ioctlsocket (connection->socket_fd, FIONBIO, &flags))
+	    {
+#if HAVE_MESSAGES
+	      MHD_DLOG (daemon,
+			"Failed to make socket non-blocking: %s\n",
+			MHD_socket_last_strerr_ ());
+#endif
+	    }
+#endif
+	}
+    }
+
+#if HTTPS_SUPPORT
+  if (0 != (daemon->options & MHD_USE_SSL))
+    {
+      connection->recv_cls = &recv_tls_adapter;
+      connection->send_cls = &send_tls_adapter;
+      connection->state = MHD_TLS_CONNECTION_INIT;
+      MHD_set_https_callbacks (connection);
+      gnutls_init (&connection->tls_session, GNUTLS_SERVER);
+      gnutls_priority_set (connection->tls_session,
+			   daemon->priority_cache);
+      switch (daemon->cred_type)
+        {
+          /* set needed credentials for certificate authentication. */
+        case GNUTLS_CRD_CERTIFICATE:
+          gnutls_credentials_set (connection->tls_session,
+				  GNUTLS_CRD_CERTIFICATE,
+				  daemon->x509_cred);
+          break;
+        default:
+#if HAVE_MESSAGES
+          MHD_DLOG (connection->daemon,
+                    "Failed to setup TLS credentials: unknown credential type %d\n",
+                    daemon->cred_type);
+#endif
+          if (0 != MHD_socket_close_ (client_socket))
+	    MHD_PANIC ("close failed\n");
+          MHD_ip_limit_del (daemon, addr, addrlen);
+          free (connection->addr);
+          free (connection);
+          MHD_PANIC ("Unknown credential type");
+#if EINVAL
+	  errno = EINVAL;
+#endif
+ 	  return MHD_NO;
+        }
+      gnutls_transport_set_ptr (connection->tls_session,
+				(gnutls_transport_ptr_t) connection);
+      gnutls_transport_set_pull_function (connection->tls_session,
+					  (gnutls_pull_func) &recv_param_adapter);
+      gnutls_transport_set_push_function (connection->tls_session,
+					  (gnutls_push_func) &send_param_adapter);
+
+      if (daemon->https_mem_trust)
+	  gnutls_certificate_server_set_request (connection->tls_session,
+						 GNUTLS_CERT_REQUEST);
+    }
+#endif
+
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to acquire cleanup mutex\n");
+  XDLL_insert (daemon->normal_timeout_head,
+	       daemon->normal_timeout_tail,
+	       connection);
+  DLL_insert (daemon->connections_head,
+	      daemon->connections_tail,
+	      connection);
+  if  ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+	(MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to release cleanup mutex\n");
+
+  if (NULL != daemon->notify_connection)
+    daemon->notify_connection (daemon->notify_connection_cls,
+                               connection,
+                               &connection->socket_context,
+                               MHD_CONNECTION_NOTIFY_STARTED);
+
+  /* attempt to create handler thread */
+  if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+    {
+      res_thread_create = create_thread (&connection->pid,
+                                         daemon,
+					 &MHD_handle_connection,
+                                         connection);
+      if (0 != res_thread_create)
+        {
+	  eno = errno;
+#if HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "Failed to create a thread: %s\n",
+                    MHD_strerror_ (res_thread_create));
+#endif
+	  goto cleanup;
+        }
+    }
+  else
+    if ( (MHD_YES == external_add) &&
+	 (MHD_INVALID_PIPE_ != daemon->wpipe[1]) &&
+	 (1 != MHD_pipe_write_ (daemon->wpipe[1], "n", 1)) )
+      {
+#if HAVE_MESSAGES
+	MHD_DLOG (daemon,
+		  "failed to signal new connection via pipe");
+#endif
+      }
+#if EPOLL_SUPPORT
+  if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
+    {
+      if (0 == (daemon->options & MHD_USE_EPOLL_TURBO))
+	{
+	  struct epoll_event event;
+
+	  event.events = EPOLLIN | EPOLLOUT | EPOLLET;
+	  event.data.ptr = connection;
+	  if (0 != epoll_ctl (daemon->epoll_fd,
+			      EPOLL_CTL_ADD,
+			      client_socket,
+			      &event))
+	    {
+	      eno = errno;
+#if HAVE_MESSAGES
+              MHD_DLOG (daemon,
+                        "Call to epoll_ctl failed: %s\n",
+                        MHD_socket_last_strerr_ ());
+#endif
+	      goto cleanup;
+	    }
+	  connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
+	}
+      else
+	{
+	  connection->epoll_state |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY
+	    | MHD_EPOLL_STATE_IN_EREADY_EDLL;
+	  EDLL_insert (daemon->eready_head,
+		       daemon->eready_tail,
+		       connection);
+	}
+    }
+#endif
+  daemon->connections++;
+  return MHD_YES;
+ cleanup:
+  if (NULL != daemon->notify_connection)
+    daemon->notify_connection (daemon->notify_connection_cls,
+                               connection,
+                               &connection->socket_context,
+                               MHD_CONNECTION_NOTIFY_CLOSED);
+  if (0 != MHD_socket_close_ (client_socket))
+    MHD_PANIC ("close failed\n");
+  MHD_ip_limit_del (daemon, addr, addrlen);
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to acquire cleanup mutex\n");
+  DLL_remove (daemon->connections_head,
+	      daemon->connections_tail,
+	      connection);
+  XDLL_remove (daemon->normal_timeout_head,
+	       daemon->normal_timeout_tail,
+	       connection);
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to release cleanup mutex\n");
+  MHD_pool_destroy (connection->pool);
+  free (connection->addr);
+  free (connection);
+#if EINVAL
+  errno = eno;
+#endif
+  return MHD_NO;
+}
+
+
+/**
+ * Suspend handling of network data for a given connection.  This can
+ * be used to dequeue a connection from MHD's event loop (external
+ * select, internal select or thread pool; not applicable to
+ * thread-per-connection!) for a while.
+ *
+ * If you use this API in conjunction with a internal select or a
+ * thread pool, you must set the option #MHD_USE_PIPE_FOR_SHUTDOWN to
+ * ensure that a resumed connection is immediately processed by MHD.
+ *
+ * Suspended connections continue to count against the total number of
+ * connections allowed (per daemon, as well as per IP, if such limits
+ * are set).  Suspended connections will NOT time out; timeouts will
+ * restart when the connection handling is resumed.  While a
+ * connection is suspended, MHD will not detect disconnects by the
+ * client.
+ *
+ * The only safe time to suspend a connection is from the
+ * #MHD_AccessHandlerCallback.
+ *
+ * Finally, it is an API violation to call #MHD_stop_daemon while
+ * having suspended connections (this will at least create memory and
+ * socket leaks or lead to undefined behavior).  You must explicitly
+ * resume all connections before stopping the daemon.
+ *
+ * @param connection the connection to suspend
+ */
+void
+MHD_suspend_connection (struct MHD_Connection *connection)
+{
+  struct MHD_Daemon *daemon;
+
+  daemon = connection->daemon;
+  if (MHD_USE_SUSPEND_RESUME != (daemon->options & MHD_USE_SUSPEND_RESUME))
+    MHD_PANIC ("Cannot suspend connections without enabling MHD_USE_SUSPEND_RESUME!\n");
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to acquire cleanup mutex\n");
+  DLL_remove (daemon->connections_head,
+              daemon->connections_tail,
+              connection);
+  DLL_insert (daemon->suspended_connections_head,
+              daemon->suspended_connections_tail,
+              connection);
+  if (connection->connection_timeout == daemon->connection_timeout)
+    XDLL_remove (daemon->normal_timeout_head,
+                 daemon->normal_timeout_tail,
+                 connection);
+  else
+    XDLL_remove (daemon->manual_timeout_head,
+                 daemon->manual_timeout_tail,
+                 connection);
+#if EPOLL_SUPPORT
+  if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
+    {
+      if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
+        {
+          EDLL_remove (daemon->eready_head,
+                       daemon->eready_tail,
+                       connection);
+          connection->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL;
+        }
+      if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET))
+        {
+          if (0 != epoll_ctl (daemon->epoll_fd,
+                              EPOLL_CTL_DEL,
+                              connection->socket_fd,
+                              NULL))
+            MHD_PANIC ("Failed to remove FD from epoll set\n");
+          connection->epoll_state &= ~MHD_EPOLL_STATE_IN_EPOLL_SET;
+        }
+      connection->epoll_state |= MHD_EPOLL_STATE_SUSPENDED;
+    }
+#endif
+  connection->suspended = MHD_YES;
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to release cleanup mutex\n");
+}
+
+
+/**
+ * Resume handling of network data for suspended connection.  It is
+ * safe to resume a suspended connection at any time.  Calling this function
+ * on a connection that was not previously suspended will result
+ * in undefined behavior.
+ *
+ * @param connection the connection to resume
+ */
+void
+MHD_resume_connection (struct MHD_Connection *connection)
+{
+  struct MHD_Daemon *daemon;
+
+  daemon = connection->daemon;
+  if (MHD_USE_SUSPEND_RESUME != (daemon->options & MHD_USE_SUSPEND_RESUME))
+    MHD_PANIC ("Cannot resume connections without enabling MHD_USE_SUSPEND_RESUME!\n");
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to acquire cleanup mutex\n");
+  connection->resuming = MHD_YES;
+  daemon->resuming = MHD_YES;
+  if ( (MHD_INVALID_PIPE_ != daemon->wpipe[1]) &&
+       (1 != MHD_pipe_write_ (daemon->wpipe[1], "r", 1)) )
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                "failed to signal resume via pipe");
+#endif
+    }
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to release cleanup mutex\n");
+}
+
+
+/**
+ * Run through the suspended connections and move any that are no
+ * longer suspended back to the active state.
+ *
+ * @param daemon daemon context
+ * @return #MHD_YES if a connection was actually resumed
+ */
+static int
+resume_suspended_connections (struct MHD_Daemon *daemon)
+{
+  struct MHD_Connection *pos;
+  struct MHD_Connection *next = NULL;
+  int ret;
+
+  ret = MHD_NO;
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to acquire cleanup mutex\n");
+  if (MHD_YES == daemon->resuming)
+    next = daemon->suspended_connections_head;
+
+  while (NULL != (pos = next))
+    {
+      next = pos->next;
+      if (MHD_NO == pos->resuming)
+        continue;
+      ret = MHD_YES;
+      DLL_remove (daemon->suspended_connections_head,
+                  daemon->suspended_connections_tail,
+                  pos);
+      DLL_insert (daemon->connections_head,
+                  daemon->connections_tail,
+                  pos);
+      if (pos->connection_timeout == daemon->connection_timeout)
+        XDLL_insert (daemon->normal_timeout_head,
+                     daemon->normal_timeout_tail,
+                     pos);
+      else
+        XDLL_insert (daemon->manual_timeout_head,
+                     daemon->manual_timeout_tail,
+                     pos);
+#if EPOLL_SUPPORT
+      if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
+        {
+          if (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
+            MHD_PANIC ("Resumed connection was already in EREADY set\n");
+          /* we always mark resumed connections as ready, as we
+             might have missed the edge poll event during suspension */
+          EDLL_insert (daemon->eready_head,
+                       daemon->eready_tail,
+                       pos);
+          pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
+          pos->epoll_state &= ~MHD_EPOLL_STATE_SUSPENDED;
+        }
+#endif
+      pos->suspended = MHD_NO;
+      pos->resuming = MHD_NO;
+    }
+  daemon->resuming = MHD_NO;
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to release cleanup mutex\n");
+  return ret;
+}
+
+
+/**
+ * Change socket options to be non-blocking, non-inheritable.
+ *
+ * @param daemon daemon context
+ * @param sock socket to manipulate
+ */
+static void
+make_nonblocking_noninheritable (struct MHD_Daemon *daemon,
+				 MHD_socket sock)
+{
+#ifdef WINDOWS
+  DWORD dwFlags;
+  unsigned long flags = 1;
+
+  if (0 != ioctlsocket (sock, FIONBIO, &flags))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"Failed to make socket non-blocking: %s\n",
+		MHD_socket_last_strerr_ ());
+#endif
+    }
+  if (!GetHandleInformation ((HANDLE) sock, &dwFlags) ||
+      ((dwFlags != (dwFlags & ~HANDLE_FLAG_INHERIT)) &&
+       !SetHandleInformation ((HANDLE) sock, HANDLE_FLAG_INHERIT, 0)))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"Failed to make socket non-inheritable: %u\n",
+		(unsigned int) GetLastError ());
+#endif
+    }
+#else
+  int flags;
+  int nonblock;
+
+  nonblock = O_NONBLOCK;
+#ifdef CYGWIN
+  if (0 == (daemon->options & MHD_USE_SSL))
+    nonblock = 0;
+#endif
+  flags = fcntl (sock, F_GETFD);
+  if ( ( (-1 == flags) ||
+	 ( (flags != (flags | FD_CLOEXEC)) &&
+	   (0 != fcntl (sock, F_SETFD, flags | nonblock | FD_CLOEXEC)) ) ) )
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"Failed to make socket non-inheritable: %s\n",
+		MHD_socket_last_strerr_ ());
+#endif
+    }
+#endif
+}
+
+
+/**
+ * Add another client connection to the set of connections managed by
+ * MHD.  This API is usually not needed (since MHD will accept inbound
+ * connections on the server socket).  Use this API in special cases,
+ * for example if your HTTP server is behind NAT and needs to connect
+ * out to the HTTP client, or if you are building a proxy.
+ *
+ * If you use this API in conjunction with a internal select or a
+ * thread pool, you must set the option
+ * #MHD_USE_PIPE_FOR_SHUTDOWN to ensure that the freshly added
+ * connection is immediately processed by MHD.
+ *
+ * The given client socket will be managed (and closed!) by MHD after
+ * this call and must no longer be used directly by the application
+ * afterwards.
+ *
+ * Per-IP connection limits are ignored when using this API.
+ *
+ * @param daemon daemon that manages the connection
+ * @param client_socket socket to manage (MHD will expect
+ *        to receive an HTTP request from this socket next).
+ * @param addr IP address of the client
+ * @param addrlen number of bytes in @a addr
+ * @return #MHD_YES on success, #MHD_NO if this daemon could
+ *        not handle the connection (i.e. malloc() failed, etc).
+ *        The socket will be closed in any case; `errno` is
+ *        set to indicate further details about the error.
+ * @ingroup specialized
+ */
+int
+MHD_add_connection (struct MHD_Daemon *daemon,
+		    MHD_socket client_socket,
+		    const struct sockaddr *addr,
+		    socklen_t addrlen)
+{
+  make_nonblocking_noninheritable (daemon,
+				   client_socket);
+  return internal_add_connection (daemon,
+				  client_socket,
+				  addr, addrlen,
+				  MHD_YES);
+}
+
+
+/**
+ * Accept an incoming connection and create the MHD_Connection object for
+ * it.  This function also enforces policy by way of checking with the
+ * accept policy callback.
+ *
+ * @param daemon handle with the listen socket
+ * @return #MHD_YES on success (connections denied by policy or due
+ *         to 'out of memory' and similar errors) are still considered
+ *         successful as far as #MHD_accept_connection() is concerned);
+ *         a return code of #MHD_NO only refers to the actual
+ *         accept() system call.
+ */
+static int
+MHD_accept_connection (struct MHD_Daemon *daemon)
+{
+#if HAVE_INET6
+  struct sockaddr_in6 addrstorage;
+#else
+  struct sockaddr_in addrstorage;
+#endif
+  struct sockaddr *addr = (struct sockaddr *) &addrstorage;
+  socklen_t addrlen;
+  MHD_socket s;
+  MHD_socket fd;
+  int nonblock;
+
+  addrlen = sizeof (addrstorage);
+  memset (addr, 0, sizeof (addrstorage));
+  if (MHD_INVALID_SOCKET == (fd = daemon->socket_fd))
+    return MHD_NO;
+#ifdef HAVE_SOCK_NONBLOCK
+  nonblock = SOCK_NONBLOCK;
+#else
+  nonblock = 0;
+#endif
+#ifdef CYGWIN
+  if (0 == (daemon->options & MHD_USE_SSL))
+    nonblock = 0;
+#endif
+#if HAVE_ACCEPT4
+  s = accept4 (fd, addr, &addrlen, SOCK_CLOEXEC | nonblock);
+#else
+  s = accept (fd, addr, &addrlen);
+#endif
+  if ((MHD_INVALID_SOCKET == s) || (addrlen <= 0))
+    {
+#if HAVE_MESSAGES
+      const int err = MHD_socket_errno_;
+      /* This could be a common occurance with multiple worker threads */
+      if ( (EINVAL == err) &&
+           (MHD_INVALID_SOCKET == daemon->socket_fd) )
+        return MHD_NO; /* can happen during shutdown */
+      if ((EAGAIN != err) && (EWOULDBLOCK != err))
+        MHD_DLOG (daemon,
+		  "Error accepting connection: %s\n",
+		  MHD_socket_last_strerr_ ());
+#endif
+      if (MHD_INVALID_SOCKET != s)
+        {
+          if (0 != MHD_socket_close_ (s))
+	    MHD_PANIC ("close failed\n");
+          /* just in case */
+        }
+      return MHD_NO;
+    }
+#if !defined(HAVE_ACCEPT4) || HAVE_ACCEPT4+0 == 0 || !defined(HAVE_SOCK_NONBLOCK) || SOCK_CLOEXEC+0 == 0
+  make_nonblocking_noninheritable (daemon, s);
+#endif
+#if HAVE_MESSAGES
+#if DEBUG_CONNECT
+  MHD_DLOG (daemon,
+            "Accepted connection on socket %d\n",
+            s);
+#endif
+#endif
+  (void) internal_add_connection (daemon, s,
+				  addr, addrlen,
+				  MHD_NO);
+  return MHD_YES;
+}
+
+
+/**
+ * Free resources associated with all closed connections.
+ * (destroy responses, free buffers, etc.).  All closed
+ * connections are kept in the "cleanup" doubly-linked list.
+ *
+ * @param daemon daemon to clean up
+ */
+static void
+MHD_cleanup_connections (struct MHD_Daemon *daemon)
+{
+  struct MHD_Connection *pos;
+
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to acquire cleanup mutex\n");
+  while (NULL != (pos = daemon->cleanup_head))
+    {
+      DLL_remove (daemon->cleanup_head,
+		  daemon->cleanup_tail,
+		  pos);
+      if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+	   (MHD_NO == pos->thread_joined) )
+	{
+	  if (0 != MHD_join_thread_ (pos->pid))
+	    {
+	      MHD_PANIC ("Failed to join a thread\n");
+	    }
+	}
+      MHD_pool_destroy (pos->pool);
+#if HTTPS_SUPPORT
+      if (NULL != pos->tls_session)
+	gnutls_deinit (pos->tls_session);
+#endif
+      if (NULL != daemon->notify_connection)
+        daemon->notify_connection (daemon->notify_connection_cls,
+                                   pos,
+                                   &pos->socket_context,
+                                   MHD_CONNECTION_NOTIFY_CLOSED);
+      MHD_ip_limit_del (daemon, pos->addr, pos->addr_len);
+#if EPOLL_SUPPORT
+      if (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
+	{
+	  EDLL_remove (daemon->eready_head,
+		       daemon->eready_tail,
+		       pos);
+	  pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL;
+	}
+      if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
+	   (MHD_INVALID_SOCKET != daemon->epoll_fd) &&
+	   (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) )
+	{
+	  /* epoll documentation suggests that closing a FD
+	     automatically removes it from the epoll set; however,
+	     this is not true as if we fail to do manually remove it,
+	     we are still seeing an event for this fd in epoll,
+	     causing grief (use-after-free...) --- at least on my
+	     system. */
+	  if (0 != epoll_ctl (daemon->epoll_fd,
+			      EPOLL_CTL_DEL,
+			      pos->socket_fd,
+			      NULL))
+	    MHD_PANIC ("Failed to remove FD from epoll set\n");
+	  pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EPOLL_SET;
+	}
+#endif
+      if (NULL != pos->response)
+	{
+	  MHD_destroy_response (pos->response);
+	  pos->response = NULL;
+	}
+      if (MHD_INVALID_SOCKET != pos->socket_fd)
+	{
+#ifdef WINDOWS
+	  shutdown (pos->socket_fd, SHUT_WR);
+#endif
+	  if (0 != MHD_socket_close_ (pos->socket_fd))
+	    MHD_PANIC ("close failed\n");
+	}
+      if (NULL != pos->addr)
+	free (pos->addr);
+      free (pos);
+      daemon->connections--;
+    }
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to release cleanup mutex\n");
+}
+
+
+/**
+ * Obtain timeout value for `select()` for this daemon (only needed if
+ * connection timeout is used).  The returned value is how long
+ * `select()` or `poll()` should at most block, not the timeout value set
+ * for connections.  This function MUST NOT be called if MHD is
+ * running with #MHD_USE_THREAD_PER_CONNECTION.
+ *
+ * @param daemon daemon to query for timeout
+ * @param timeout set to the timeout (in milliseconds)
+ * @return #MHD_YES on success, #MHD_NO if timeouts are
+ *        not used (or no connections exist that would
+ *        necessiate the use of a timeout right now).
+ * @ingroup event
+ */
+int
+MHD_get_timeout (struct MHD_Daemon *daemon,
+		 MHD_UNSIGNED_LONG_LONG *timeout)
+{
+  time_t earliest_deadline;
+  time_t now;
+  struct MHD_Connection *pos;
+  int have_timeout;
+
+  if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                "Illegal call to MHD_get_timeout\n");
+#endif
+      return MHD_NO;
+    }
+
+#if HTTPS_SUPPORT
+  if (0 != daemon->num_tls_read_ready)
+    {
+      /* if there is any TLS connection with data ready for
+	 reading, we must not block in the event loop */
+      *timeout = 0;
+      return MHD_YES;
+    }
+#endif
+
+  have_timeout = MHD_NO;
+  earliest_deadline = 0; /* avoid compiler warnings */
+  for (pos = daemon->manual_timeout_head; NULL != pos; pos = pos->nextX)
+    {
+      if (0 != pos->connection_timeout)
+	{
+	  if ( (! have_timeout) ||
+	       (earliest_deadline > pos->last_activity + pos->connection_timeout) )
+	    earliest_deadline = pos->last_activity + pos->connection_timeout;
+#if HTTPS_SUPPORT
+	  if (  (0 != (daemon->options & MHD_USE_SSL)) &&
+		(0 != gnutls_record_check_pending (pos->tls_session)) )
+	    earliest_deadline = 0;
+#endif
+	  have_timeout = MHD_YES;
+	}
+    }
+  /* normal timeouts are sorted, so we only need to look at the 'head' */
+  pos = daemon->normal_timeout_head;
+  if ( (NULL != pos) &&
+       (0 != pos->connection_timeout) )
+    {
+      if ( (! have_timeout) ||
+	   (earliest_deadline > pos->last_activity + pos->connection_timeout) )
+	earliest_deadline = pos->last_activity + pos->connection_timeout;
+#if HTTPS_SUPPORT
+      if (  (0 != (daemon->options & MHD_USE_SSL)) &&
+	    (0 != gnutls_record_check_pending (pos->tls_session)) )
+	earliest_deadline = 0;
+#endif
+      have_timeout = MHD_YES;
+    }
+
+  if (MHD_NO == have_timeout)
+    return MHD_NO;
+  now = MHD_monotonic_time();
+  if (earliest_deadline < now)
+    *timeout = 0;
+  else
+    *timeout = 1000 * (1 + earliest_deadline - now);
+  return MHD_YES;
+}
+
+
+/**
+ * Run webserver operations. This method should be called by clients
+ * in combination with #MHD_get_fdset if the client-controlled select
+ * method is used.
+ *
+ * You can use this function instead of #MHD_run if you called
+ * `select()` on the result from #MHD_get_fdset.  File descriptors in
+ * the sets that are not controlled by MHD will be ignored.  Calling
+ * this function instead of #MHD_run is more efficient as MHD will
+ * not have to call `select()` again to determine which operations are
+ * ready.
+ *
+ * @param daemon daemon to run select loop for
+ * @param read_fd_set read set
+ * @param write_fd_set write set
+ * @param except_fd_set except set (not used, can be NULL)
+ * @return #MHD_NO on serious errors, #MHD_YES on success
+ * @ingroup event
+ */
+int
+MHD_run_from_select (struct MHD_Daemon *daemon,
+		     const fd_set *read_fd_set,
+		     const fd_set *write_fd_set,
+		     const fd_set *except_fd_set)
+{
+  MHD_socket ds;
+  char tmp;
+  struct MHD_Connection *pos;
+  struct MHD_Connection *next;
+
+#if EPOLL_SUPPORT
+  if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
+    {
+      /* we're in epoll mode, the epoll FD stands for
+	 the entire event set! */
+      if (daemon->epoll_fd >= FD_SETSIZE)
+	return MHD_NO; /* poll fd too big, fail hard */
+      if (FD_ISSET (daemon->epoll_fd, read_fd_set))
+	return MHD_run (daemon);
+      return MHD_YES;
+    }
+#endif
+
+  /* select connection thread handling type */
+  if ( (MHD_INVALID_SOCKET != (ds = daemon->socket_fd)) &&
+       (FD_ISSET (ds, read_fd_set)) )
+    (void) MHD_accept_connection (daemon);
+  /* drain signaling pipe to avoid spinning select */
+  if ( (MHD_INVALID_PIPE_ != daemon->wpipe[0]) &&
+       (FD_ISSET (daemon->wpipe[0], read_fd_set)) )
+    (void) MHD_pipe_read_ (daemon->wpipe[0], &tmp, sizeof (tmp));
+
+  if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+    {
+      /* do not have a thread per connection, process all connections now */
+      next = daemon->connections_head;
+      while (NULL != (pos = next))
+        {
+	  next = pos->next;
+          ds = pos->socket_fd;
+          if (MHD_INVALID_SOCKET == ds)
+	    continue;
+	  switch (pos->event_loop_info)
+	    {
+	    case MHD_EVENT_LOOP_INFO_READ:
+	      if ( (FD_ISSET (ds, read_fd_set))
+#if HTTPS_SUPPORT
+		   || (MHD_YES == pos->tls_read_ready)
+#endif
+		   )
+		pos->read_handler (pos);
+	      break;
+	    case MHD_EVENT_LOOP_INFO_WRITE:
+	      if ( (FD_ISSET (ds, read_fd_set)) &&
+		   (pos->read_buffer_size > pos->read_buffer_offset) )
+		pos->read_handler (pos);
+	      if (FD_ISSET (ds, write_fd_set))
+		pos->write_handler (pos);
+	      break;
+	    case MHD_EVENT_LOOP_INFO_BLOCK:
+	      if ( (FD_ISSET (ds, read_fd_set)) &&
+		   (pos->read_buffer_size > pos->read_buffer_offset) )
+		pos->read_handler (pos);
+	      break;
+	    case MHD_EVENT_LOOP_INFO_CLEANUP:
+	      /* should never happen */
+	      break;
+	    }
+	  pos->idle_handler (pos);
+        }
+    }
+  MHD_cleanup_connections (daemon);
+  return MHD_YES;
+}
+
+
+/**
+ * Main internal select() call.  Will compute select sets, call select()
+ * and then #MHD_run_from_select with the result.
+ *
+ * @param daemon daemon to run select() loop for
+ * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking
+ * @return #MHD_NO on serious errors, #MHD_YES on success
+ */
+static int
+MHD_select (struct MHD_Daemon *daemon,
+	    int may_block)
+{
+  int num_ready;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  struct timeval timeout;
+  struct timeval *tv;
+  MHD_UNSIGNED_LONG_LONG ltimeout;
+
+  timeout.tv_sec = 0;
+  timeout.tv_usec = 0;
+  if (MHD_YES == daemon->shutdown)
+    return MHD_NO;
+  FD_ZERO (&rs);
+  FD_ZERO (&ws);
+  FD_ZERO (&es);
+  max = MHD_INVALID_SOCKET;
+  if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+    {
+      if ( (MHD_USE_SUSPEND_RESUME == (daemon->options & MHD_USE_SUSPEND_RESUME)) &&
+           (MHD_YES == resume_suspended_connections (daemon)) )
+        may_block = MHD_NO;
+
+      /* single-threaded, go over everything */
+      if (MHD_NO == MHD_get_fdset2 (daemon, &rs, &ws, &es, &max, FD_SETSIZE))
+        return MHD_NO;
+
+      /* If we're at the connection limit, no need to
+         accept new connections; however, make sure
+         we do not miss the shutdown, so only do this
+         optimization if we have a shutdown signaling
+         pipe. */
+      if ( (MHD_INVALID_SOCKET != daemon->socket_fd) &&
+           (daemon->connections == daemon->connection_limit) &&
+           (0 != (daemon->options & MHD_USE_PIPE_FOR_SHUTDOWN)) )
+        FD_CLR (daemon->socket_fd, &rs);
+    }
+  else
+    {
+      /* accept only, have one thread per connection */
+      if ( (MHD_INVALID_SOCKET != daemon->socket_fd) &&
+           (MHD_YES != add_to_fd_set (daemon->socket_fd,
+                                      &rs,
+                                      &max,
+                                      FD_SETSIZE)) )
+        return MHD_NO;
+    }
+  if ( (MHD_INVALID_PIPE_ != daemon->wpipe[0]) &&
+       (MHD_YES != add_to_fd_set (daemon->wpipe[0],
+                                  &rs,
+                                  &max,
+                                  FD_SETSIZE)) )
+    return MHD_NO;
+
+  tv = NULL;
+  if (MHD_NO == may_block)
+    {
+      timeout.tv_usec = 0;
+      timeout.tv_sec = 0;
+      tv = &timeout;
+    }
+  else if ( (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+	    (MHD_YES == MHD_get_timeout (daemon, &ltimeout)) )
+    {
+      /* ltimeout is in ms */
+      timeout.tv_usec = (ltimeout % 1000) * 1000;
+      timeout.tv_sec = ltimeout / 1000;
+      tv = &timeout;
+    }
+  if (MHD_INVALID_SOCKET == max)
+    return MHD_YES;
+  num_ready = MHD_SYS_select_ (max + 1, &rs, &ws, &es, tv);
+  if (MHD_YES == daemon->shutdown)
+    return MHD_NO;
+  if (num_ready < 0)
+    {
+      if (EINTR == MHD_socket_errno_)
+        return MHD_YES;
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                "select failed: %s\n",
+                MHD_socket_last_strerr_ ());
+#endif
+      return MHD_NO;
+    }
+  return MHD_run_from_select (daemon, &rs, &ws, &es);
+}
+
+
+#ifdef HAVE_POLL
+/**
+ * Process all of our connections and possibly the server
+ * socket using poll().
+ *
+ * @param daemon daemon to run poll loop for
+ * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking
+ * @return #MHD_NO on serious errors, #MHD_YES on success
+ */
+static int
+MHD_poll_all (struct MHD_Daemon *daemon,
+	      int may_block)
+{
+  unsigned int num_connections;
+  struct MHD_Connection *pos;
+  struct MHD_Connection *next;
+
+  if ( (MHD_USE_SUSPEND_RESUME == (daemon->options & MHD_USE_SUSPEND_RESUME)) &&
+       (MHD_YES == resume_suspended_connections (daemon)) )
+    may_block = MHD_NO;
+
+  /* count number of connections and thus determine poll set size */
+  num_connections = 0;
+  for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
+    num_connections++;
+  {
+    MHD_UNSIGNED_LONG_LONG ltimeout;
+    unsigned int i;
+    int timeout;
+    unsigned int poll_server;
+    int poll_listen;
+    int poll_pipe;
+    char tmp;
+    struct pollfd *p;
+
+    p = malloc(sizeof (struct pollfd) * (2 + num_connections));
+    if (NULL == p)
+      {
+#if HAVE_MESSAGES
+        MHD_DLOG(daemon,
+                 "Error allocating memory: %s\n",
+                 MHD_strerror_(errno));
+#endif
+        return MHD_NO;
+      }
+    memset (p, 0, sizeof (struct pollfd) * (2 + num_connections));
+    poll_server = 0;
+    poll_listen = -1;
+    if ( (MHD_INVALID_SOCKET != daemon->socket_fd) &&
+	 (daemon->connections < daemon->connection_limit) )
+      {
+	/* only listen if we are not at the connection limit */
+	p[poll_server].fd = daemon->socket_fd;
+	p[poll_server].events = POLLIN;
+	p[poll_server].revents = 0;
+	poll_listen = (int) poll_server;
+	poll_server++;
+      }
+    poll_pipe = -1;
+    if (MHD_INVALID_PIPE_ != daemon->wpipe[0])
+      {
+	p[poll_server].fd = daemon->wpipe[0];
+	p[poll_server].events = POLLIN;
+	p[poll_server].revents = 0;
+        poll_pipe = (int) poll_server;
+	poll_server++;
+      }
+    if (may_block == MHD_NO)
+      timeout = 0;
+    else if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
+	      (MHD_YES != MHD_get_timeout (daemon, &ltimeout)) )
+      timeout = -1;
+    else
+      timeout = (ltimeout > INT_MAX) ? INT_MAX : (int) ltimeout;
+
+    i = 0;
+    for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
+      {
+	p[poll_server+i].fd = pos->socket_fd;
+	switch (pos->event_loop_info)
+	  {
+	  case MHD_EVENT_LOOP_INFO_READ:
+	    p[poll_server+i].events |= POLLIN;
+	    break;
+	  case MHD_EVENT_LOOP_INFO_WRITE:
+	    p[poll_server+i].events |= POLLOUT;
+	    if (pos->read_buffer_size > pos->read_buffer_offset)
+	      p[poll_server+i].events |= POLLIN;
+	    break;
+	  case MHD_EVENT_LOOP_INFO_BLOCK:
+	    if (pos->read_buffer_size > pos->read_buffer_offset)
+	      p[poll_server+i].events |= POLLIN;
+	    break;
+	  case MHD_EVENT_LOOP_INFO_CLEANUP:
+	    timeout = 0; /* clean up "pos" immediately */
+	    break;
+	  }
+	i++;
+      }
+    if (0 == poll_server + num_connections)
+      {
+        free(p);
+        return MHD_YES;
+      }
+    if (MHD_sys_poll_(p, poll_server + num_connections, timeout) < 0)
+      {
+	if (EINTR == MHD_socket_errno_)
+      {
+        free(p);
+        return MHD_YES;
+      }
+#if HAVE_MESSAGES
+	MHD_DLOG (daemon,
+		  "poll failed: %s\n",
+		  MHD_socket_last_strerr_ ());
+#endif
+        free(p);
+	return MHD_NO;
+      }
+    /* handle shutdown */
+    if (MHD_YES == daemon->shutdown)
+      {
+        free(p);
+        return MHD_NO;
+      }
+    i = 0;
+    next = daemon->connections_head;
+    while (NULL != (pos = next))
+      {
+	next = pos->next;
+	switch (pos->event_loop_info)
+	  {
+	  case MHD_EVENT_LOOP_INFO_READ:
+	    /* first, sanity checks */
+	    if (i >= num_connections)
+	      break; /* connection list changed somehow, retry later ... */
+	    if (p[poll_server+i].fd != pos->socket_fd)
+	      break; /* fd mismatch, something else happened, retry later ... */
+	    /* normal handling */
+	    if (0 != (p[poll_server+i].revents & POLLIN))
+	      pos->read_handler (pos);
+	    pos->idle_handler (pos);
+	    i++;
+	    break;
+	  case MHD_EVENT_LOOP_INFO_WRITE:
+	    /* first, sanity checks */
+	    if (i >= num_connections)
+	      break; /* connection list changed somehow, retry later ... */
+	    if (p[poll_server+i].fd != pos->socket_fd)
+	      break; /* fd mismatch, something else happened, retry later ... */
+	    /* normal handling */
+	    if (0 != (p[poll_server+i].revents & POLLIN))
+	      pos->read_handler (pos);
+	    if (0 != (p[poll_server+i].revents & POLLOUT))
+	      pos->write_handler (pos);
+	    pos->idle_handler (pos);
+	    i++;
+	    break;
+	  case MHD_EVENT_LOOP_INFO_BLOCK:
+	    if (0 != (p[poll_server+i].revents & POLLIN))
+	      pos->read_handler (pos);
+	    pos->idle_handler (pos);
+	    break;
+	  case MHD_EVENT_LOOP_INFO_CLEANUP:
+	    pos->idle_handler (pos);
+	    break;
+	  }
+      }
+    /* handle 'listen' FD */
+    if ( (-1 != poll_listen) &&
+	 (0 != (p[poll_listen].revents & POLLIN)) )
+      (void) MHD_accept_connection (daemon);
+
+    /* handle pipe FD */
+    if ( (-1 != poll_pipe) &&
+         (0 != (p[poll_pipe].revents & POLLIN)) )
+      (void) MHD_pipe_read_ (daemon->wpipe[0], &tmp, sizeof (tmp));
+
+    free(p);
+  }
+  return MHD_YES;
+}
+
+
+/**
+ * Process only the listen socket using poll().
+ *
+ * @param daemon daemon to run poll loop for
+ * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking
+ * @return #MHD_NO on serious errors, #MHD_YES on success
+ */
+static int
+MHD_poll_listen_socket (struct MHD_Daemon *daemon,
+			int may_block)
+{
+  struct pollfd p[2];
+  int timeout;
+  unsigned int poll_count;
+  int poll_listen;
+
+  memset (&p, 0, sizeof (p));
+  poll_count = 0;
+  poll_listen = -1;
+  if (MHD_INVALID_SOCKET != daemon->socket_fd)
+    {
+      p[poll_count].fd = daemon->socket_fd;
+      p[poll_count].events = POLLIN;
+      p[poll_count].revents = 0;
+      poll_listen = poll_count;
+      poll_count++;
+    }
+  if (MHD_INVALID_PIPE_ != daemon->wpipe[0])
+    {
+      p[poll_count].fd = daemon->wpipe[0];
+      p[poll_count].events = POLLIN;
+      p[poll_count].revents = 0;
+      poll_count++;
+    }
+  if (MHD_NO == may_block)
+    timeout = 0;
+  else
+    timeout = -1;
+  if (0 == poll_count)
+    return MHD_YES;
+  if (MHD_sys_poll_(p, poll_count, timeout) < 0)
+    {
+      if (EINTR == MHD_socket_errno_)
+	return MHD_YES;
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                "poll failed: %s\n",
+                MHD_socket_last_strerr_ ());
+#endif
+      return MHD_NO;
+    }
+  /* handle shutdown */
+  if (MHD_YES == daemon->shutdown)
+    return MHD_NO;
+  if ( (-1 != poll_listen) &&
+       (0 != (p[poll_listen].revents & POLLIN)) )
+    (void) MHD_accept_connection (daemon);
+  return MHD_YES;
+}
+#endif
+
+
+/**
+ * Do poll()-based processing.
+ *
+ * @param daemon daemon to run poll()-loop for
+ * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking
+ * @return #MHD_NO on serious errors, #MHD_YES on success
+ */
+static int
+MHD_poll (struct MHD_Daemon *daemon,
+	  int may_block)
+{
+#ifdef HAVE_POLL
+  if (MHD_YES == daemon->shutdown)
+    return MHD_NO;
+  if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+    return MHD_poll_all (daemon, may_block);
+  else
+    return MHD_poll_listen_socket (daemon, may_block);
+#else
+  return MHD_NO;
+#endif
+}
+
+
+#if EPOLL_SUPPORT
+
+/**
+ * How many events to we process at most per epoll() call?  Trade-off
+ * between required stack-size and number of system calls we have to
+ * make; 128 should be way enough to avoid more than one system call
+ * for most scenarios, and still be moderate in stack size
+ * consumption.  Embedded systems might want to choose a smaller value
+ * --- but why use epoll() on such a system in the first place?
+ */
+#define MAX_EVENTS 128
+
+
+/**
+ * Do epoll()-based processing (this function is allowed to
+ * block if @a may_block is set to #MHD_YES).
+ *
+ * @param daemon daemon to run poll loop for
+ * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking
+ * @return #MHD_NO on serious errors, #MHD_YES on success
+ */
+static int
+MHD_epoll (struct MHD_Daemon *daemon,
+	   int may_block)
+{
+  struct MHD_Connection *pos;
+  struct MHD_Connection *next;
+  struct epoll_event events[MAX_EVENTS];
+  struct epoll_event event;
+  int timeout_ms;
+  MHD_UNSIGNED_LONG_LONG timeout_ll;
+  int num_events;
+  unsigned int i;
+  unsigned int series_length;
+  char tmp;
+
+  if (-1 == daemon->epoll_fd)
+    return MHD_NO; /* we're down! */
+  if (MHD_YES == daemon->shutdown)
+    return MHD_NO;
+  if ( (MHD_INVALID_SOCKET != daemon->socket_fd) &&
+       (daemon->connections < daemon->connection_limit) &&
+       (MHD_NO == daemon->listen_socket_in_epoll) )
+    {
+      event.events = EPOLLIN;
+      event.data.ptr = daemon;
+      if (0 != epoll_ctl (daemon->epoll_fd,
+			  EPOLL_CTL_ADD,
+			  daemon->socket_fd,
+			  &event))
+	{
+#if HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "Call to epoll_ctl failed: %s\n",
+                    MHD_socket_last_strerr_ ());
+#endif
+	  return MHD_NO;
+	}
+      daemon->listen_socket_in_epoll = MHD_YES;
+    }
+  if ( (MHD_YES == daemon->listen_socket_in_epoll) &&
+       (daemon->connections == daemon->connection_limit) )
+    {
+      /* we're at the connection limit, disable listen socket
+	 for event loop for now */
+      if (0 != epoll_ctl (daemon->epoll_fd,
+			  EPOLL_CTL_DEL,
+			  daemon->socket_fd,
+			  NULL))
+	MHD_PANIC ("Failed to remove listen FD from epoll set\n");
+      daemon->listen_socket_in_epoll = MHD_NO;
+    }
+  if (MHD_YES == may_block)
+    {
+      if (MHD_YES == MHD_get_timeout (daemon,
+				      &timeout_ll))
+	{
+	  if (timeout_ll >= (MHD_UNSIGNED_LONG_LONG) INT_MAX)
+	    timeout_ms = INT_MAX;
+	  else
+	    timeout_ms = (int) timeout_ll;
+	}
+      else
+	timeout_ms = -1;
+    }
+  else
+    timeout_ms = 0;
+
+  /* drain 'epoll' event queue; need to iterate as we get at most
+     MAX_EVENTS in one system call here; in practice this should
+     pretty much mean only one round, but better an extra loop here
+     than unfair behavior... */
+  num_events = MAX_EVENTS;
+  while (MAX_EVENTS == num_events)
+    {
+      /* update event masks */
+      num_events = epoll_wait (daemon->epoll_fd,
+			       events, MAX_EVENTS, timeout_ms);
+      if (-1 == num_events)
+	{
+	  if (EINTR == MHD_socket_errno_)
+	    return MHD_YES;
+#if HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "Call to epoll_wait failed: %s\n",
+                    MHD_socket_last_strerr_ ());
+#endif
+	  return MHD_NO;
+	}
+      for (i=0;i<(unsigned int) num_events;i++)
+	{
+	  if (NULL == events[i].data.ptr)
+	    continue; /* shutdown signal! */
+          if ( (MHD_INVALID_PIPE_ != daemon->wpipe[0]) &&
+               (daemon->wpipe[0] == events[i].data.fd) )
+            {
+              (void) MHD_pipe_read_ (daemon->wpipe[0], &tmp, sizeof (tmp));
+              continue;
+            }
+	  if (daemon != events[i].data.ptr)
+	    {
+	      /* this is an event relating to a 'normal' connection,
+		 remember the event and if appropriate mark the
+		 connection as 'eready'. */
+	      pos = events[i].data.ptr;
+	      if (0 != (events[i].events & EPOLLIN))
+		{
+		  pos->epoll_state |= MHD_EPOLL_STATE_READ_READY;
+		  if ( ( (MHD_EVENT_LOOP_INFO_READ == pos->event_loop_info) ||
+			 (pos->read_buffer_size > pos->read_buffer_offset) ) &&
+		       (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) )
+		    {
+		      EDLL_insert (daemon->eready_head,
+				   daemon->eready_tail,
+				   pos);
+		      pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
+		    }
+		}
+	      if (0 != (events[i].events & EPOLLOUT))
+		{
+		  pos->epoll_state |= MHD_EPOLL_STATE_WRITE_READY;
+		  if ( (MHD_EVENT_LOOP_INFO_WRITE == pos->event_loop_info) &&
+		       (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) )
+		    {
+		      EDLL_insert (daemon->eready_head,
+				   daemon->eready_tail,
+				   pos);
+		      pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
+		    }
+		}
+	    }
+	  else /* must be listen socket */
+	    {
+	      /* run 'accept' until it fails or we are not allowed to take
+		 on more connections */
+	      series_length = 0;
+	      while ( (MHD_YES == MHD_accept_connection (daemon)) &&
+		      (daemon->connections < daemon->connection_limit) &&
+		      (series_length < 128) )
+                series_length++;
+	    }
+	}
+    }
+
+  /* we handle resumes here because we may have ready connections
+     that will not be placed into the epoll list immediately. */
+  if ( (MHD_USE_SUSPEND_RESUME == (daemon->options & MHD_USE_SUSPEND_RESUME)) &&
+       (MHD_YES == resume_suspended_connections (daemon)) )
+    may_block = MHD_NO;
+
+  /* process events for connections */
+  while (NULL != (pos = daemon->eready_tail))
+    {
+      EDLL_remove (daemon->eready_head,
+		   daemon->eready_tail,
+		   pos);
+      pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL;
+      if (MHD_EVENT_LOOP_INFO_READ == pos->event_loop_info)
+	pos->read_handler (pos);
+      if (MHD_EVENT_LOOP_INFO_WRITE == pos->event_loop_info)
+	pos->write_handler (pos);
+      pos->idle_handler (pos);
+    }
+
+  /* Finally, handle timed-out connections; we need to do this here
+     as the epoll mechanism won't call the 'idle_handler' on everything,
+     as the other event loops do.  As timeouts do not get an explicit
+     event, we need to find those connections that might have timed out
+     here.
+
+     Connections with custom timeouts must all be looked at, as we
+     do not bother to sort that (presumably very short) list. */
+  next = daemon->manual_timeout_head;
+  while (NULL != (pos = next))
+    {
+      next = pos->nextX;
+      pos->idle_handler (pos);
+    }
+  /* Connections with the default timeout are sorted by prepending
+     them to the head of the list whenever we touch the connection;
+     thus it sufficies to iterate from the tail until the first
+     connection is NOT timed out */
+  next = daemon->normal_timeout_tail;
+  while (NULL != (pos = next))
+    {
+      next = pos->prevX;
+      pos->idle_handler (pos);
+      if (MHD_CONNECTION_CLOSED != pos->state)
+	break; /* sorted by timeout, no need to visit the rest! */
+    }
+  return MHD_YES;
+}
+#endif
+
+
+/**
+ * Run webserver operations (without blocking unless in client
+ * callbacks).  This method should be called by clients in combination
+ * with #MHD_get_fdset if the client-controlled select method is used.
+ *
+ * This function is a convenience method, which is useful if the
+ * fd_sets from #MHD_get_fdset were not directly passed to `select()`;
+ * with this function, MHD will internally do the appropriate `select()`
+ * call itself again.  While it is always safe to call #MHD_run (in
+ * external select mode), you should call #MHD_run_from_select if
+ * performance is important (as it saves an expensive call to
+ * `select()`).
+ *
+ * @param daemon daemon to run
+ * @return #MHD_YES on success, #MHD_NO if this
+ *         daemon was not started with the right
+ *         options for this call.
+ * @ingroup event
+ */
+int
+MHD_run (struct MHD_Daemon *daemon)
+{
+  if ( (MHD_YES == daemon->shutdown) ||
+       (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
+       (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)) )
+    return MHD_NO;
+  if (0 != (daemon->options & MHD_USE_POLL))
+  {
+    MHD_poll (daemon, MHD_NO);
+    MHD_cleanup_connections (daemon);
+  }
+#if EPOLL_SUPPORT
+  else if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
+  {
+    MHD_epoll (daemon, MHD_NO);
+    MHD_cleanup_connections (daemon);
+  }
+#endif
+  else
+  {
+    MHD_select (daemon, MHD_NO);
+    /* MHD_select does MHD_cleanup_connections already */
+  }
+  return MHD_YES;
+}
+
+
+/**
+ * Thread that runs the select loop until the daemon
+ * is explicitly shut down.
+ *
+ * @param cls 'struct MHD_Deamon' to run select loop in a thread for
+ * @return always 0 (on shutdown)
+ */
+static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
+MHD_select_thread (void *cls)
+{
+  struct MHD_Daemon *daemon = cls;
+
+  while (MHD_YES != daemon->shutdown)
+    {
+      if (0 != (daemon->options & MHD_USE_POLL))
+	MHD_poll (daemon, MHD_YES);
+#if EPOLL_SUPPORT
+      else if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
+	MHD_epoll (daemon, MHD_YES);
+#endif
+      else
+	MHD_select (daemon, MHD_YES);
+      MHD_cleanup_connections (daemon);
+    }
+  return (MHD_THRD_RTRN_TYPE_)0;
+}
+
+
+/**
+ * Process escape sequences ('%HH') Updates val in place; the
+ * result should be UTF-8 encoded and cannot be larger than the input.
+ * The result must also still be 0-terminated.
+ *
+ * @param cls closure (use NULL)
+ * @param connection handle to connection, not used
+ * @param val value to unescape (modified in the process)
+ * @return length of the resulting val (strlen(val) maybe
+ *  shorter afterwards due to elimination of escape sequences)
+ */
+static size_t
+unescape_wrapper (void *cls,
+                  struct MHD_Connection *connection,
+                  char *val)
+{
+  return MHD_http_unescape (val);
+}
+
+
+/**
+ * Start a webserver on the given port.  Variadic version of
+ * #MHD_start_daemon_va.
+ *
+ * @param flags combination of `enum MHD_FLAG` values
+ * @param port port to bind to
+ * @param apc callback to call to check which clients
+ *        will be allowed to connect; you can pass NULL
+ *        in which case connections from any IP will be
+ *        accepted
+ * @param apc_cls extra argument to @a apc
+ * @param dh handler called for all requests (repeatedly)
+ * @param dh_cls extra argument to @a dh
+ * @return NULL on error, handle to daemon on success
+ * @ingroup event
+ */
+struct MHD_Daemon *
+MHD_start_daemon (unsigned int flags,
+                  uint16_t port,
+                  MHD_AcceptPolicyCallback apc,
+                  void *apc_cls,
+                  MHD_AccessHandlerCallback dh, void *dh_cls, ...)
+{
+  struct MHD_Daemon *daemon;
+  va_list ap;
+
+  va_start (ap, dh_cls);
+  daemon = MHD_start_daemon_va (flags, port, apc, apc_cls, dh, dh_cls, ap);
+  va_end (ap);
+  return daemon;
+}
+
+
+/**
+ * Stop accepting connections from the listening socket.  Allows
+ * clients to continue processing, but stops accepting new
+ * connections.  Note that the caller is responsible for closing the
+ * returned socket; however, if MHD is run using threads (anything but
+ * external select mode), it must not be closed until AFTER
+ * #MHD_stop_daemon has been called (as it is theoretically possible
+ * that an existing thread is still using it).
+ *
+ * Note that some thread modes require the caller to have passed
+ * #MHD_USE_PIPE_FOR_SHUTDOWN when using this API.  If this daemon is
+ * in one of those modes and this option was not given to
+ * #MHD_start_daemon, this function will return #MHD_INVALID_SOCKET.
+ *
+ * @param daemon daemon to stop accepting new connections for
+ * @return old listen socket on success, #MHD_INVALID_SOCKET if
+ *         the daemon was already not listening anymore
+ * @ingroup specialized
+ */
+MHD_socket
+MHD_quiesce_daemon (struct MHD_Daemon *daemon)
+{
+  unsigned int i;
+  MHD_socket ret;
+
+  ret = daemon->socket_fd;
+  if (MHD_INVALID_SOCKET == ret)
+    return MHD_INVALID_SOCKET;
+  if ( (MHD_INVALID_PIPE_ == daemon->wpipe[1]) &&
+       (0 != (daemon->options & (MHD_USE_SELECT_INTERNALLY | MHD_USE_THREAD_PER_CONNECTION))) )
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"Using MHD_quiesce_daemon in this mode requires MHD_USE_PIPE_FOR_SHUTDOWN\n");
+#endif
+      return MHD_INVALID_SOCKET;
+    }
+
+  if (NULL != daemon->worker_pool)
+    for (i = 0; i < daemon->worker_pool_size; i++)
+      {
+	daemon->worker_pool[i].socket_fd = MHD_INVALID_SOCKET;
+#if EPOLL_SUPPORT
+	if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
+	     (-1 != daemon->worker_pool[i].epoll_fd) &&
+	     (MHD_YES == daemon->worker_pool[i].listen_socket_in_epoll) )
+	  {
+	    if (0 != epoll_ctl (daemon->worker_pool[i].epoll_fd,
+				EPOLL_CTL_DEL,
+				ret,
+				NULL))
+	      MHD_PANIC ("Failed to remove listen FD from epoll set\n");
+	    daemon->worker_pool[i].listen_socket_in_epoll = MHD_NO;
+	  }
+#endif
+      }
+  daemon->socket_fd = MHD_INVALID_SOCKET;
+#if EPOLL_SUPPORT
+  if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
+       (-1 != daemon->epoll_fd) &&
+       (MHD_YES == daemon->listen_socket_in_epoll) )
+    {
+      if (0 != epoll_ctl (daemon->epoll_fd,
+			  EPOLL_CTL_DEL,
+			  ret,
+			  NULL))
+	MHD_PANIC ("Failed to remove listen FD from epoll set\n");
+      daemon->listen_socket_in_epoll = MHD_NO;
+    }
+#endif
+  return ret;
+}
+
+
+/**
+ * Signature of the MHD custom logger function.
+ *
+ * @param cls closure
+ * @param format format string
+ * @param va arguments to the format string (fprintf-style)
+ */
+typedef void (*VfprintfFunctionPointerType)(void *cls,
+					    const char *format,
+					    va_list va);
+
+
+/**
+ * Parse a list of options given as varargs.
+ *
+ * @param daemon the daemon to initialize
+ * @param servaddr where to store the server's listen address
+ * @param ap the options
+ * @return #MHD_YES on success, #MHD_NO on error
+ */
+static int
+parse_options_va (struct MHD_Daemon *daemon,
+		  const struct sockaddr **servaddr,
+		  va_list ap);
+
+
+/**
+ * Parse a list of options given as varargs.
+ *
+ * @param daemon the daemon to initialize
+ * @param servaddr where to store the server's listen address
+ * @param ... the options
+ * @return #MHD_YES on success, #MHD_NO on error
+ */
+static int
+parse_options (struct MHD_Daemon *daemon,
+	       const struct sockaddr **servaddr,
+	       ...)
+{
+  va_list ap;
+  int ret;
+
+  va_start (ap, servaddr);
+  ret = parse_options_va (daemon, servaddr, ap);
+  va_end (ap);
+  return ret;
+}
+
+
+/**
+ * Parse a list of options given as varargs.
+ *
+ * @param daemon the daemon to initialize
+ * @param servaddr where to store the server's listen address
+ * @param ap the options
+ * @return #MHD_YES on success, #MHD_NO on error
+ */
+static int
+parse_options_va (struct MHD_Daemon *daemon,
+		  const struct sockaddr **servaddr,
+		  va_list ap)
+{
+  enum MHD_OPTION opt;
+  struct MHD_OptionItem *oa;
+  unsigned int i;
+#if HTTPS_SUPPORT
+  int ret;
+  const char *pstr;
+#endif
+
+  while (MHD_OPTION_END != (opt = (enum MHD_OPTION) va_arg (ap, int)))
+    {
+      switch (opt)
+        {
+        case MHD_OPTION_CONNECTION_MEMORY_LIMIT:
+          daemon->pool_size = va_arg (ap, size_t);
+          break;
+        case MHD_OPTION_CONNECTION_MEMORY_INCREMENT:
+          daemon->pool_increment= va_arg (ap, size_t);
+          break;
+        case MHD_OPTION_CONNECTION_LIMIT:
+          daemon->connection_limit = va_arg (ap, unsigned int);
+          break;
+        case MHD_OPTION_CONNECTION_TIMEOUT:
+          daemon->connection_timeout = va_arg (ap, unsigned int);
+          break;
+        case MHD_OPTION_NOTIFY_COMPLETED:
+          daemon->notify_completed =
+            va_arg (ap, MHD_RequestCompletedCallback);
+          daemon->notify_completed_cls = va_arg (ap, void *);
+          break;
+        case MHD_OPTION_NOTIFY_CONNECTION:
+          daemon->notify_connection =
+            va_arg (ap, MHD_NotifyConnectionCallback);
+          daemon->notify_connection_cls = va_arg (ap, void *);
+          break;
+        case MHD_OPTION_PER_IP_CONNECTION_LIMIT:
+          daemon->per_ip_connection_limit = va_arg (ap, unsigned int);
+          break;
+        case MHD_OPTION_SOCK_ADDR:
+          *servaddr = va_arg (ap, const struct sockaddr *);
+          break;
+        case MHD_OPTION_URI_LOG_CALLBACK:
+          daemon->uri_log_callback =
+            va_arg (ap, LogCallback);
+          daemon->uri_log_callback_cls = va_arg (ap, void *);
+          break;
+        case MHD_OPTION_THREAD_POOL_SIZE:
+          daemon->worker_pool_size = va_arg (ap, unsigned int);
+	  if (daemon->worker_pool_size >= (SIZE_MAX / sizeof (struct MHD_Daemon)))
+	    {
+#if HAVE_MESSAGES
+	      MHD_DLOG (daemon,
+			"Specified thread pool size (%u) too big\n",
+			daemon->worker_pool_size);
+#endif
+	      return MHD_NO;
+	    }
+          break;
+#if HTTPS_SUPPORT
+        case MHD_OPTION_HTTPS_MEM_KEY:
+	  if (0 != (daemon->options & MHD_USE_SSL))
+	    daemon->https_mem_key = va_arg (ap, const char *);
+#if HAVE_MESSAGES
+	  else
+	    MHD_DLOG (daemon,
+		      "MHD HTTPS option %d passed to MHD but MHD_USE_SSL not set\n",
+		      opt);
+#endif
+          break;
+        case MHD_OPTION_HTTPS_KEY_PASSWORD:
+	  if (0 != (daemon->options & MHD_USE_SSL))
+	    daemon->https_key_password = va_arg (ap, const char *);
+#if HAVE_MESSAGES
+	  else
+	    MHD_DLOG (daemon,
+		      "MHD HTTPS option %d passed to MHD but MHD_USE_SSL not set\n",
+		      opt);
+#endif
+          break;
+        case MHD_OPTION_HTTPS_MEM_CERT:
+	  if (0 != (daemon->options & MHD_USE_SSL))
+	    daemon->https_mem_cert = va_arg (ap, const char *);
+#if HAVE_MESSAGES
+	  else
+	    MHD_DLOG (daemon,
+		      "MHD HTTPS option %d passed to MHD but MHD_USE_SSL not set\n",
+		      opt);
+#endif
+          break;
+        case MHD_OPTION_HTTPS_MEM_TRUST:
+	  if (0 != (daemon->options & MHD_USE_SSL))
+	    daemon->https_mem_trust = va_arg (ap, const char *);
+#if HAVE_MESSAGES
+	  else
+	    MHD_DLOG (daemon,
+		      "MHD HTTPS option %d passed to MHD but MHD_USE_SSL not set\n",
+		      opt);
+#endif
+          break;
+	case MHD_OPTION_HTTPS_CRED_TYPE:
+	  daemon->cred_type = (gnutls_credentials_type_t) va_arg (ap, int);
+	  break;
+        case MHD_OPTION_HTTPS_MEM_DHPARAMS:
+          if (0 != (daemon->options & MHD_USE_SSL))
+            {
+              const char *arg = va_arg (ap, const char *);
+              gnutls_datum_t dhpar;
+
+              if (gnutls_dh_params_init (&daemon->https_mem_dhparams) < 0)
+                {
+#if HAVE_MESSAGES
+                  MHD_DLOG(daemon,
+                           "Error initializing DH parameters\n");
+#endif
+                  return MHD_NO;
+                }
+              dhpar.data = (unsigned char *) arg;
+              dhpar.size = strlen (arg);
+              if (gnutls_dh_params_import_pkcs3 (daemon->https_mem_dhparams, &dhpar,
+                                                 GNUTLS_X509_FMT_PEM) < 0)
+                {
+#if HAVE_MESSAGES
+                  MHD_DLOG(daemon,
+                           "Bad Diffie-Hellman parameters format\n");
+#endif
+                  gnutls_dh_params_deinit (daemon->https_mem_dhparams);
+                  return MHD_NO;
+                }
+              daemon->have_dhparams = MHD_YES;
+            }
+          else
+            {
+#if HAVE_MESSAGES
+              MHD_DLOG (daemon,
+                        "MHD HTTPS option %d passed to MHD but MHD_USE_SSL not set\n",
+                        opt);
+#endif
+              return MHD_NO;
+            }
+          break;
+        case MHD_OPTION_HTTPS_PRIORITIES:
+	  if (0 != (daemon->options & MHD_USE_SSL))
+	    {
+	      gnutls_priority_deinit (daemon->priority_cache);
+	      ret = gnutls_priority_init (&daemon->priority_cache,
+					  pstr = va_arg (ap, const char*),
+					  NULL);
+	      if (GNUTLS_E_SUCCESS != ret)
+	      {
+#if HAVE_MESSAGES
+		MHD_DLOG (daemon,
+			  "Setting priorities to `%s' failed: %s\n",
+			  pstr,
+			  gnutls_strerror (ret));
+#endif
+		daemon->priority_cache = NULL;
+		return MHD_NO;
+	      }
+	    }
+          break;
+        case MHD_OPTION_HTTPS_CERT_CALLBACK:
+#if GNUTLS_VERSION_MAJOR < 3
+#if HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "MHD_OPTION_HTTPS_CERT_CALLBACK requires building MHD with GnuTLS >= 3.0\n");
+#endif
+          return MHD_NO;
+#else
+          if (0 != (daemon->options & MHD_USE_SSL))
+            daemon->cert_callback = va_arg (ap, gnutls_certificate_retrieve_function2 *);
+          break;
+#endif
+#endif
+#ifdef DAUTH_SUPPORT
+	case MHD_OPTION_DIGEST_AUTH_RANDOM:
+	  daemon->digest_auth_rand_size = va_arg (ap, size_t);
+	  daemon->digest_auth_random = va_arg (ap, const char *);
+	  break;
+	case MHD_OPTION_NONCE_NC_SIZE:
+	  daemon->nonce_nc_size = va_arg (ap, unsigned int);
+	  break;
+#endif
+	case MHD_OPTION_LISTEN_SOCKET:
+	  daemon->socket_fd = va_arg (ap, MHD_socket);
+	  break;
+        case MHD_OPTION_EXTERNAL_LOGGER:
+#if HAVE_MESSAGES
+          daemon->custom_error_log =
+            va_arg (ap, VfprintfFunctionPointerType);
+          daemon->custom_error_log_cls = va_arg (ap, void *);
+#else
+          va_arg (ap, VfprintfFunctionPointerType);
+          va_arg (ap, void *);
+#endif
+          break;
+        case MHD_OPTION_THREAD_STACK_SIZE:
+          daemon->thread_stack_size = va_arg (ap, size_t);
+          break;
+#ifdef TCP_FASTOPEN
+        case MHD_OPTION_TCP_FASTOPEN_QUEUE_SIZE:
+          daemon->fastopen_queue_size = va_arg (ap, unsigned int);
+          break;
+#endif
+	case MHD_OPTION_LISTENING_ADDRESS_REUSE:
+	  daemon->listening_address_reuse = va_arg (ap, unsigned int) ? 1 : -1;
+	  break;
+	case MHD_OPTION_ARRAY:
+	  oa = va_arg (ap, struct MHD_OptionItem*);
+	  i = 0;
+	  while (MHD_OPTION_END != (opt = oa[i].option))
+	    {
+	      switch (opt)
+		{
+		  /* all options taking 'size_t' */
+		case MHD_OPTION_CONNECTION_MEMORY_LIMIT:
+		case MHD_OPTION_CONNECTION_MEMORY_INCREMENT:
+		case MHD_OPTION_THREAD_STACK_SIZE:
+		  if (MHD_YES != parse_options (daemon,
+						servaddr,
+						opt,
+						(size_t) oa[i].value,
+						MHD_OPTION_END))
+		    return MHD_NO;
+		  break;
+		  /* all options taking 'unsigned int' */
+		case MHD_OPTION_NONCE_NC_SIZE:
+		case MHD_OPTION_CONNECTION_LIMIT:
+		case MHD_OPTION_CONNECTION_TIMEOUT:
+		case MHD_OPTION_PER_IP_CONNECTION_LIMIT:
+		case MHD_OPTION_THREAD_POOL_SIZE:
+                case MHD_OPTION_TCP_FASTOPEN_QUEUE_SIZE:
+		case MHD_OPTION_LISTENING_ADDRESS_REUSE:
+		  if (MHD_YES != parse_options (daemon,
+						servaddr,
+						opt,
+						(unsigned int) oa[i].value,
+						MHD_OPTION_END))
+		    return MHD_NO;
+		  break;
+		  /* all options taking 'enum' */
+		case MHD_OPTION_HTTPS_CRED_TYPE:
+		  if (MHD_YES != parse_options (daemon,
+						servaddr,
+						opt,
+						(int) oa[i].value,
+						MHD_OPTION_END))
+		    return MHD_NO;
+		  break;
+                  /* all options taking 'MHD_socket' */
+                case MHD_OPTION_LISTEN_SOCKET:
+                  if (MHD_YES != parse_options (daemon,
+                                                servaddr,
+                                                opt,
+                                                (MHD_socket) oa[i].value,
+                                                MHD_OPTION_END))
+                    return MHD_NO;
+                  break;
+		  /* all options taking one pointer */
+		case MHD_OPTION_SOCK_ADDR:
+		case MHD_OPTION_HTTPS_MEM_KEY:
+		case MHD_OPTION_HTTPS_KEY_PASSWORD:
+		case MHD_OPTION_HTTPS_MEM_CERT:
+		case MHD_OPTION_HTTPS_MEM_TRUST:
+	        case MHD_OPTION_HTTPS_MEM_DHPARAMS:
+		case MHD_OPTION_HTTPS_PRIORITIES:
+		case MHD_OPTION_ARRAY:
+                case MHD_OPTION_HTTPS_CERT_CALLBACK:
+		  if (MHD_YES != parse_options (daemon,
+						servaddr,
+						opt,
+						oa[i].ptr_value,
+						MHD_OPTION_END))
+		    return MHD_NO;
+		  break;
+		  /* all options taking two pointers */
+		case MHD_OPTION_NOTIFY_COMPLETED:
+		case MHD_OPTION_NOTIFY_CONNECTION:
+		case MHD_OPTION_URI_LOG_CALLBACK:
+		case MHD_OPTION_EXTERNAL_LOGGER:
+		case MHD_OPTION_UNESCAPE_CALLBACK:
+		  if (MHD_YES != parse_options (daemon,
+						servaddr,
+						opt,
+						(void *) oa[i].value,
+						oa[i].ptr_value,
+						MHD_OPTION_END))
+		    return MHD_NO;
+		  break;
+		  /* options taking size_t-number followed by pointer */
+		case MHD_OPTION_DIGEST_AUTH_RANDOM:
+		  if (MHD_YES != parse_options (daemon,
+						servaddr,
+						opt,
+						(size_t) oa[i].value,
+						oa[i].ptr_value,
+						MHD_OPTION_END))
+		    return MHD_NO;
+		  break;
+		default:
+		  return MHD_NO;
+		}
+	      i++;
+	    }
+	  break;
+        case MHD_OPTION_UNESCAPE_CALLBACK:
+          daemon->unescape_callback =
+            va_arg (ap, UnescapeCallback);
+          daemon->unescape_callback_cls = va_arg (ap, void *);
+          break;
+        default:
+#if HAVE_MESSAGES
+          if (((opt >= MHD_OPTION_HTTPS_MEM_KEY) &&
+              (opt <= MHD_OPTION_HTTPS_PRIORITIES)) || (opt == MHD_OPTION_HTTPS_MEM_TRUST))
+            {
+              MHD_DLOG (daemon,
+			"MHD HTTPS option %d passed to MHD compiled without HTTPS support\n",
+			opt);
+            }
+          else
+            {
+              MHD_DLOG (daemon,
+			"Invalid option %d! (Did you terminate the list with MHD_OPTION_END?)\n",
+			opt);
+            }
+#endif
+	  return MHD_NO;
+        }
+    }
+  return MHD_YES;
+}
+
+
+/**
+ * Create a listen socket, if possible with SOCK_CLOEXEC flag set.
+ *
+ * @param daemon daemon for which we create the socket
+ * @param domain socket domain (i.e. PF_INET)
+ * @param type socket type (usually SOCK_STREAM)
+ * @param protocol desired protocol, 0 for default
+ */
+static MHD_socket
+create_socket (struct MHD_Daemon *daemon,
+	       int domain, int type, int protocol)
+{
+  int ctype = type | SOCK_CLOEXEC;
+  MHD_socket fd;
+
+  /* use SOCK_STREAM rather than ai_socktype: some getaddrinfo
+   * implementations do not set ai_socktype, e.g. RHL6.2. */
+  fd = socket (domain, ctype, protocol);
+  if ( (MHD_INVALID_SOCKET == fd) && (EINVAL == MHD_socket_errno_) && (0 != SOCK_CLOEXEC) )
+  {
+    ctype = type;
+    fd = socket(domain, type, protocol);
+  }
+  if (MHD_INVALID_SOCKET == fd)
+    return MHD_INVALID_SOCKET;
+  if (type == ctype)
+    make_nonblocking_noninheritable (daemon, fd);
+  return fd;
+}
+
+
+#if EPOLL_SUPPORT
+/**
+ * Setup epoll() FD for the daemon and initialize it to listen
+ * on the listen FD.
+ *
+ * @param daemon daemon to initialize for epoll()
+ * @return #MHD_YES on success, #MHD_NO on failure
+ */
+static int
+setup_epoll_to_listen (struct MHD_Daemon *daemon)
+{
+  struct epoll_event event;
+
+#ifdef HAVE_EPOLL_CREATE1
+  daemon->epoll_fd = epoll_create1 (EPOLL_CLOEXEC);
+#else  /* !HAVE_EPOLL_CREATE1 */
+  daemon->epoll_fd = epoll_create (MAX_EVENTS);
+#endif /* !HAVE_EPOLL_CREATE1 */
+  if (-1 == daemon->epoll_fd)
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                "Call to epoll_create1 failed: %s\n",
+                MHD_socket_last_strerr_ ());
+#endif
+      return MHD_NO;
+    }
+#ifndef HAVE_EPOLL_CREATE1
+  else
+    {
+      int fdflags = fcntl (daemon->epoll_fd, F_GETFD);
+      if (0 > fdflags || 0 > fcntl (daemon->epoll_fd, F_SETFD, fdflags | FD_CLOEXEC))
+        {
+#if HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "Failed to change flags on epoll fd: %s\n",
+                    MHD_socket_last_strerr_ ());
+#endif /* HAVE_MESSAGES */
+        }
+    }
+#endif /* !HAVE_EPOLL_CREATE1 */
+  if (0 == EPOLL_CLOEXEC)
+    make_nonblocking_noninheritable (daemon,
+				     daemon->epoll_fd);
+  if (MHD_INVALID_SOCKET == daemon->socket_fd)
+    return MHD_YES; /* non-listening daemon */
+  event.events = EPOLLIN;
+  event.data.ptr = daemon;
+  if (0 != epoll_ctl (daemon->epoll_fd,
+		      EPOLL_CTL_ADD,
+		      daemon->socket_fd,
+		      &event))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                "Call to epoll_ctl failed: %s\n",
+                MHD_socket_last_strerr_ ());
+#endif
+      return MHD_NO;
+    }
+  if ( (MHD_INVALID_PIPE_ != daemon->wpipe[0]) &&
+       (MHD_USE_SUSPEND_RESUME == (daemon->options & MHD_USE_SUSPEND_RESUME)) )
+    {
+      event.events = EPOLLIN | EPOLLET;
+      event.data.ptr = NULL;
+      event.data.fd = daemon->wpipe[0];
+      if (0 != epoll_ctl (daemon->epoll_fd,
+                          EPOLL_CTL_ADD,
+                          daemon->wpipe[0],
+                          &event))
+        {
+#if HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "Call to epoll_ctl failed: %s\n",
+                    MHD_socket_last_strerr_ ());
+#endif
+          return MHD_NO;
+        }
+    }
+  daemon->listen_socket_in_epoll = MHD_YES;
+  return MHD_YES;
+}
+#endif
+
+
+/**
+ * Start a webserver on the given port.
+ *
+ * @param flags combination of `enum MHD_FLAG` values
+ * @param port port to bind to (in host byte order)
+ * @param apc callback to call to check which clients
+ *        will be allowed to connect; you can pass NULL
+ *        in which case connections from any IP will be
+ *        accepted
+ * @param apc_cls extra argument to @a apc
+ * @param dh handler called for all requests (repeatedly)
+ * @param dh_cls extra argument to @a dh
+ * @param ap list of options (type-value pairs,
+ *        terminated with #MHD_OPTION_END).
+ * @return NULL on error, handle to daemon on success
+ * @ingroup event
+ */
+struct MHD_Daemon *
+MHD_start_daemon_va (unsigned int flags,
+                     uint16_t port,
+                     MHD_AcceptPolicyCallback apc,
+                     void *apc_cls,
+                     MHD_AccessHandlerCallback dh, void *dh_cls,
+		     va_list ap)
+{
+  const int on = 1;
+  struct MHD_Daemon *daemon;
+  MHD_socket socket_fd;
+  struct sockaddr_in servaddr4;
+#if HAVE_INET6
+  struct sockaddr_in6 servaddr6;
+#endif
+  const struct sockaddr *servaddr = NULL;
+  socklen_t addrlen;
+  unsigned int i;
+  int res_thread_create;
+  int use_pipe;
+
+#ifndef HAVE_INET6
+  if (0 != (flags & MHD_USE_IPv6))
+    return NULL;
+#endif
+#ifndef HAVE_POLL
+  if (0 != (flags & MHD_USE_POLL))
+    return NULL;
+#endif
+#if ! HTTPS_SUPPORT
+  if (0 != (flags & MHD_USE_SSL))
+    return NULL;
+#endif
+#ifndef TCP_FASTOPEN
+  if (0 != (flags & MHD_USE_TCP_FASTOPEN))
+    return NULL;
+#endif
+  if (NULL == dh)
+    return NULL;
+  if (NULL == (daemon = malloc (sizeof (struct MHD_Daemon))))
+    return NULL;
+  memset (daemon, 0, sizeof (struct MHD_Daemon));
+#if EPOLL_SUPPORT
+  daemon->epoll_fd = -1;
+#endif
+  /* try to open listen socket */
+#if HTTPS_SUPPORT
+  if (0 != (flags & MHD_USE_SSL))
+    {
+      gnutls_priority_init (&daemon->priority_cache,
+			    "NORMAL",
+			    NULL);
+    }
+#endif
+  daemon->socket_fd = MHD_INVALID_SOCKET;
+  daemon->listening_address_reuse = 0;
+  daemon->options = flags;
+#if WINDOWS
+  /* Winsock is broken with respect to 'shutdown';
+     this disables us calling 'shutdown' on W32. */
+  daemon->options |= MHD_USE_EPOLL_TURBO;
+#endif
+  daemon->port = port;
+  daemon->apc = apc;
+  daemon->apc_cls = apc_cls;
+  daemon->default_handler = dh;
+  daemon->default_handler_cls = dh_cls;
+  daemon->connections = 0;
+  daemon->connection_limit = MHD_MAX_CONNECTIONS_DEFAULT;
+  daemon->pool_size = MHD_POOL_SIZE_DEFAULT;
+  daemon->pool_increment = MHD_BUF_INC_SIZE;
+  daemon->unescape_callback = &unescape_wrapper;
+  daemon->connection_timeout = 0;       /* no timeout */
+  daemon->wpipe[0] = MHD_INVALID_PIPE_;
+  daemon->wpipe[1] = MHD_INVALID_PIPE_;
+#if HAVE_MESSAGES
+  daemon->custom_error_log = (MHD_LogCallback) &vfprintf;
+  daemon->custom_error_log_cls = stderr;
+#endif
+#ifdef HAVE_LISTEN_SHUTDOWN
+  use_pipe = (0 != (daemon->options & (MHD_USE_NO_LISTEN_SOCKET | MHD_USE_PIPE_FOR_SHUTDOWN)));
+#else
+  use_pipe = 1; /* yes, must use pipe to signal shutdown */
+#endif
+  if (0 == (flags & (MHD_USE_SELECT_INTERNALLY | MHD_USE_THREAD_PER_CONNECTION)))
+    use_pipe = 0; /* useless if we are using 'external' select */
+  if ( (use_pipe) && (0 != MHD_pipe_ (daemon->wpipe)) )
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"Failed to create control pipe: %s\n",
+		MHD_strerror_ (errno));
+#endif
+      free (daemon);
+      return NULL;
+    }
+#ifndef WINDOWS
+  if ( (0 == (flags & (MHD_USE_POLL | MHD_USE_EPOLL_LINUX_ONLY))) &&
+       (1 == use_pipe) &&
+       (daemon->wpipe[0] >= FD_SETSIZE) )
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"file descriptor for control pipe exceeds maximum value\n");
+#endif
+      if (0 != MHD_pipe_close_ (daemon->wpipe[0]))
+	MHD_PANIC ("close failed\n");
+      if (0 != MHD_pipe_close_ (daemon->wpipe[1]))
+	MHD_PANIC ("close failed\n");
+      free (daemon);
+      return NULL;
+    }
+#endif
+#ifdef DAUTH_SUPPORT
+  daemon->digest_auth_rand_size = 0;
+  daemon->digest_auth_random = NULL;
+  daemon->nonce_nc_size = 4; /* tiny */
+#endif
+#if HTTPS_SUPPORT
+  if (0 != (flags & MHD_USE_SSL))
+    {
+      daemon->cred_type = GNUTLS_CRD_CERTIFICATE;
+    }
+#endif
+
+
+  if (MHD_YES != parse_options_va (daemon, &servaddr, ap))
+    {
+#if HTTPS_SUPPORT
+      if ( (0 != (flags & MHD_USE_SSL)) &&
+	   (NULL != daemon->priority_cache) )
+	gnutls_priority_deinit (daemon->priority_cache);
+#endif
+      free (daemon);
+      return NULL;
+    }
+#ifdef DAUTH_SUPPORT
+  if (daemon->nonce_nc_size > 0)
+    {
+      if ( ( (size_t) (daemon->nonce_nc_size * sizeof (struct MHD_NonceNc))) /
+	   sizeof(struct MHD_NonceNc) != daemon->nonce_nc_size)
+	{
+#if HAVE_MESSAGES
+	  MHD_DLOG (daemon,
+		    "Specified value for NC_SIZE too large\n");
+#endif
+#if HTTPS_SUPPORT
+	  if (0 != (flags & MHD_USE_SSL))
+	    gnutls_priority_deinit (daemon->priority_cache);
+#endif
+	  free (daemon);
+	  return NULL;
+	}
+      daemon->nnc = malloc (daemon->nonce_nc_size * sizeof (struct MHD_NonceNc));
+      if (NULL == daemon->nnc)
+	{
+#if HAVE_MESSAGES
+	  MHD_DLOG (daemon,
+		    "Failed to allocate memory for nonce-nc map: %s\n",
+		    MHD_strerror_ (errno));
+#endif
+#if HTTPS_SUPPORT
+	  if (0 != (flags & MHD_USE_SSL))
+	    gnutls_priority_deinit (daemon->priority_cache);
+#endif
+	  free (daemon);
+	  return NULL;
+	}
+    }
+
+  if (MHD_YES != MHD_mutex_create_ (&daemon->nnc_lock))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"MHD failed to initialize nonce-nc mutex\n");
+#endif
+#if HTTPS_SUPPORT
+      if (0 != (flags & MHD_USE_SSL))
+	gnutls_priority_deinit (daemon->priority_cache);
+#endif
+      free (daemon->nnc);
+      free (daemon);
+      return NULL;
+    }
+#endif
+
+  /* Thread pooling currently works only with internal select thread model */
+  if ( (0 == (flags & MHD_USE_SELECT_INTERNALLY)) &&
+       (daemon->worker_pool_size > 0) )
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"MHD thread pooling only works with MHD_USE_SELECT_INTERNALLY\n");
+#endif
+      goto free_and_fail;
+    }
+
+  if ( (MHD_USE_SUSPEND_RESUME == (flags & MHD_USE_SUSPEND_RESUME)) &&
+       (0 != (flags & MHD_USE_THREAD_PER_CONNECTION)) )
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                "Combining MHD_USE_THREAD_PER_CONNECTION and MHD_USE_SUSPEND_RESUME is not supported.\n");
+#endif
+      goto free_and_fail;
+    }
+
+#ifdef __SYMBIAN32__
+  if (0 != (flags & (MHD_USE_SELECT_INTERNALLY | MHD_USE_THREAD_PER_CONNECTION)))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"Threaded operations are not supported on Symbian.\n");
+#endif
+      goto free_and_fail;
+    }
+#endif
+  if ( (MHD_INVALID_SOCKET == daemon->socket_fd) &&
+       (0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) )
+    {
+      /* try to open listen socket */
+      if (0 != (flags & MHD_USE_IPv6))
+	socket_fd = create_socket (daemon,
+				   PF_INET6, SOCK_STREAM, 0);
+      else
+	socket_fd = create_socket (daemon,
+				   PF_INET, SOCK_STREAM, 0);
+      if (MHD_INVALID_SOCKET == socket_fd)
+	{
+#if HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "Call to socket failed: %s\n",
+                    MHD_socket_last_strerr_ ());
+#endif
+	  goto free_and_fail;
+	}
+
+      /* Apply the socket options according to listening_address_reuse. */
+      if (0 == daemon->listening_address_reuse)
+        {
+          /* No user requirement, use "traditional" default SO_REUSEADDR,
+           and do not fail if it doesn't work */
+          if (0 > setsockopt (socket_fd,
+                              SOL_SOCKET,
+                              SO_REUSEADDR,
+                              (void*)&on, sizeof (on)))
+          {
+#if HAVE_MESSAGES
+            MHD_DLOG (daemon,
+                      "setsockopt failed: %s\n",
+                      MHD_socket_last_strerr_ ());
+#endif
+          }
+        }
+      else if (daemon->listening_address_reuse > 0)
+        {
+          /* User requested to allow reusing listening address:port.
+           * Use SO_REUSEADDR on Windows and SO_REUSEPORT on most platforms.
+           * Fail if SO_REUSEPORT does not exist or setsockopt fails.
+           */
+#ifdef _WIN32
+          /* SO_REUSEADDR on W32 has the same semantics
+             as SO_REUSEPORT on BSD/Linux */
+          if (0 > setsockopt (socket_fd,
+                              SOL_SOCKET,
+                              SO_REUSEADDR,
+                              (void*)&on, sizeof (on)))
+            {
+#if HAVE_MESSAGES
+              MHD_DLOG (daemon,
+                        "setsockopt failed: %s\n",
+                        MHD_socket_last_strerr_ ());
+#endif
+              goto free_and_fail;
+            }
+#else
+#ifndef SO_REUSEPORT
+#ifdef LINUX
+/* Supported since Linux 3.9, but often not present (or commented out)
+   in the headers at this time; but 15 is reserved for this and
+   thus should be safe to use. */
+#define SO_REUSEPORT 15
+#endif
+#endif
+#ifdef SO_REUSEPORT
+          if (0 > setsockopt (socket_fd,
+                              SOL_SOCKET,
+                              SO_REUSEPORT,
+                              (void*)&on, sizeof (on)))
+            {
+#if HAVE_MESSAGES
+              MHD_DLOG (daemon,
+                        "setsockopt failed: %s\n",
+                        MHD_socket_last_strerr_ ());
+#endif
+              goto free_and_fail;
+            }
+#else
+          /* we're supposed to allow address:port re-use, but
+             on this platform we cannot; fail hard */
+#if HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "Cannot allow listening address reuse: SO_REUSEPORT not defined\n");
+#endif
+          goto free_and_fail;
+#endif
+#endif
+        }
+      else /* if (daemon->listening_address_reuse < 0) */
+        {
+          /* User requested to disallow reusing listening address:port.
+           * Do nothing except for Windows where SO_EXCLUSIVEADDRUSE
+           * is used. Fail if it does not exist or setsockopt fails.
+           */
+#ifdef _WIN32
+#ifdef SO_EXCLUSIVEADDRUSE
+          if (0 > setsockopt (socket_fd,
+                              SOL_SOCKET,
+                              SO_EXCLUSIVEADDRUSE,
+                              (void*)&on, sizeof (on)))
+            {
+#if HAVE_MESSAGES
+              MHD_DLOG (daemon,
+                        "setsockopt failed: %s\n",
+                        MHD_socket_last_strerr_ ());
+#endif
+              goto free_and_fail;
+            }
+#else /* SO_EXCLUSIVEADDRUSE not defined on W32? */
+#if HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "Cannot disallow listening address reuse: SO_EXCLUSIVEADDRUSE not defined\n");
+#endif
+          goto free_and_fail;
+#endif
+#endif /* _WIN32 */
+        }
+
+      /* check for user supplied sockaddr */
+#if HAVE_INET6
+      if (0 != (flags & MHD_USE_IPv6))
+	addrlen = sizeof (struct sockaddr_in6);
+      else
+#endif
+	addrlen = sizeof (struct sockaddr_in);
+      if (NULL == servaddr)
+	{
+#if HAVE_INET6
+	  if (0 != (flags & MHD_USE_IPv6))
+	    {
+	      memset (&servaddr6, 0, sizeof (struct sockaddr_in6));
+	      servaddr6.sin6_family = AF_INET6;
+	      servaddr6.sin6_port = htons (port);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+	      servaddr6.sin6_len = sizeof (struct sockaddr_in6);
+#endif
+	      servaddr = (struct sockaddr *) &servaddr6;
+	    }
+	  else
+#endif
+	    {
+	      memset (&servaddr4, 0, sizeof (struct sockaddr_in));
+	      servaddr4.sin_family = AF_INET;
+	      servaddr4.sin_port = htons (port);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+	      servaddr4.sin_len = sizeof (struct sockaddr_in);
+#endif
+	      servaddr = (struct sockaddr *) &servaddr4;
+	    }
+	}
+      daemon->socket_fd = socket_fd;
+
+      if (0 != (flags & MHD_USE_IPv6))
+	{
+#ifdef IPPROTO_IPV6
+#ifdef IPV6_V6ONLY
+	  /* Note: "IPV6_V6ONLY" is declared by Windows Vista ff., see "IPPROTO_IPV6 Socket Options"
+	     (http://msdn.microsoft.com/en-us/library/ms738574%28v=VS.85%29.aspx);
+	     and may also be missing on older POSIX systems; good luck if you have any of those,
+	     your IPv6 socket may then also bind against IPv4 anyway... */
+#ifndef WINDOWS
+	  const int
+#else
+	  const char
+#endif
+            on = (MHD_USE_DUAL_STACK != (flags & MHD_USE_DUAL_STACK));
+	  if (0 > setsockopt (socket_fd,
+                              IPPROTO_IPV6, IPV6_V6ONLY,
+                              &on, sizeof (on)))
+      {
+#if HAVE_MESSAGES
+            MHD_DLOG (daemon,
+                      "setsockopt failed: %s\n",
+                      MHD_socket_last_strerr_ ());
+#endif
+      }
+#endif
+#endif
+	}
+      if (-1 == bind (socket_fd, servaddr, addrlen))
+	{
+#if HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "Failed to bind to port %u: %s\n",
+                    (unsigned int) port,
+                    MHD_socket_last_strerr_ ());
+#endif
+	  if (0 != MHD_socket_close_ (socket_fd))
+	    MHD_PANIC ("close failed\n");
+	  goto free_and_fail;
+	}
+#ifdef TCP_FASTOPEN
+      if (0 != (flags & MHD_USE_TCP_FASTOPEN))
+      {
+        if (0 == daemon->fastopen_queue_size)
+          daemon->fastopen_queue_size = MHD_TCP_FASTOPEN_QUEUE_SIZE_DEFAULT;
+        if (0 != setsockopt (socket_fd,
+                             IPPROTO_TCP, TCP_FASTOPEN,
+                             &daemon->fastopen_queue_size,
+                             sizeof (daemon->fastopen_queue_size)))
+        {
+#if HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "setsockopt failed: %s\n",
+                    MHD_socket_last_strerr_ ());
+#endif
+        }
+      }
+#endif
+#if EPOLL_SUPPORT
+      if (0 != (flags & MHD_USE_EPOLL_LINUX_ONLY))
+	{
+	  int sk_flags = fcntl (socket_fd, F_GETFL);
+	  if (0 != fcntl (socket_fd, F_SETFL, sk_flags | O_NONBLOCK))
+	    {
+#if HAVE_MESSAGES
+	      MHD_DLOG (daemon,
+			"Failed to make listen socket non-blocking: %s\n",
+			MHD_socket_last_strerr_ ());
+#endif
+	      if (0 != MHD_socket_close_ (socket_fd))
+		MHD_PANIC ("close failed\n");
+	      goto free_and_fail;
+	    }
+	}
+#endif
+      if (listen (socket_fd, 32) < 0)
+	{
+#if HAVE_MESSAGES
+          MHD_DLOG (daemon,
+                    "Failed to listen for connections: %s\n",
+                    MHD_socket_last_strerr_ ());
+#endif
+	  if (0 != MHD_socket_close_ (socket_fd))
+	    MHD_PANIC ("close failed\n");
+	  goto free_and_fail;
+	}
+    }
+  else
+    {
+      socket_fd = daemon->socket_fd;
+    }
+#ifndef WINDOWS
+  if ( (socket_fd >= FD_SETSIZE) &&
+       (0 == (flags & (MHD_USE_POLL | MHD_USE_EPOLL_LINUX_ONLY)) ) )
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                "Socket descriptor larger than FD_SETSIZE: %d > %d\n",
+                socket_fd,
+                FD_SETSIZE);
+#endif
+      if (0 != MHD_socket_close_ (socket_fd))
+	MHD_PANIC ("close failed\n");
+      goto free_and_fail;
+    }
+#endif
+
+#if EPOLL_SUPPORT
+  if ( (0 != (flags & MHD_USE_EPOLL_LINUX_ONLY)) &&
+       (0 == daemon->worker_pool_size) &&
+       (0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) )
+    {
+      if (0 != (flags & MHD_USE_THREAD_PER_CONNECTION))
+	{
+#if HAVE_MESSAGES
+	  MHD_DLOG (daemon,
+		    "Combining MHD_USE_THREAD_PER_CONNECTION and MHD_USE_EPOLL_LINUX_ONLY is not supported.\n");
+#endif
+	  goto free_and_fail;
+	}
+      if (MHD_YES != setup_epoll_to_listen (daemon))
+	goto free_and_fail;
+    }
+#else
+  if (0 != (flags & MHD_USE_EPOLL_LINUX_ONLY))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"epoll is not supported on this platform by this build.\n");
+#endif
+      goto free_and_fail;
+    }
+#endif
+
+  if (MHD_YES != MHD_mutex_create_ (&daemon->per_ip_connection_mutex))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+               "MHD failed to initialize IP connection limit mutex\n");
+#endif
+      if ( (MHD_INVALID_SOCKET != socket_fd) &&
+	   (0 != MHD_socket_close_ (socket_fd)) )
+	MHD_PANIC ("close failed\n");
+      goto free_and_fail;
+    }
+  if (MHD_YES != MHD_mutex_create_ (&daemon->cleanup_connection_mutex))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+               "MHD failed to initialize IP connection limit mutex\n");
+#endif
+      (void) MHD_mutex_destroy_ (&daemon->cleanup_connection_mutex);
+      if ( (MHD_INVALID_SOCKET != socket_fd) &&
+	   (0 != MHD_socket_close_ (socket_fd)) )
+	MHD_PANIC ("close failed\n");
+      goto free_and_fail;
+    }
+
+#if HTTPS_SUPPORT
+  /* initialize HTTPS daemon certificate aspects & send / recv functions */
+  if ((0 != (flags & MHD_USE_SSL)) && (0 != MHD_TLS_init (daemon)))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+		"Failed to initialize TLS support\n");
+#endif
+      if ( (MHD_INVALID_SOCKET != socket_fd) &&
+	   (0 != MHD_socket_close_ (socket_fd)) )
+	MHD_PANIC ("close failed\n");
+      (void) MHD_mutex_destroy_ (&daemon->cleanup_connection_mutex);
+      (void) MHD_mutex_destroy_ (&daemon->per_ip_connection_mutex);
+      goto free_and_fail;
+    }
+#endif
+  if ( ( (0 != (flags & MHD_USE_THREAD_PER_CONNECTION)) ||
+	 ( (0 != (flags & MHD_USE_SELECT_INTERNALLY)) &&
+	   (0 == daemon->worker_pool_size)) ) &&
+       (0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) &&
+       (0 != (res_thread_create =
+	      create_thread (&daemon->pid, daemon, &MHD_select_thread, daemon))))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (daemon,
+                "Failed to create listen thread: %s\n",
+		MHD_strerror_ (res_thread_create));
+#endif
+      (void) MHD_mutex_destroy_ (&daemon->cleanup_connection_mutex);
+      (void) MHD_mutex_destroy_ (&daemon->per_ip_connection_mutex);
+      if ( (MHD_INVALID_SOCKET != socket_fd) &&
+	   (0 != MHD_socket_close_ (socket_fd)) )
+	MHD_PANIC ("close failed\n");
+      goto free_and_fail;
+    }
+  if ( (daemon->worker_pool_size > 0) &&
+       (0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) )
+    {
+#if !defined(WINDOWS) || defined(CYGWIN)
+      int sk_flags;
+#else
+      unsigned long sk_flags;
+#endif
+
+      /* Coarse-grained count of connections per thread (note error
+       * due to integer division). Also keep track of how many
+       * connections are leftover after an equal split. */
+      unsigned int conns_per_thread = daemon->connection_limit
+                                      / daemon->worker_pool_size;
+      unsigned int leftover_conns = daemon->connection_limit
+                                    % daemon->worker_pool_size;
+
+      i = 0; /* we need this in case fcntl or malloc fails */
+
+      /* Accept must be non-blocking. Multiple children may wake up
+       * to handle a new connection, but only one will win the race.
+       * The others must immediately return. */
+#if !defined(WINDOWS) || defined(CYGWIN)
+      sk_flags = fcntl (socket_fd, F_GETFL);
+      if (sk_flags < 0)
+        goto thread_failed;
+      if (0 != fcntl (socket_fd, F_SETFL, sk_flags | O_NONBLOCK))
+        goto thread_failed;
+#else
+      sk_flags = 1;
+      if (SOCKET_ERROR == ioctlsocket (socket_fd, FIONBIO, &sk_flags))
+        goto thread_failed;
+#endif /* WINDOWS && !CYGWIN */
+
+      /* Allocate memory for pooled objects */
+      daemon->worker_pool = malloc (sizeof (struct MHD_Daemon)
+                                    * daemon->worker_pool_size);
+      if (NULL == daemon->worker_pool)
+        goto thread_failed;
+
+      /* Start the workers in the pool */
+      for (i = 0; i < daemon->worker_pool_size; ++i)
+        {
+          /* Create copy of the Daemon object for each worker */
+          struct MHD_Daemon *d = &daemon->worker_pool[i];
+
+          memcpy (d, daemon, sizeof (struct MHD_Daemon));
+          /* Adjust pooling params for worker daemons; note that memcpy()
+             has already copied MHD_USE_SELECT_INTERNALLY thread model into
+             the worker threads. */
+          d->master = daemon;
+          d->worker_pool_size = 0;
+          d->worker_pool = NULL;
+
+          if ( (MHD_USE_SUSPEND_RESUME == (flags & MHD_USE_SUSPEND_RESUME)) &&
+               (0 != MHD_pipe_ (d->wpipe)) )
+            {
+#if HAVE_MESSAGES
+              MHD_DLOG (daemon,
+                        "Failed to create worker control pipe: %s\n",
+                        MHD_pipe_last_strerror_() );
+#endif
+              goto thread_failed;
+            }
+#ifndef WINDOWS
+          if ( (0 == (flags & (MHD_USE_POLL | MHD_USE_EPOLL_LINUX_ONLY))) &&
+               (MHD_USE_SUSPEND_RESUME == (flags & MHD_USE_SUSPEND_RESUME)) &&
+               (d->wpipe[0] >= FD_SETSIZE) )
+            {
+#if HAVE_MESSAGES
+              MHD_DLOG (daemon,
+                        "file descriptor for worker control pipe exceeds maximum value\n");
+#endif
+              if (0 != MHD_pipe_close_ (d->wpipe[0]))
+                MHD_PANIC ("close failed\n");
+              if (0 != MHD_pipe_close_ (d->wpipe[1]))
+                MHD_PANIC ("close failed\n");
+              goto thread_failed;
+            }
+#endif
+
+          /* Divide available connections evenly amongst the threads.
+           * Thread indexes in [0, leftover_conns) each get one of the
+           * leftover connections. */
+          d->connection_limit = conns_per_thread;
+          if (i < leftover_conns)
+            ++d->connection_limit;
+#if EPOLL_SUPPORT
+	  if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
+	       (MHD_YES != setup_epoll_to_listen (d)) )
+	    goto thread_failed;
+#endif
+          /* Must init cleanup connection mutex for each worker */
+          if (MHD_YES != MHD_mutex_create_ (&d->cleanup_connection_mutex))
+            {
+#if HAVE_MESSAGES
+              MHD_DLOG (daemon,
+                       "MHD failed to initialize cleanup connection mutex for thread worker %d\n", i);
+#endif
+              goto thread_failed;
+            }
+
+          /* Spawn the worker thread */
+          if (0 != (res_thread_create =
+		    create_thread (&d->pid, daemon, &MHD_select_thread, d)))
+            {
+#if HAVE_MESSAGES
+              MHD_DLOG (daemon,
+                        "Failed to create pool thread: %s\n",
+			MHD_strerror_ (res_thread_create));
+#endif
+              /* Free memory for this worker; cleanup below handles
+               * all previously-created workers. */
+              (void) MHD_mutex_destroy_ (&d->cleanup_connection_mutex);
+              goto thread_failed;
+            }
+        }
+    }
+#if HTTPS_SUPPORT
+  /* API promises to never use the password after initialization,
+     so we additionally NULL it here to not deref a dangling pointer. */
+  daemon->https_key_password = NULL;
+#endif /* HTTPS_SUPPORT */
+
+  return daemon;
+
+thread_failed:
+  /* If no worker threads created, then shut down normally. Calling
+     MHD_stop_daemon (as we do below) doesn't work here since it
+     assumes a 0-sized thread pool means we had been in the default
+     MHD_USE_SELECT_INTERNALLY mode. */
+  if (0 == i)
+    {
+      if ( (MHD_INVALID_SOCKET != socket_fd) &&
+	   (0 != MHD_socket_close_ (socket_fd)) )
+	MHD_PANIC ("close failed\n");
+      (void) MHD_mutex_destroy_ (&daemon->cleanup_connection_mutex);
+      (void) MHD_mutex_destroy_ (&daemon->per_ip_connection_mutex);
+      if (NULL != daemon->worker_pool)
+        free (daemon->worker_pool);
+      goto free_and_fail;
+    }
+
+  /* Shutdown worker threads we've already created. Pretend
+     as though we had fully initialized our daemon, but
+     with a smaller number of threads than had been
+     requested. */
+  daemon->worker_pool_size = i;
+  MHD_stop_daemon (daemon);
+  return NULL;
+
+ free_and_fail:
+  /* clean up basic memory state in 'daemon' and return NULL to
+     indicate failure */
+#if EPOLL_SUPPORT
+  if (-1 != daemon->epoll_fd)
+    close (daemon->epoll_fd);
+#endif
+#ifdef DAUTH_SUPPORT
+  free (daemon->nnc);
+  (void) MHD_mutex_destroy_ (&daemon->nnc_lock);
+#endif
+#if HTTPS_SUPPORT
+  if (0 != (flags & MHD_USE_SSL))
+    gnutls_priority_deinit (daemon->priority_cache);
+#endif
+  free (daemon);
+  return NULL;
+}
+
+
+/**
+ * Close the given connection, remove it from all of its
+ * DLLs and move it into the cleanup queue.
+ *
+ * @param pos connection to move to cleanup
+ */
+static void
+close_connection (struct MHD_Connection *pos)
+{
+  struct MHD_Daemon *daemon = pos->daemon;
+
+  MHD_connection_close (pos,
+			MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
+  if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+    return; /* must let thread to the rest */
+  if (pos->connection_timeout == pos->daemon->connection_timeout)
+    XDLL_remove (daemon->normal_timeout_head,
+		 daemon->normal_timeout_tail,
+		 pos);
+  else
+    XDLL_remove (daemon->manual_timeout_head,
+		 daemon->manual_timeout_tail,
+		 pos);
+  DLL_remove (daemon->connections_head,
+	      daemon->connections_tail,
+	      pos);
+  pos->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
+  DLL_insert (daemon->cleanup_head,
+	      daemon->cleanup_tail,
+	      pos);
+}
+
+
+/**
+ * Close all connections for the daemon; must only be called after
+ * all of the threads have been joined and there is no more concurrent
+ * activity on the connection lists.
+ *
+ * @param daemon daemon to close down
+ */
+static void
+close_all_connections (struct MHD_Daemon *daemon)
+{
+  struct MHD_Connection *pos;
+
+  /* first, make sure all threads are aware of shutdown; need to
+     traverse DLLs in peace... */
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_lock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to acquire cleanup mutex\n");
+  for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
+    {
+      shutdown (pos->socket_fd,
+                (pos->read_closed == MHD_YES) ? SHUT_WR : SHUT_RDWR);
+#if WINDOWS
+      if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+           (MHD_INVALID_PIPE_ != daemon->wpipe[1]) &&
+           (1 != MHD_pipe_write_ (daemon->wpipe[1], "e", 1)) )
+        MHD_PANIC ("failed to signal shutdown via pipe");
+#endif
+    }
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (MHD_YES != MHD_mutex_unlock_ (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to release cleanup mutex\n");
+
+  /* now, collect threads from thread pool */
+  if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+    {
+      for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
+	{
+	  if (0 != MHD_join_thread_ (pos->pid))
+	    MHD_PANIC ("Failed to join a thread\n");
+	  pos->thread_joined = MHD_YES;
+	}
+    }
+
+  /* now that we're alone, move everyone to cleanup */
+  while (NULL != (pos = daemon->connections_head))
+    close_connection (pos);
+  MHD_cleanup_connections (daemon);
+}
+
+
+#if EPOLL_SUPPORT
+/**
+ * Shutdown epoll()-event loop by adding 'wpipe' to its event set.
+ *
+ * @param daemon daemon of which the epoll() instance must be signalled
+ */
+static void
+epoll_shutdown (struct MHD_Daemon *daemon)
+{
+  struct epoll_event event;
+
+  if (MHD_INVALID_PIPE_ == daemon->wpipe[1])
+    {
+      /* wpipe was required in this mode, how could this happen? */
+      MHD_PANIC ("Internal error\n");
+    }
+  event.events = EPOLLOUT;
+  event.data.ptr = NULL;
+  if (0 != epoll_ctl (daemon->epoll_fd,
+		      EPOLL_CTL_ADD,
+		      daemon->wpipe[1],
+		      &event))
+    MHD_PANIC ("Failed to add wpipe to epoll set to signal termination\n");
+}
+#endif
+
+
+/**
+ * Shutdown an HTTP daemon.
+ *
+ * @param daemon daemon to stop
+ * @ingroup event
+ */
+void
+MHD_stop_daemon (struct MHD_Daemon *daemon)
+{
+  MHD_socket fd;
+  unsigned int i;
+
+  if (NULL == daemon)
+    return;
+  daemon->shutdown = MHD_YES;
+  fd = daemon->socket_fd;
+  daemon->socket_fd = MHD_INVALID_SOCKET;
+  /* Prepare workers for shutdown */
+  if (NULL != daemon->worker_pool)
+    {
+      /* MHD_USE_NO_LISTEN_SOCKET disables thread pools, hence we need to check */
+      for (i = 0; i < daemon->worker_pool_size; ++i)
+	{
+	  daemon->worker_pool[i].shutdown = MHD_YES;
+	  daemon->worker_pool[i].socket_fd = MHD_INVALID_SOCKET;
+#if EPOLL_SUPPORT
+	  if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
+	       (-1 != daemon->worker_pool[i].epoll_fd) &&
+	       (MHD_INVALID_SOCKET == fd) )
+	    epoll_shutdown (&daemon->worker_pool[i]);
+#endif
+	}
+    }
+  if (MHD_INVALID_PIPE_ != daemon->wpipe[1])
+    {
+      if (1 != MHD_pipe_write_ (daemon->wpipe[1], "e", 1))
+	MHD_PANIC ("failed to signal shutdown via pipe");
+    }
+#ifdef HAVE_LISTEN_SHUTDOWN
+  else
+    {
+      /* fd might be MHD_INVALID_SOCKET here due to 'MHD_quiesce_daemon' */
+      if (MHD_INVALID_SOCKET != fd)
+	(void) shutdown (fd, SHUT_RDWR);
+    }
+#endif
+#if EPOLL_SUPPORT
+  if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
+       (-1 != daemon->epoll_fd) &&
+       (MHD_INVALID_SOCKET == fd) )
+    epoll_shutdown (daemon);
+#endif
+
+#if DEBUG_CLOSE
+#if HAVE_MESSAGES
+  MHD_DLOG (daemon,
+            "MHD listen socket shutdown\n");
+#endif
+#endif
+
+
+  /* Signal workers to stop and clean them up */
+  if (NULL != daemon->worker_pool)
+    {
+      /* MHD_USE_NO_LISTEN_SOCKET disables thread pools, hence we need to check */
+      for (i = 0; i < daemon->worker_pool_size; ++i)
+	{
+	  if (MHD_INVALID_PIPE_ != daemon->worker_pool[i].wpipe[1])
+	    {
+	      if (1 != MHD_pipe_write_ (daemon->worker_pool[i].wpipe[1], "e", 1))
+		MHD_PANIC ("failed to signal shutdown via pipe");
+	    }
+	  if (0 != MHD_join_thread_ (daemon->worker_pool[i].pid))
+	      MHD_PANIC ("Failed to join a thread\n");
+	  close_all_connections (&daemon->worker_pool[i]);
+	  (void) MHD_mutex_destroy_ (&daemon->worker_pool[i].cleanup_connection_mutex);
+#if EPOLL_SUPPORT
+	  if ( (-1 != daemon->worker_pool[i].epoll_fd) &&
+	       (0 != MHD_socket_close_ (daemon->worker_pool[i].epoll_fd)) )
+	    MHD_PANIC ("close failed\n");
+#endif
+          if ( (MHD_USE_SUSPEND_RESUME == (daemon->options & MHD_USE_SUSPEND_RESUME)) )
+            {
+              if (MHD_INVALID_PIPE_ != daemon->worker_pool[i].wpipe[1])
+                {
+	           if (0 != MHD_pipe_close_ (daemon->worker_pool[i].wpipe[0]))
+	             MHD_PANIC ("close failed\n");
+	           if (0 != MHD_pipe_close_ (daemon->worker_pool[i].wpipe[1]))
+	             MHD_PANIC ("close failed\n");
+                }
+	    }
+	}
+      free (daemon->worker_pool);
+    }
+  else
+    {
+      /* clean up master threads */
+      if ((0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
+	  ((0 != (daemon->options & MHD_USE_SELECT_INTERNALLY))
+	   && (0 == daemon->worker_pool_size)))
+	{
+	  if (0 != MHD_join_thread_ (daemon->pid))
+	    {
+	      MHD_PANIC ("Failed to join a thread\n");
+	    }
+	}
+    }
+  close_all_connections (daemon);
+  if ( (MHD_INVALID_SOCKET != fd) &&
+       (0 != MHD_socket_close_ (fd)) )
+    MHD_PANIC ("close failed\n");
+
+  /* TLS clean up */
+#if HTTPS_SUPPORT
+  if (MHD_YES == daemon->have_dhparams)
+    {
+      gnutls_dh_params_deinit (daemon->https_mem_dhparams);
+      daemon->have_dhparams = MHD_NO;
+    }
+  if (0 != (daemon->options & MHD_USE_SSL))
+    {
+      gnutls_priority_deinit (daemon->priority_cache);
+      if (daemon->x509_cred)
+        gnutls_certificate_free_credentials (daemon->x509_cred);
+    }
+#endif
+#if EPOLL_SUPPORT
+  if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
+       (-1 != daemon->epoll_fd) &&
+       (0 != MHD_socket_close_ (daemon->epoll_fd)) )
+    MHD_PANIC ("close failed\n");
+#endif
+
+#ifdef DAUTH_SUPPORT
+  free (daemon->nnc);
+  (void) MHD_mutex_destroy_ (&daemon->nnc_lock);
+#endif
+  (void) MHD_mutex_destroy_ (&daemon->per_ip_connection_mutex);
+  (void) MHD_mutex_destroy_ (&daemon->cleanup_connection_mutex);
+
+  if (MHD_INVALID_PIPE_ != daemon->wpipe[1])
+    {
+      if (0 != MHD_pipe_close_ (daemon->wpipe[0]))
+	MHD_PANIC ("close failed\n");
+      if (0 != MHD_pipe_close_ (daemon->wpipe[1]))
+	MHD_PANIC ("close failed\n");
+    }
+  free (daemon);
+}
+
+
+/**
+ * Obtain information about the given daemon
+ * (not fully implemented!).
+ *
+ * @param daemon what daemon to get information about
+ * @param info_type what information is desired?
+ * @param ... depends on @a info_type
+ * @return NULL if this information is not available
+ *         (or if the @a info_type is unknown)
+ * @ingroup specialized
+ */
+const union MHD_DaemonInfo *
+MHD_get_daemon_info (struct MHD_Daemon *daemon,
+		     enum MHD_DaemonInfoType info_type,
+		     ...)
+{
+  switch (info_type)
+    {
+    case MHD_DAEMON_INFO_KEY_SIZE:
+      return NULL; /* no longer supported */
+    case MHD_DAEMON_INFO_MAC_KEY_SIZE:
+      return NULL; /* no longer supported */
+    case MHD_DAEMON_INFO_LISTEN_FD:
+      return (const union MHD_DaemonInfo *) &daemon->socket_fd;
+#if EPOLL_SUPPORT
+    case MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY:
+      return (const union MHD_DaemonInfo *) &daemon->epoll_fd;
+#endif
+    case MHD_DAEMON_INFO_CURRENT_CONNECTIONS:
+      MHD_cleanup_connections (daemon);
+      if (daemon->worker_pool)
+        {
+          /* Collect the connection information stored in the workers. */
+          unsigned int i;
+
+          daemon->connections = 0;
+          for (i=0;i<daemon->worker_pool_size;i++)
+            {
+              MHD_cleanup_connections (&daemon->worker_pool[i]);
+              daemon->connections += daemon->worker_pool[i].connections;
+            }
+        }
+      return (const union MHD_DaemonInfo *) &daemon->connections;
+    default:
+      return NULL;
+    };
+}
+
+
+/**
+ * Sets the global error handler to a different implementation.  @a cb
+ * will only be called in the case of typically fatal, serious
+ * internal consistency issues.  These issues should only arise in the
+ * case of serious memory corruption or similar problems with the
+ * architecture.  While @a cb is allowed to return and MHD will then
+ * try to continue, this is never safe.
+ *
+ * The default implementation that is used if no panic function is set
+ * simply prints an error message and calls `abort()`.  Alternative
+ * implementations might call `exit()` or other similar functions.
+ *
+ * @param cb new error handler
+ * @param cls passed to @a cb
+ * @ingroup logging
+ */
+void
+MHD_set_panic_func (MHD_PanicCallback cb, void *cls)
+{
+  mhd_panic = cb;
+  mhd_panic_cls = cls;
+}
+
+
+/**
+ * Obtain the version of this library
+ *
+ * @return static version string, e.g. "0.9.9"
+ * @ingroup specialized
+ */
+const char *
+MHD_get_version (void)
+{
+#ifdef PACKAGE_VERSION
+  return PACKAGE_VERSION;
+#else  /* !PACKAGE_VERSION */
+  static char ver[12] = "\0\0\0\0\0\0\0\0\0\0\0";
+  if (0 == ver[0])
+  {
+    int res = MHD_snprintf_(ver, sizeof(ver), "%x.%x.%x",
+                            (((int)MHD_VERSION >> 24) & 0xFF),
+                            (((int)MHD_VERSION >> 16) & 0xFF),
+                            (((int)MHD_VERSION >> 8) & 0xFF));
+    if (0 >= res || sizeof(ver) <= res)
+      return "0.0.0"; /* Can't return real version*/
+  }
+  return ver;
+#endif /* !PACKAGE_VERSION */
+}
+
+
+/**
+ * Get information about supported MHD features.
+ * Indicate that MHD was compiled with or without support for
+ * particular feature. Some features require additional support
+ * by kernel. Kernel support is not checked by this function.
+ *
+ * @param feature type of requested information
+ * @return #MHD_YES if feature is supported by MHD, #MHD_NO if
+ * feature is not supported or feature is unknown.
+ * @ingroup specialized
+ */
+_MHD_EXTERN int
+MHD_is_feature_supported(enum MHD_FEATURE feature)
+{
+  switch(feature)
+    {
+    case MHD_FEATURE_MESSGES:
+#if HAVE_MESSAGES
+      return MHD_YES;
+#else
+      return MHD_NO;
+#endif
+    case MHD_FEATURE_SSL:
+#if HTTPS_SUPPORT
+      return MHD_YES;
+#else
+      return MHD_NO;
+#endif
+    case MHD_FEATURE_HTTPS_CERT_CALLBACK:
+#if HTTPS_SUPPORT && GNUTLS_VERSION_MAJOR >= 3
+      return MHD_YES;
+#else
+      return MHD_NO;
+#endif
+    case MHD_FEATURE_IPv6:
+#ifdef HAVE_INET6
+      return MHD_YES;
+#else
+      return MHD_NO;
+#endif
+    case MHD_FEATURE_IPv6_ONLY:
+#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
+      return MHD_YES;
+#else
+      return MHD_NO;
+#endif
+    case MHD_FEATURE_POLL:
+#ifdef HAVE_POLL
+      return MHD_YES;
+#else
+      return MHD_NO;
+#endif
+    case MHD_FEATURE_EPOLL:
+#if EPOLL_SUPPORT
+      return MHD_YES;
+#else
+      return MHD_NO;
+#endif
+    case MHD_FEATURE_SHUTDOWN_LISTEN_SOCKET:
+#ifdef HAVE_LISTEN_SHUTDOWN
+      return MHD_YES;
+#else
+      return MHD_NO;
+#endif
+    case MHD_FEATURE_SOCKETPAIR:
+#ifdef MHD_DONT_USE_PIPES
+      return MHD_YES;
+#else
+      return MHD_NO;
+#endif
+    case MHD_FEATURE_TCP_FASTOPEN:
+#ifdef TCP_FASTOPEN
+      return MHD_YES;
+#else
+      return MHD_NO;
+#endif
+    case MHD_FEATURE_BASIC_AUTH:
+#if BAUTH_SUPPORT
+      return MHD_YES;
+#else
+      return MHD_NO;
+#endif
+    case MHD_FEATURE_DIGEST_AUTH:
+#if DAUTH_SUPPORT
+      return MHD_YES;
+#else
+      return MHD_NO;
+#endif
+    case MHD_FEATURE_POSTPROCESSOR:
+#if HAVE_POSTPROCESSOR
+      return MHD_YES;
+#else
+      return MHD_NO;
+#endif
+    case MHD_FEATURE_HTTPS_KEY_PASSWORD:
+#if HTTPS_SUPPORT && GNUTLS_VERSION_NUMBER >= 0x030111
+      return MHD_YES;
+#else
+      return MHD_NO;
+#endif
+    }
+  return MHD_NO;
+}
+
+
+#if HTTPS_SUPPORT && GCRYPT_VERSION_NUMBER < 0x010600
+#if defined(MHD_USE_POSIX_THREADS)
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#elif defined(MHD_W32_MUTEX_)
+static int gcry_w32_mutex_init (void **ppmtx)
+{
+  *ppmtx = malloc (sizeof (MHD_mutex_));
+
+  if (NULL == *ppmtx)
+    return ENOMEM;
+
+  if (MHD_YES != MHD_mutex_create_ ((MHD_mutex_*)*ppmtx))
+    {
+      free (*ppmtx);
+      *ppmtx = NULL;
+      return EPERM;
+    }
+
+  return 0;
+}
+static int gcry_w32_mutex_destroy (void **ppmtx)
+  { int res = (MHD_YES == MHD_mutex_destroy_ ((MHD_mutex_*)*ppmtx)) ? 0 : 1;
+    free (*ppmtx); return res; }
+static int gcry_w32_mutex_lock (void **ppmtx)
+  { return (MHD_YES == MHD_mutex_lock_ ((MHD_mutex_*)*ppmtx)) ? 0 : 1; }
+static int gcry_w32_mutex_unlock (void **ppmtx)
+  { return (MHD_YES == MHD_mutex_unlock_ ((MHD_mutex_*)*ppmtx)) ? 0 : 1; }
+
+static struct gcry_thread_cbs gcry_threads_w32 = {
+  (GCRY_THREAD_OPTION_USER | (GCRY_THREAD_OPTION_VERSION << 8)),
+  NULL, gcry_w32_mutex_init, gcry_w32_mutex_destroy,
+  gcry_w32_mutex_lock, gcry_w32_mutex_unlock,
+  NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+
+#endif // defined(MHD_W32_MUTEX_)
+#endif // HTTPS_SUPPORT && GCRYPT_VERSION_NUMBER < 0x010600
+
+
+/**
+ * Initialize do setup work.
+ */
+void MHD_init(void)
+{
+  mhd_panic = &mhd_panic_std;
+  mhd_panic_cls = NULL;
+
+#ifdef _WIN32
+  WSADATA wsd;
+  if (0 != WSAStartup(MAKEWORD(2, 2), &wsd))
+    MHD_PANIC ("Failed to initialize winsock\n");
+  mhd_winsock_inited_ = 1;
+  if (2 != LOBYTE(wsd.wVersion) && 2 != HIBYTE(wsd.wVersion))
+    MHD_PANIC ("Winsock version 2.2 is not available\n");
+#endif
+#if HTTPS_SUPPORT
+#if GCRYPT_VERSION_NUMBER < 0x010600
+#if defined(MHD_USE_POSIX_THREADS)
+  if (0 != gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread))
+    MHD_PANIC ("Failed to initialise multithreading in libgcrypt\n");
+#elif defined(MHD_W32_MUTEX_)
+  if (0 != gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_w32))
+    MHD_PANIC ("Failed to initialise multithreading in libgcrypt\n");
+#endif // defined(MHD_W32_MUTEX_)
+  gcry_check_version (NULL);
+#else
+  if (NULL == gcry_check_version ("1.6.0"))
+    MHD_PANIC ("libgcrypt is too old. MHD was compiled for libgcrypt 1.6.0 or newer\n");
+#endif
+  gnutls_global_init ();
+#endif
+}
+
+
+void MHD_fini(void)
+{
+#if HTTPS_SUPPORT
+  gnutls_global_deinit ();
+#endif
+#ifdef _WIN32
+  if (mhd_winsock_inited_)
+    WSACleanup();
+#endif
+}
+
+_SET_INIT_AND_DEINIT_FUNCS(MHD_init, MHD_fini);
+
+/* end of daemon.c */
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c
new file mode 100644
index 0000000..9c3fe8c
--- /dev/null
+++ b/src/microhttpd/digestauth.c
@@ -0,0 +1,871 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2010, 2011, 2012 Daniel Pittman and Christian Grothoff
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+/**
+ * @file digestauth.c
+ * @brief Implements HTTP digest authentication
+ * @author Amr Ali
+ * @author Matthieu Speder
+ */
+#include "platform.h"
+#include <limits.h>
+#include "internal.h"
+#include "md5.h"
+
+#if defined(_WIN32) && defined(MHD_W32_MUTEX_)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif /* _WIN32 && MHD_W32_MUTEX_ */
+
+#define HASH_MD5_HEX_LEN (2 * MD5_DIGEST_SIZE)
+
+/**
+ * Beginning string for any valid Digest authentication header.
+ */
+#define _BASE		"Digest "
+
+/**
+ * Maximum length of a username for digest authentication.
+ */
+#define MAX_USERNAME_LENGTH 128
+
+/**
+ * Maximum length of a realm for digest authentication.
+ */
+#define MAX_REALM_LENGTH 256
+
+/**
+ * Maximum length of the response in digest authentication.
+ */
+#define MAX_AUTH_RESPONSE_LENGTH 128
+
+
+/**
+ * convert bin to hex
+ *
+ * @param bin binary data
+ * @param len number of bytes in bin
+ * @param hex pointer to len*2+1 bytes
+ */
+static void
+cvthex (const unsigned char *bin,
+	size_t len,
+	char *hex)
+{
+  size_t i;
+  unsigned int j;
+
+  for (i = 0; i < len; ++i)
+    {
+      j = (bin[i] >> 4) & 0x0f;
+      hex[i * 2] = j <= 9 ? (j + '0') : (j + 'a' - 10);
+      j = bin[i] & 0x0f;
+      hex[i * 2 + 1] = j <= 9 ? (j + '0') : (j + 'a' - 10);
+    }
+  hex[len * 2] = '\0';
+}
+
+
+/**
+ * calculate H(A1) as per RFC2617 spec and store the
+ * result in 'sessionkey'.
+ *
+ * @param alg The hash algorithm used, can be "md5" or "md5-sess"
+ * @param username A `char *' pointer to the username value
+ * @param realm A `char *' pointer to the realm value
+ * @param password A `char *' pointer to the password value
+ * @param nonce A `char *' pointer to the nonce value
+ * @param cnonce A `char *' pointer to the cnonce value
+ * @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes
+ */
+static void
+digest_calc_ha1 (const char *alg,
+		 const char *username,
+		 const char *realm,
+		 const char *password,
+		 const char *nonce,
+		 const char *cnonce,
+		 char *sessionkey)
+{
+  struct MD5Context md5;
+  unsigned char ha1[MD5_DIGEST_SIZE];
+
+  MD5Init (&md5);
+  MD5Update (&md5, username, strlen (username));
+  MD5Update (&md5, ":", 1);
+  MD5Update (&md5, realm, strlen (realm));
+  MD5Update (&md5, ":", 1);
+  MD5Update (&md5, password, strlen (password));
+  MD5Final (ha1, &md5);
+  if (MHD_str_equal_caseless_(alg, "md5-sess"))
+    {
+      MD5Init (&md5);
+      MD5Update (&md5, ha1, sizeof (ha1));
+      MD5Update (&md5, ":", 1);
+      MD5Update (&md5, nonce, strlen (nonce));
+      MD5Update (&md5, ":", 1);
+      MD5Update (&md5, cnonce, strlen (cnonce));
+      MD5Final (ha1, &md5);
+    }
+  cvthex (ha1, sizeof (ha1), sessionkey);
+}
+
+
+/**
+ * Calculate request-digest/response-digest as per RFC2617 spec
+ *
+ * @param ha1 H(A1)
+ * @param nonce nonce from server
+ * @param noncecount 8 hex digits
+ * @param cnonce client nonce
+ * @param qop qop-value: "", "auth" or "auth-int"
+ * @param method method from request
+ * @param uri requested URL
+ * @param hentity H(entity body) if qop="auth-int"
+ * @param response request-digest or response-digest
+ */
+static void
+digest_calc_response (const char *ha1,
+		      const char *nonce,
+		      const char *noncecount,
+		      const char *cnonce,
+		      const char *qop,
+		      const char *method,
+		      const char *uri,
+		      const char *hentity,
+		      char *response)
+{
+  struct MD5Context md5;
+  unsigned char ha2[MD5_DIGEST_SIZE];
+  unsigned char resphash[MD5_DIGEST_SIZE];
+  char ha2hex[HASH_MD5_HEX_LEN + 1];
+
+  MD5Init (&md5);
+  MD5Update (&md5, method, strlen(method));
+  MD5Update (&md5, ":", 1);
+  MD5Update (&md5, uri, strlen(uri));
+#if 0
+  if (0 == strcasecmp(qop, "auth-int"))
+    {
+      /* This is dead code since the rest of this module does
+	 not support auth-int. */
+      MD5Update (&md5, ":", 1);
+      if (NULL != hentity)
+	MD5Update (&md5, hentity, strlen(hentity));
+    }
+#endif
+  MD5Final (ha2, &md5);
+  cvthex (ha2, MD5_DIGEST_SIZE, ha2hex);
+  MD5Init (&md5);
+  /* calculate response */
+  MD5Update (&md5, ha1, HASH_MD5_HEX_LEN);
+  MD5Update (&md5, ":", 1);
+  MD5Update (&md5, nonce, strlen(nonce));
+  MD5Update (&md5, ":", 1);
+  if ('\0' != *qop)
+    {
+      MD5Update (&md5, noncecount, strlen(noncecount));
+      MD5Update (&md5, ":", 1);
+      MD5Update (&md5, cnonce, strlen(cnonce));
+      MD5Update (&md5, ":", 1);
+      MD5Update (&md5, qop, strlen(qop));
+      MD5Update (&md5, ":", 1);
+    }
+  MD5Update (&md5, ha2hex, HASH_MD5_HEX_LEN);
+  MD5Final (resphash, &md5);
+  cvthex (resphash, sizeof (resphash), response);
+}
+
+
+/**
+ * Lookup subvalue off of the HTTP Authorization header.
+ *
+ * A description of the input format for 'data' is at
+ * http://en.wikipedia.org/wiki/Digest_access_authentication
+ *
+ *
+ * @param dest where to store the result (possibly truncated if
+ *             the buffer is not big enough).
+ * @param size size of dest
+ * @param data pointer to the Authorization header
+ * @param key key to look up in data
+ * @return size of the located value, 0 if otherwise
+ */
+static size_t
+lookup_sub_value (char *dest,
+		  size_t size,
+		  const char *data,
+		  const char *key)
+{
+  size_t keylen;
+  size_t len;
+  const char *ptr;
+  const char *eq;
+  const char *q1;
+  const char *q2;
+  const char *qn;
+
+  if (0 == size)
+    return 0;
+  keylen = strlen (key);
+  ptr = data;
+  while ('\0' != *ptr)
+    {
+      if (NULL == (eq = strchr (ptr, '=')))
+	return 0;
+      q1 = eq + 1;
+      while (' ' == *q1)
+	q1++;
+      if ('\"' != *q1)
+	{
+	  q2 = strchr (q1, ',');
+	  qn = q2;
+	}
+      else
+	{
+	  q1++;
+	  q2 = strchr (q1, '\"');
+	  if (NULL == q2)
+	    return 0; /* end quote not found */
+	  qn = q2 + 1;
+	}
+      if ((MHD_str_equal_caseless_n_(ptr,
+			      key,
+			      keylen)) &&
+	   (eq == &ptr[keylen]) )
+	{
+	  if (NULL == q2)
+	    {
+	      len = strlen (q1) + 1;
+	      if (size > len)
+		size = len;
+	      size--;
+	      strncpy (dest,
+		       q1,
+		       size);
+	      dest[size] = '\0';
+	      return size;
+	    }
+	  else
+	    {
+	      if (size > (size_t) ((q2 - q1) + 1))
+		size = (q2 - q1) + 1;
+	      size--;
+	      memcpy (dest,
+		      q1,
+		      size);
+	      dest[size] = '\0';
+	      return size;
+	    }
+	}
+      if (NULL == qn)
+	return 0;
+      ptr = strchr (qn, ',');
+      if (NULL == ptr)
+	return 0;
+      ptr++;
+      while (' ' == *ptr)
+	ptr++;
+    }
+  return 0;
+}
+
+
+/**
+ * Check nonce-nc map array with either new nonce counter
+ * or a whole new nonce.
+ *
+ * @param connection The MHD connection structure
+ * @param nonce A pointer that referenced a zero-terminated array of nonce
+ * @param nc The nonce counter, zero to add the nonce to the array
+ * @return MHD_YES if successful, MHD_NO if invalid (or we have no NC array)
+ */
+static int
+check_nonce_nc (struct MHD_Connection *connection,
+		const char *nonce,
+		unsigned long int nc)
+{
+  uint32_t off;
+  uint32_t mod;
+  const char *np;
+
+  mod = connection->daemon->nonce_nc_size;
+  if (0 == mod)
+    return MHD_NO; /* no array! */
+  /* super-fast xor-based "hash" function for HT lookup in nonce array */
+  off = 0;
+  np = nonce;
+  while ('\0' != *np)
+    {
+      off = (off << 8) | (*np ^ (off >> 24));
+      np++;
+    }
+  off = off % mod;
+  /*
+   * Look for the nonce, if it does exist and its corresponding
+   * nonce counter is less than the current nonce counter by 1,
+   * then only increase the nonce counter by one.
+   */
+
+  (void) MHD_mutex_lock_ (&connection->daemon->nnc_lock);
+  if (0 == nc)
+    {
+      strcpy(connection->daemon->nnc[off].nonce,
+	     nonce);
+      connection->daemon->nnc[off].nc = 0;
+      (void) MHD_mutex_unlock_ (&connection->daemon->nnc_lock);
+      return MHD_YES;
+    }
+  if ( (nc <= connection->daemon->nnc[off].nc) ||
+       (0 != strcmp(connection->daemon->nnc[off].nonce, nonce)) )
+    {
+      (void) MHD_mutex_unlock_ (&connection->daemon->nnc_lock);
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+		"Stale nonce received.  If this happens a lot, you should probably increase the size of the nonce array.\n");
+#endif
+      return MHD_NO;
+    }
+  connection->daemon->nnc[off].nc = nc;
+  (void) MHD_mutex_unlock_ (&connection->daemon->nnc_lock);
+  return MHD_YES;
+}
+
+
+/**
+ * Get the username from the authorization header sent by the client
+ *
+ * @param connection The MHD connection structure
+ * @return NULL if no username could be found, a pointer
+ * 			to the username if found
+ * @ingroup authentication
+ */
+char *
+MHD_digest_auth_get_username(struct MHD_Connection *connection)
+{
+  size_t len;
+  char user[MAX_USERNAME_LENGTH];
+  const char *header;
+
+  if (NULL == (header = MHD_lookup_connection_value (connection,
+						     MHD_HEADER_KIND,
+						     MHD_HTTP_HEADER_AUTHORIZATION)))
+    return NULL;
+  if (0 != strncmp (header, _BASE, strlen (_BASE)))
+    return NULL;
+  header += strlen (_BASE);
+  if (0 == (len = lookup_sub_value (user,
+				    sizeof (user),
+				    header,
+				    "username")))
+    return NULL;
+  return strdup (user);
+}
+
+
+/**
+ * Calculate the server nonce so that it mitigates replay attacks
+ * The current format of the nonce is ...
+ * H(timestamp ":" method ":" random ":" uri ":" realm) + Hex(timestamp)
+ *
+ * @param nonce_time The amount of time in seconds for a nonce to be invalid
+ * @param method HTTP method
+ * @param rnd A pointer to a character array for the random seed
+ * @param rnd_size The size of the random seed array @a rnd
+ * @param uri HTTP URI (in MHD, without the arguments ("?k=v")
+ * @param realm A string of characters that describes the realm of auth.
+ * @param nonce A pointer to a character array for the nonce to put in
+ */
+static void
+calculate_nonce (uint32_t nonce_time,
+		 const char *method,
+		 const char *rnd,
+		 size_t rnd_size,
+		 const char *uri,
+		 const char *realm,
+		 char *nonce)
+{
+  struct MD5Context md5;
+  unsigned char timestamp[4];
+  unsigned char tmpnonce[MD5_DIGEST_SIZE];
+  char timestamphex[sizeof(timestamp) * 2 + 1];
+
+  MD5Init (&md5);
+  timestamp[0] = (nonce_time & 0xff000000) >> 0x18;
+  timestamp[1] = (nonce_time & 0x00ff0000) >> 0x10;
+  timestamp[2] = (nonce_time & 0x0000ff00) >> 0x08;
+  timestamp[3] = (nonce_time & 0x000000ff);
+  MD5Update (&md5, timestamp, 4);
+  MD5Update (&md5, ":", 1);
+  MD5Update (&md5, method, strlen (method));
+  MD5Update (&md5, ":", 1);
+  if (rnd_size > 0)
+    MD5Update (&md5, rnd, rnd_size);
+  MD5Update (&md5, ":", 1);
+  MD5Update (&md5, uri, strlen (uri));
+  MD5Update (&md5, ":", 1);
+  MD5Update (&md5, realm, strlen (realm));
+  MD5Final (tmpnonce, &md5);
+  cvthex (tmpnonce, sizeof (tmpnonce), nonce);
+  cvthex (timestamp, 4, timestamphex);
+  strncat (nonce, timestamphex, 8);
+}
+
+
+/**
+ * Test if the given key-value pair is in the headers for the
+ * given connection.
+ *
+ * @param connection the connection
+ * @param key the key
+ * @param value the value, can be NULL
+ * @return #MHD_YES if the key-value pair is in the headers,
+ *         #MHD_NO if not
+ */
+static int
+test_header (struct MHD_Connection *connection,
+	     const char *key,
+	     const char *value)
+{
+  struct MHD_HTTP_Header *pos;
+
+  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
+    {
+      if (MHD_GET_ARGUMENT_KIND != pos->kind)
+	continue;
+      if (0 != strcmp (key, pos->header))
+	continue;
+      if ( (NULL == value) &&
+	   (NULL == pos->value) )
+	return MHD_YES;
+      if ( (NULL == value) ||
+	   (NULL == pos->value) ||
+	   (0 != strcmp (value, pos->value)) )
+	continue;
+      return MHD_YES;
+    }
+  return MHD_NO;
+}
+
+
+/**
+ * Check that the arguments given by the client as part
+ * of the authentication header match the arguments we
+ * got as part of the HTTP request URI.
+ *
+ * @param connection connections with headers to compare against
+ * @param args argument URI string (after "?" in URI)
+ * @return MHD_YES if the arguments match,
+ *         MHD_NO if not
+ */
+static int
+check_argument_match (struct MHD_Connection *connection,
+		      const char *args)
+{
+  struct MHD_HTTP_Header *pos;
+  char *argb;
+  char *argp;
+  char *equals;
+  char *amper;
+  unsigned int num_headers;
+
+  argb = strdup(args);
+  if (NULL == argb)
+  {
+#if HAVE_MESSAGES
+    MHD_DLOG(connection->daemon,
+             "Failed to allocate memory for copy of URI arguments\n");
+#endif /* HAVE_MESSAGES */
+    return MHD_NO;
+  }
+  num_headers = 0;
+  argp = argb;
+  while ( (NULL != argp) &&
+	  ('\0' != argp[0]) )
+    {
+      equals = strchr (argp, '=');
+      if (NULL == equals)
+	{
+	  /* add with 'value' NULL */
+	  connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
+						 connection,
+						 argp);
+	  if (MHD_YES != test_header (connection, argp, NULL))
+	    return MHD_NO;
+	  num_headers++;
+	  break;
+	}
+      equals[0] = '\0';
+      equals++;
+      amper = strchr (equals, '&');
+      if (NULL != amper)
+	{
+	  amper[0] = '\0';
+	  amper++;
+	}
+      connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
+					     connection,
+					     argp);
+      connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
+					     connection,
+					     equals);
+      if (! test_header (connection, argp, equals))
+	return MHD_NO;
+      num_headers++;
+      argp = amper;
+    }
+
+  /* also check that the number of headers matches */
+  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
+    {
+      if (MHD_GET_ARGUMENT_KIND != pos->kind)
+	continue;
+      num_headers--;
+    }
+  if (0 != num_headers)
+    return MHD_NO;
+  return MHD_YES;
+}
+
+
+/**
+ * Authenticates the authorization header sent by the client
+ *
+ * @param connection The MHD connection structure
+ * @param realm The realm presented to the client
+ * @param username The username needs to be authenticated
+ * @param password The password used in the authentication
+ * @param nonce_timeout The amount of time for a nonce to be
+ * 			invalid in seconds
+ * @return #MHD_YES if authenticated, #MHD_NO if not,
+ * 			#MHD_INVALID_NONCE if nonce is invalid
+ * @ingroup authentication
+ */
+int
+MHD_digest_auth_check (struct MHD_Connection *connection,
+		       const char *realm,
+		       const char *username,
+		       const char *password,
+		       unsigned int nonce_timeout)
+{
+  size_t len;
+  const char *header;
+  char *end;
+  char nonce[MAX_NONCE_LENGTH];
+  char cnonce[MAX_NONCE_LENGTH];
+  char qop[15]; /* auth,auth-int */
+  char nc[20];
+  char response[MAX_AUTH_RESPONSE_LENGTH];
+  const char *hentity = NULL; /* "auth-int" is not supported */
+  char ha1[HASH_MD5_HEX_LEN + 1];
+  char respexp[HASH_MD5_HEX_LEN + 1];
+  char noncehashexp[HASH_MD5_HEX_LEN + 9];
+  uint32_t nonce_time;
+  uint32_t t;
+  size_t left; /* number of characters left in 'header' for 'uri' */
+  unsigned long int nci;
+
+  header = MHD_lookup_connection_value (connection,
+					MHD_HEADER_KIND,
+					MHD_HTTP_HEADER_AUTHORIZATION);
+  if (NULL == header)
+    return MHD_NO;
+  if (0 != strncmp(header, _BASE, strlen(_BASE)))
+    return MHD_NO;
+  header += strlen (_BASE);
+  left = strlen (header);
+
+  {
+    char un[MAX_USERNAME_LENGTH];
+
+    len = lookup_sub_value (un,
+			    sizeof (un),
+			    header, "username");
+    if ( (0 == len) ||
+	 (0 != strcmp(username, un)) )
+      return MHD_NO;
+    left -= strlen ("username") + len;
+  }
+
+  {
+    char r[MAX_REALM_LENGTH];
+
+    len = lookup_sub_value(r,
+			   sizeof (r),
+			   header, "realm");
+    if ( (0 == len) ||
+	 (0 != strcmp(realm, r)) )
+      return MHD_NO;
+    left -= strlen ("realm") + len;
+  }
+
+  if (0 == (len = lookup_sub_value (nonce,
+				    sizeof (nonce),
+				    header, "nonce")))
+    return MHD_NO;
+  left -= strlen ("nonce") + len;
+  if (left > 32 * 1024)
+  {
+    /* we do not permit URIs longer than 32k, as we want to
+       make sure to not blow our stack (or per-connection
+       heap memory limit).  Besides, 32k is already insanely
+       large, but of course in theory the
+       #MHD_OPTION_CONNECTION_MEMORY_LIMIT might be very large
+       and would thus permit sending a >32k authorization
+       header value. */
+    return MHD_NO;
+  }
+  {
+    char *uri;
+    
+    uri = malloc(left + 1);
+    if (NULL == uri)
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG(connection->daemon,
+               "Failed to allocate memory for auth header processing\n");
+#endif /* HAVE_MESSAGES */
+      return MHD_NO;
+    }
+    if (0 == lookup_sub_value (uri,
+                               left + 1,
+                               header, "uri"))
+    {
+      free(uri);
+      return MHD_NO;
+    }
+
+    /* 8 = 4 hexadecimal numbers for the timestamp */
+    nonce_time = strtoul (nonce + len - 8, (char **)NULL, 16);
+    t = (uint32_t) MHD_monotonic_time();
+    /*
+     * First level vetting for the nonce validity: if the timestamp
+     * attached to the nonce exceeds `nonce_timeout', then the nonce is
+     * invalid.
+     */
+    if ( (t > nonce_time + nonce_timeout) ||
+	 (nonce_time + nonce_timeout < nonce_time) )
+    { 
+      free(uri);
+      return MHD_INVALID_NONCE;
+    }
+    if (0 != strncmp (uri,
+		      connection->url,
+		      strlen (connection->url)))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+		"Authentication failed, URI does not match.\n");
+#endif
+      free(uri);
+      return MHD_NO;
+    }
+    {
+      const char *args = strchr (uri, '?');
+
+      if (NULL == args)
+	args = "";
+      else
+	args++;
+      if (MHD_YES !=
+	  check_argument_match (connection,
+				args) )
+      {
+#if HAVE_MESSAGES
+	MHD_DLOG (connection->daemon,
+		  "Authentication failed, arguments do not match.\n");
+#endif
+       free(uri);
+       return MHD_NO;
+      }
+    }
+    calculate_nonce (nonce_time,
+		     connection->method,
+		     connection->daemon->digest_auth_random,
+		     connection->daemon->digest_auth_rand_size,
+		     connection->url,
+		     realm,
+		     noncehashexp);
+    /*
+     * Second level vetting for the nonce validity
+     * if the timestamp attached to the nonce is valid
+     * and possibly fabricated (in case of an attack)
+     * the attacker must also know the random seed to be
+     * able to generate a "sane" nonce, which if he does
+     * not, the nonce fabrication process going to be
+     * very hard to achieve.
+     */
+
+    if (0 != strcmp (nonce, noncehashexp))
+    {
+      free(uri);
+      return MHD_INVALID_NONCE;
+    }
+    if ( (0 == lookup_sub_value (cnonce,
+				 sizeof (cnonce),
+				 header, "cnonce")) ||
+	 (0 == lookup_sub_value (qop, sizeof (qop), header, "qop")) ||
+	 ( (0 != strcmp (qop, "auth")) &&
+	   (0 != strcmp (qop, "")) ) ||
+	 (0 == lookup_sub_value (nc, sizeof (nc), header, "nc"))  ||
+	 (0 == lookup_sub_value (response, sizeof (response), header, "response")) )
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+		"Authentication failed, invalid format.\n");
+#endif
+      free(uri);
+      return MHD_NO;
+    }
+    nci = strtoul (nc, &end, 16);
+    if ( ('\0' != *end) ||
+	 ( (LONG_MAX == nci) &&
+	   (ERANGE == errno) ) )
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+		"Authentication failed, invalid format.\n");
+#endif
+      free(uri);
+      return MHD_NO; /* invalid nonce format */
+    }
+    /*
+     * Checking if that combination of nonce and nc is sound
+     * and not a replay attack attempt. Also adds the nonce
+     * to the nonce-nc map if it does not exist there.
+     */
+
+    if (MHD_YES != check_nonce_nc (connection, nonce, nci))
+    {
+      free(uri);
+      return MHD_NO;
+    }
+
+    digest_calc_ha1("md5",
+		    username,
+		    realm,
+		    password,
+		    nonce,
+		    cnonce,
+		    ha1);
+    digest_calc_response (ha1,
+			  nonce,
+			  nc,
+			  cnonce,
+			  qop,
+			  connection->method,
+			  uri,
+			  hentity,
+			  respexp);
+    free(uri);
+    return (0 == strcmp(response, respexp))
+      ? MHD_YES
+      : MHD_NO;
+  }
+}
+
+
+/**
+ * Queues a response to request authentication from the client
+ *
+ * @param connection The MHD connection structure
+ * @param realm the realm presented to the client
+ * @param opaque string to user for opaque value
+ * @param response reply to send; should contain the "access denied"
+ *        body; note that this function will set the "WWW Authenticate"
+ *        header and that the caller should not do this
+ * @param signal_stale #MHD_YES if the nonce is invalid to add
+ * 			'stale=true' to the authentication header
+ * @return #MHD_YES on success, #MHD_NO otherwise
+ * @ingroup authentication
+ */
+int
+MHD_queue_auth_fail_response (struct MHD_Connection *connection,
+			      const char *realm,
+			      const char *opaque,
+			      struct MHD_Response *response,
+			      int signal_stale)
+{
+  int ret;
+  size_t hlen;
+  char nonce[HASH_MD5_HEX_LEN + 9];
+
+  /* Generating the server nonce */
+  calculate_nonce ((uint32_t) MHD_monotonic_time(),
+		   connection->method,
+		   connection->daemon->digest_auth_random,
+		   connection->daemon->digest_auth_rand_size,
+		   connection->url,
+		   realm,
+		   nonce);
+  if (MHD_YES != check_nonce_nc (connection, nonce, 0))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+		"Could not register nonce (is the nonce array size zero?).\n");
+#endif
+      return MHD_NO;
+    }
+  /* Building the authentication header */
+  hlen = MHD_snprintf_(NULL,
+		   0,
+		   "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
+		   realm,
+		   nonce,
+		   opaque,
+		   signal_stale
+		   ? ",stale=\"true\""
+		   : "");
+  {
+    char *header;
+    
+    header = malloc(hlen + 1);
+    if (NULL == header)
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG(connection->daemon,
+               "Failed to allocate memory for auth response header\n");
+#endif /* HAVE_MESSAGES */
+      return MHD_NO;
+    }
+
+    MHD_snprintf_(header,
+	      hlen + 1,
+	      "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
+	      realm,
+	      nonce,
+	      opaque,
+	      signal_stale
+	      ? ",stale=\"true\""
+	      : "");
+    ret = MHD_add_response_header(response,
+				  MHD_HTTP_HEADER_WWW_AUTHENTICATE,
+				  header);
+    free(header);
+  }
+  if (MHD_YES == ret)
+    ret = MHD_queue_response(connection,
+			     MHD_HTTP_UNAUTHORIZED,
+			     response);
+  return ret;
+}
+
+
+/* end of digestauth.c */
diff --git a/src/microhttpd/internal.c b/src/microhttpd/internal.c
new file mode 100644
index 0000000..6a9188f
--- /dev/null
+++ b/src/microhttpd/internal.c
@@ -0,0 +1,195 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Daniel Pittman and Christian Grothoff
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file microhttpd/internal.c
+ * @brief  internal shared structures
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ */
+
+#include "internal.h"
+
+#if HAVE_MESSAGES
+#if DEBUG_STATES
+/**
+ * State to string dictionary.
+ */
+const char *
+MHD_state_to_string (enum MHD_CONNECTION_STATE state)
+{
+  switch (state)
+    {
+    case MHD_CONNECTION_INIT:
+      return "connection init";
+    case MHD_CONNECTION_URL_RECEIVED:
+      return "connection url received";
+    case MHD_CONNECTION_HEADER_PART_RECEIVED:
+      return "header partially received";
+    case MHD_CONNECTION_HEADERS_RECEIVED:
+      return "headers received";
+    case MHD_CONNECTION_HEADERS_PROCESSED:
+      return "headers processed";
+    case MHD_CONNECTION_CONTINUE_SENDING:
+      return "continue sending";
+    case MHD_CONNECTION_CONTINUE_SENT:
+      return "continue sent";
+    case MHD_CONNECTION_BODY_RECEIVED:
+      return "body received";
+    case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+      return "footer partially received";
+    case MHD_CONNECTION_FOOTERS_RECEIVED:
+      return "footers received";
+    case MHD_CONNECTION_HEADERS_SENDING:
+      return "headers sending";
+    case MHD_CONNECTION_HEADERS_SENT:
+      return "headers sent";
+    case MHD_CONNECTION_NORMAL_BODY_READY:
+      return "normal body ready";
+    case MHD_CONNECTION_NORMAL_BODY_UNREADY:
+      return "normal body unready";
+    case MHD_CONNECTION_CHUNKED_BODY_READY:
+      return "chunked body ready";
+    case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
+      return "chunked body unready";
+    case MHD_CONNECTION_BODY_SENT:
+      return "body sent";
+    case MHD_CONNECTION_FOOTERS_SENDING:
+      return "footers sending";
+    case MHD_CONNECTION_FOOTERS_SENT:
+      return "footers sent";
+    case MHD_CONNECTION_CLOSED:
+      return "closed";
+    case MHD_TLS_CONNECTION_INIT:
+      return "secure connection init";
+    default:
+      return "unrecognized connection state";
+    }
+}
+#endif
+#endif
+
+#if HAVE_MESSAGES
+/**
+ * fprintf-like helper function for logging debug
+ * messages.
+ */
+void
+MHD_DLOG (const struct MHD_Daemon *daemon, const char *format, ...)
+{
+  va_list va;
+
+  if (0 == (daemon->options & MHD_USE_DEBUG))
+    return;
+  va_start (va, format);
+  daemon->custom_error_log (daemon->custom_error_log_cls, format, va);
+  va_end (va);
+}
+#endif
+
+
+/**
+ * Convert all occurences of '+' to ' '.
+ *
+ * @param arg string that is modified (in place), must be 0-terminated
+ */
+void
+MHD_unescape_plus (char *arg)
+{
+  char *p;
+
+  for (p=strchr (arg, '+'); NULL != p; p = strchr (p + 1, '+'))
+    *p = ' ';
+}
+
+
+/**
+ * Process escape sequences ('%HH') Updates val in place; the
+ * result should be UTF-8 encoded and cannot be larger than the input.
+ * The result must also still be 0-terminated.
+ *
+ * @param val value to unescape (modified in the process)
+ * @return length of the resulting val (strlen(val) maybe
+ *  shorter afterwards due to elimination of escape sequences)
+ */
+size_t
+MHD_http_unescape (char *val)
+{
+  char *rpos = val;
+  char *wpos = val;
+  char *end;
+  unsigned int num;
+  char buf3[3];
+
+  while ('\0' != *rpos)
+    {
+      switch (*rpos)
+	{
+	case '%':
+          if ( ('\0' == rpos[1]) ||
+               ('\0' == rpos[2]) )
+          {
+            *wpos = '\0';
+            return wpos - val;
+          }
+	  buf3[0] = rpos[1];
+	  buf3[1] = rpos[2];
+	  buf3[2] = '\0';
+	  num = strtoul (buf3, &end, 16);
+	  if ('\0' == *end)
+	    {
+	      *wpos = (char)((unsigned char) num);
+	      wpos++;
+	      rpos += 3;
+	      break;
+	    }
+	  /* intentional fall through! */
+	default:
+	  *wpos = *rpos;
+	  wpos++;
+	  rpos++;
+	}
+    }
+  *wpos = '\0'; /* add 0-terminator */
+  return wpos - val; /* = strlen(val) */
+}
+
+
+/**
+ * Equivalent to time(NULL) but tries to use some sort of monotonic
+ * clock that isn't affected by someone setting the system real time
+ * clock.
+ *
+ * @return 'current' time
+ */
+time_t
+MHD_monotonic_time (void)
+{
+#ifdef HAVE_CLOCK_GETTIME
+#ifdef CLOCK_MONOTONIC
+  struct timespec ts;
+
+  if (0 == clock_gettime (CLOCK_MONOTONIC, &ts))
+    return ts.tv_sec;
+#endif
+#endif
+  return time (NULL);
+}
+
+/* end of internal.c */
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h
new file mode 100644
index 0000000..286aee6
--- /dev/null
+++ b/src/microhttpd/internal.h
@@ -0,0 +1,1459 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2007-2015 Daniel Pittman and Christian Grothoff
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file microhttpd/internal.h
+ * @brief  internal shared structures
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ */
+
+#ifndef INTERNAL_H
+#define INTERNAL_H
+
+#include "platform.h"
+#include "microhttpd.h"
+#include "platform_interface.h"
+#if HTTPS_SUPPORT
+#include <gnutls/gnutls.h>
+#if GNUTLS_VERSION_MAJOR >= 3
+#include <gnutls/abstract.h>
+#endif
+#endif
+#if EPOLL_SUPPORT
+#include <sys/epoll.h>
+#endif
+#if HAVE_NETINET_TCP_H
+/* for TCP_FASTOPEN */
+#include <netinet/tcp.h>
+#endif
+
+
+/**
+ * Should we perform additional sanity checks at runtime (on our internal
+ * invariants)?  This may lead to aborts, but can be useful for debugging.
+ */
+#define EXTRA_CHECKS MHD_NO
+
+#define MHD_MAX(a,b) ((a)<(b)) ? (b) : (a)
+#define MHD_MIN(a,b) ((a)<(b)) ? (a) : (b)
+
+
+/**
+ * Minimum size by which MHD tries to increment read/write buffers.
+ * We usually begin with half the available pool space for the
+ * IO-buffer, but if absolutely needed we additively grow by the
+ * number of bytes given here (up to -- theoretically -- the full pool
+ * space).
+ */
+#define MHD_BUF_INC_SIZE 1024
+
+
+/**
+ * Handler for fatal errors.
+ */
+extern MHD_PanicCallback mhd_panic;
+
+/**
+ * Closure argument for "mhd_panic".
+ */
+extern void *mhd_panic_cls;
+
+/* If we have Clang or gcc >= 4.5, use __buildin_unreachable() */
+#if defined(__clang__) || (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+#define BUILTIN_NOT_REACHED __builtin_unreachable()
+#else
+#define BUILTIN_NOT_REACHED
+#endif
+
+
+#if HAVE_MESSAGES
+/**
+ * Trigger 'panic' action based on fatal errors.
+ *
+ * @param msg error message (const char *)
+ */
+#define MHD_PANIC(msg) do { mhd_panic (mhd_panic_cls, __FILE__, __LINE__, msg); BUILTIN_NOT_REACHED; } while (0)
+#else
+/**
+ * Trigger 'panic' action based on fatal errors.
+ *
+ * @param msg error message (const char *)
+ */
+#define MHD_PANIC(msg) do { mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL); BUILTIN_NOT_REACHED; } while (0)
+#endif
+
+
+/**
+ * State of the socket with respect to epoll (bitmask).
+ */
+enum MHD_EpollState
+  {
+
+    /**
+     * The socket is not involved with a defined state in epoll right
+     * now.
+     */
+    MHD_EPOLL_STATE_UNREADY = 0,
+
+    /**
+     * epoll told us that data was ready for reading, and we did
+     * not consume all of it yet.
+     */
+    MHD_EPOLL_STATE_READ_READY = 1,
+
+    /**
+     * epoll told us that space was available for writing, and we did
+     * not consume all of it yet.
+     */
+    MHD_EPOLL_STATE_WRITE_READY = 2,
+
+    /**
+     * Is this connection currently in the 'eready' EDLL?
+     */
+    MHD_EPOLL_STATE_IN_EREADY_EDLL = 4,
+
+    /**
+     * Is this connection currently in the 'epoll' set?
+     */
+    MHD_EPOLL_STATE_IN_EPOLL_SET = 8,
+
+    /**
+     * Is this connection currently suspended?
+     */
+    MHD_EPOLL_STATE_SUSPENDED = 16
+  };
+
+
+/**
+ * What is this connection waiting for?
+ */
+enum MHD_ConnectionEventLoopInfo
+  {
+    /**
+     * We are waiting to be able to read.
+     */
+    MHD_EVENT_LOOP_INFO_READ = 0,
+
+    /**
+     * We are waiting to be able to write.
+     */
+    MHD_EVENT_LOOP_INFO_WRITE = 1,
+
+    /**
+     * We are waiting for the application to provide data.
+     */
+    MHD_EVENT_LOOP_INFO_BLOCK = 2,
+
+    /**
+     * We are finished and are awaiting cleanup.
+     */
+    MHD_EVENT_LOOP_INFO_CLEANUP = 3
+  };
+
+
+/**
+ * Maximum length of a nonce in digest authentication.  32(MD5 Hex) +
+ * 8(Timestamp Hex) + 1(NULL); hence 41 should suffice, but Opera
+ * (already) takes more (see Mantis #1633), so we've increased the
+ * value to support something longer...
+ */
+#define MAX_NONCE_LENGTH 129
+
+
+/**
+ * A structure representing the internal holder of the
+ * nonce-nc map.
+ */
+struct MHD_NonceNc
+{
+
+  /**
+   * Nonce counter, a value that increases for each subsequent
+   * request for the same nonce.
+   */
+  unsigned long int nc;
+
+  /**
+   * Nonce value:
+   */
+  char nonce[MAX_NONCE_LENGTH];
+
+};
+
+#if HAVE_MESSAGES
+/**
+ * fprintf-like helper function for logging debug
+ * messages.
+ */
+void
+MHD_DLOG (const struct MHD_Daemon *daemon,
+	  const char *format, ...);
+#endif
+
+
+/**
+ * Header or cookie in HTTP request or response.
+ */
+struct MHD_HTTP_Header
+{
+  /**
+   * Headers are kept in a linked list.
+   */
+  struct MHD_HTTP_Header *next;
+
+  /**
+   * The name of the header (key), without
+   * the colon.
+   */
+  char *header;
+
+  /**
+   * The value of the header.
+   */
+  char *value;
+
+  /**
+   * Type of the header (where in the HTTP
+   * protocol is this header from).
+   */
+  enum MHD_ValueKind kind;
+
+};
+
+
+/**
+ * Representation of a response.
+ */
+struct MHD_Response
+{
+
+  /**
+   * Headers to send for the response.  Initially
+   * the linked list is created in inverse order;
+   * the order should be inverted before sending!
+   */
+  struct MHD_HTTP_Header *first_header;
+
+  /**
+   * Buffer pointing to data that we are supposed
+   * to send as a response.
+   */
+  char *data;
+
+  /**
+   * Closure to give to the content reader @e crc
+   * and content reader free callback @e crfc.
+   */
+  void *crc_cls;
+
+  /**
+   * How do we get more data?  NULL if we are
+   * given all of the data up front.
+   */
+  MHD_ContentReaderCallback crc;
+
+  /**
+   * NULL if data must not be freed, otherwise
+   * either user-specified callback or "&free".
+   */
+  MHD_ContentReaderFreeCallback crfc;
+
+  /**
+   * Mutex to synchronize access to @e data, @e size and
+   * @e reference_count.
+   */
+  MHD_mutex_ mutex;
+
+  /**
+   * Set to #MHD_SIZE_UNKNOWN if size is not known.
+   */
+  uint64_t total_size;
+
+  /**
+   * At what offset in the stream is the
+   * beginning of @e data located?
+   */
+  uint64_t data_start;
+
+  /**
+   * Offset to start reading from when using @e fd.
+   */
+  off_t fd_off;
+
+  /**
+   * Number of bytes ready in @e data (buffer may be larger
+   * than what is filled with payload).
+   */
+  size_t data_size;
+
+  /**
+   * Size of the data buffer @e data.
+   */
+  size_t data_buffer_size;
+
+  /**
+   * Reference count for this response.  Free
+   * once the counter hits zero.
+   */
+  unsigned int reference_count;
+
+  /**
+   * File-descriptor if this response is FD-backed.
+   */
+  int fd;
+
+  /**
+   * Flags set for the MHD response.
+   */
+  enum MHD_ResponseFlags flags;
+
+};
+
+
+/**
+ * States in a state machine for a connection.
+ *
+ * Transitions are any-state to CLOSED, any state to state+1,
+ * FOOTERS_SENT to INIT.  CLOSED is the terminal state and
+ * INIT the initial state.
+ *
+ * Note that transitions for *reading* happen only after
+ * the input has been processed; transitions for
+ * *writing* happen after the respective data has been
+ * put into the write buffer (the write does not have
+ * to be completed yet).  A transition to CLOSED or INIT
+ * requires the write to be complete.
+ */
+enum MHD_CONNECTION_STATE
+{
+  /**
+   * Connection just started (no headers received).
+   * Waiting for the line with the request type, URL and version.
+   */
+  MHD_CONNECTION_INIT = 0,
+
+  /**
+   * 1: We got the URL (and request type and version).  Wait for a header line.
+   */
+  MHD_CONNECTION_URL_RECEIVED = MHD_CONNECTION_INIT + 1,
+
+  /**
+   * 2: We got part of a multi-line request header.  Wait for the rest.
+   */
+  MHD_CONNECTION_HEADER_PART_RECEIVED = MHD_CONNECTION_URL_RECEIVED + 1,
+
+  /**
+   * 3: We got the request headers.  Process them.
+   */
+  MHD_CONNECTION_HEADERS_RECEIVED = MHD_CONNECTION_HEADER_PART_RECEIVED + 1,
+
+  /**
+   * 4: We have processed the request headers.  Send 100 continue.
+   */
+  MHD_CONNECTION_HEADERS_PROCESSED = MHD_CONNECTION_HEADERS_RECEIVED + 1,
+
+  /**
+   * 5: We have processed the headers and need to send 100 CONTINUE.
+   */
+  MHD_CONNECTION_CONTINUE_SENDING = MHD_CONNECTION_HEADERS_PROCESSED + 1,
+
+  /**
+   * 6: We have sent 100 CONTINUE (or do not need to).  Read the message body.
+   */
+  MHD_CONNECTION_CONTINUE_SENT = MHD_CONNECTION_CONTINUE_SENDING + 1,
+
+  /**
+   * 7: We got the request body.  Wait for a line of the footer.
+   */
+  MHD_CONNECTION_BODY_RECEIVED = MHD_CONNECTION_CONTINUE_SENT + 1,
+
+  /**
+   * 8: We got part of a line of the footer.  Wait for the
+   * rest.
+   */
+  MHD_CONNECTION_FOOTER_PART_RECEIVED = MHD_CONNECTION_BODY_RECEIVED + 1,
+
+  /**
+   * 9: We received the entire footer.  Wait for a response to be queued
+   * and prepare the response headers.
+   */
+  MHD_CONNECTION_FOOTERS_RECEIVED = MHD_CONNECTION_FOOTER_PART_RECEIVED + 1,
+
+  /**
+   * 10: We have prepared the response headers in the writ buffer.
+   * Send the response headers.
+   */
+  MHD_CONNECTION_HEADERS_SENDING = MHD_CONNECTION_FOOTERS_RECEIVED + 1,
+
+  /**
+   * 11: We have sent the response headers.  Get ready to send the body.
+   */
+  MHD_CONNECTION_HEADERS_SENT = MHD_CONNECTION_HEADERS_SENDING + 1,
+
+  /**
+   * 12: We are ready to send a part of a non-chunked body.  Send it.
+   */
+  MHD_CONNECTION_NORMAL_BODY_READY = MHD_CONNECTION_HEADERS_SENT + 1,
+
+  /**
+   * 13: We are waiting for the client to provide more
+   * data of a non-chunked body.
+   */
+  MHD_CONNECTION_NORMAL_BODY_UNREADY = MHD_CONNECTION_NORMAL_BODY_READY + 1,
+
+  /**
+   * 14: We are ready to send a chunk.
+   */
+  MHD_CONNECTION_CHUNKED_BODY_READY = MHD_CONNECTION_NORMAL_BODY_UNREADY + 1,
+
+  /**
+   * 15: We are waiting for the client to provide a chunk of the body.
+   */
+  MHD_CONNECTION_CHUNKED_BODY_UNREADY = MHD_CONNECTION_CHUNKED_BODY_READY + 1,
+
+  /**
+   * 16: We have sent the response body. Prepare the footers.
+   */
+  MHD_CONNECTION_BODY_SENT = MHD_CONNECTION_CHUNKED_BODY_UNREADY + 1,
+
+  /**
+   * 17: We have prepared the response footer.  Send it.
+   */
+  MHD_CONNECTION_FOOTERS_SENDING = MHD_CONNECTION_BODY_SENT + 1,
+
+  /**
+   * 18: We have sent the response footer.  Shutdown or restart.
+   */
+  MHD_CONNECTION_FOOTERS_SENT = MHD_CONNECTION_FOOTERS_SENDING + 1,
+
+  /**
+   * 19: This connection is to be closed.
+   */
+  MHD_CONNECTION_CLOSED = MHD_CONNECTION_FOOTERS_SENT + 1,
+
+  /**
+   * 20: This connection is finished (only to be freed)
+   */
+  MHD_CONNECTION_IN_CLEANUP = MHD_CONNECTION_CLOSED + 1,
+
+  /*
+   *  SSL/TLS connection states
+   */
+
+  /**
+   * The initial connection state for all secure connectoins
+   * Handshake messages will be processed in this state & while
+   * in the 'MHD_TLS_HELLO_REQUEST' state
+   */
+  MHD_TLS_CONNECTION_INIT = MHD_CONNECTION_IN_CLEANUP + 1
+
+};
+
+/**
+ * Should all state transitions be printed to stderr?
+ */
+#define DEBUG_STATES MHD_NO
+
+
+#if HAVE_MESSAGES
+#if DEBUG_STATES
+const char *
+MHD_state_to_string (enum MHD_CONNECTION_STATE state);
+#endif
+#endif
+
+/**
+ * Function to receive plaintext data.
+ *
+ * @param conn the connection struct
+ * @param write_to where to write received data
+ * @param max_bytes maximum number of bytes to receive
+ * @return number of bytes written to write_to
+ */
+typedef ssize_t
+(*ReceiveCallback) (struct MHD_Connection *conn,
+                    void *write_to,
+                    size_t max_bytes);
+
+
+/**
+ * Function to transmit plaintext data.
+ *
+ * @param conn the connection struct
+ * @param read_from where to read data to transmit
+ * @param max_bytes maximum number of bytes to transmit
+ * @return number of bytes transmitted
+ */
+typedef ssize_t
+(*TransmitCallback) (struct MHD_Connection *conn,
+                     const void *write_to,
+                     size_t max_bytes);
+
+
+/**
+ * State kept for each HTTP request.
+ */
+struct MHD_Connection
+{
+
+#if EPOLL_SUPPORT
+  /**
+   * Next pointer for the EDLL listing connections that are epoll-ready.
+   */
+  struct MHD_Connection *nextE;
+
+  /**
+   * Previous pointer for the EDLL listing connections that are epoll-ready.
+   */
+  struct MHD_Connection *prevE;
+#endif
+
+  /**
+   * Next pointer for the DLL describing our IO state.
+   */
+  struct MHD_Connection *next;
+
+  /**
+   * Previous pointer for the DLL describing our IO state.
+   */
+  struct MHD_Connection *prev;
+
+  /**
+   * Next pointer for the XDLL organizing connections by timeout.
+   * This DLL can be either the
+   * 'manual_timeout_head/manual_timeout_tail' or the
+   * 'normal_timeout_head/normal_timeout_tail', depending on whether a
+   * custom timeout is set for the connection.
+   */
+  struct MHD_Connection *nextX;
+
+  /**
+   * Previous pointer for the XDLL organizing connections by timeout.
+   */
+  struct MHD_Connection *prevX;
+
+  /**
+   * Reference to the MHD_Daemon struct.
+   */
+  struct MHD_Daemon *daemon;
+
+  /**
+   * Linked list of parsed headers.
+   */
+  struct MHD_HTTP_Header *headers_received;
+
+  /**
+   * Tail of linked list of parsed headers.
+   */
+  struct MHD_HTTP_Header *headers_received_tail;
+
+  /**
+   * Response to transmit (initially NULL).
+   */
+  struct MHD_Response *response;
+
+  /**
+   * The memory pool is created whenever we first read
+   * from the TCP stream and destroyed at the end of
+   * each request (and re-created for the next request).
+   * In the meantime, this pointer is NULL.  The
+   * pool is used for all connection-related data
+   * except for the response (which maybe shared between
+   * connections) and the IP address (which persists
+   * across individual requests).
+   */
+  struct MemoryPool *pool;
+
+  /**
+   * We allow the main application to associate some pointer with the
+   * HTTP request, which is passed to each #MHD_AccessHandlerCallback
+   * and some other API calls.  Here is where we store it.  (MHD does
+   * not know or care what it is).
+   */
+  void *client_context;
+
+  /**
+   * We allow the main application to associate some pointer with the
+   * TCP connection (which may span multiple HTTP requests).  Here is
+   * where we store it.  (MHD does not know or care what it is).
+   * The location is given to the #MHD_NotifyConnectionCallback and
+   * also accessible via #MHD_CONNECTION_INFO_SOCKET_CONTEXT.
+   */
+  void *socket_context;
+
+  /**
+   * Request method.  Should be GET/POST/etc.  Allocated
+   * in pool.
+   */
+  char *method;
+
+  /**
+   * Requested URL (everything after "GET" only).  Allocated
+   * in pool.
+   */
+  char *url;
+
+  /**
+   * HTTP version string (i.e. http/1.1).  Allocated
+   * in pool.
+   */
+  char *version;
+
+  /**
+   * Buffer for reading requests.   Allocated
+   * in pool.  Actually one byte larger than
+   * @e read_buffer_size (if non-NULL) to allow for
+   * 0-termination.
+   */
+  char *read_buffer;
+
+  /**
+   * Buffer for writing response (headers only).  Allocated
+   * in pool.
+   */
+  char *write_buffer;
+
+  /**
+   * Last incomplete header line during parsing of headers.
+   * Allocated in pool.  Only valid if state is
+   * either #MHD_CONNECTION_HEADER_PART_RECEIVED or
+   * #MHD_CONNECTION_FOOTER_PART_RECEIVED.
+   */
+  char *last;
+
+  /**
+   * Position after the colon on the last incomplete header
+   * line during parsing of headers.
+   * Allocated in pool.  Only valid if state is
+   * either #MHD_CONNECTION_HEADER_PART_RECEIVED or
+   * #MHD_CONNECTION_FOOTER_PART_RECEIVED.
+   */
+  char *colon;
+
+  /**
+   * Foreign address (of length @e addr_len).  MALLOCED (not
+   * in pool!).
+   */
+  struct sockaddr *addr;
+
+  /**
+   * Thread handle for this connection (if we are using
+   * one thread per connection).
+   */
+  MHD_thread_handle_ pid;
+
+  /**
+   * Size of read_buffer (in bytes).  This value indicates
+   * how many bytes we're willing to read into the buffer;
+   * the real buffer is one byte longer to allow for
+   * adding zero-termination (when needed).
+   */
+  size_t read_buffer_size;
+
+  /**
+   * Position where we currently append data in
+   * read_buffer (last valid position).
+   */
+  size_t read_buffer_offset;
+
+  /**
+   * Size of write_buffer (in bytes).
+   */
+  size_t write_buffer_size;
+
+  /**
+   * Offset where we are with sending from write_buffer.
+   */
+  size_t write_buffer_send_offset;
+
+  /**
+   * Last valid location in write_buffer (where do we
+   * append and up to where is it safe to send?)
+   */
+  size_t write_buffer_append_offset;
+
+  /**
+   * How many more bytes of the body do we expect
+   * to read? #MHD_SIZE_UNKNOWN for unknown.
+   */
+  uint64_t remaining_upload_size;
+
+  /**
+   * Current write position in the actual response
+   * (excluding headers, content only; should be 0
+   * while sending headers).
+   */
+  uint64_t response_write_position;
+
+  /**
+   * Position in the 100 CONTINUE message that
+   * we need to send when receiving http 1.1 requests.
+   */
+  size_t continue_message_write_offset;
+
+  /**
+   * Length of the foreign address.
+   */
+  socklen_t addr_len;
+
+  /**
+   * Last time this connection had any activity
+   * (reading or writing).
+   */
+  time_t last_activity;
+
+  /**
+   * After how many seconds of inactivity should
+   * this connection time out?  Zero for no timeout.
+   */
+  unsigned int connection_timeout;
+
+  /**
+   * Did we ever call the "default_handler" on this connection?
+   * (this flag will determine if we call the 'notify_completed'
+   * handler when the connection closes down).
+   */
+  int client_aware;
+
+  /**
+   * Socket for this connection.  Set to #MHD_INVALID_SOCKET if
+   * this connection has died (daemon should clean
+   * up in that case).
+   */
+  MHD_socket socket_fd;
+
+  /**
+   * Has this socket been closed for reading (i.e.  other side closed
+   * the connection)?  If so, we must completely close the connection
+   * once we are done sending our response (and stop trying to read
+   * from this socket).
+   */
+  int read_closed;
+
+  /**
+   * Set to #MHD_YES if the thread has been joined.
+   */
+  int thread_joined;
+
+  /**
+   * Are we currently inside the "idle" handler (to avoid recursively invoking it).
+   */
+  int in_idle;
+
+#if EPOLL_SUPPORT
+  /**
+   * What is the state of this socket in relation to epoll?
+   */
+  enum MHD_EpollState epoll_state;
+#endif
+
+  /**
+   * State in the FSM for this connection.
+   */
+  enum MHD_CONNECTION_STATE state;
+
+  /**
+   * What is this connection waiting for?
+   */
+  enum MHD_ConnectionEventLoopInfo event_loop_info;
+
+  /**
+   * HTTP response code.  Only valid if response object
+   * is already set.
+   */
+  unsigned int responseCode;
+
+  /**
+   * Set to MHD_YES if the response's content reader
+   * callback failed to provide data the last time
+   * we tried to read from it.  In that case, the
+   * write socket should be marked as unready until
+   * the CRC call succeeds.
+   */
+  int response_unready;
+
+  /**
+   * Are we receiving with chunked encoding?  This will be set to
+   * MHD_YES after we parse the headers and are processing the body
+   * with chunks.  After we are done with the body and we are
+   * processing the footers; once the footers are also done, this will
+   * be set to MHD_NO again (before the final call to the handler).
+   */
+  int have_chunked_upload;
+
+  /**
+   * If we are receiving with chunked encoding, where are we right
+   * now?  Set to 0 if we are waiting to receive the chunk size;
+   * otherwise, this is the size of the current chunk.  A value of
+   * zero is also used when we're at the end of the chunks.
+   */
+  size_t current_chunk_size;
+
+  /**
+   * If we are receiving with chunked encoding, where are we currently
+   * with respect to the current chunk (at what offset / position)?
+   */
+  size_t current_chunk_offset;
+
+  /**
+   * Handler used for processing read connection operations
+   */
+  int (*read_handler) (struct MHD_Connection *connection);
+
+  /**
+   * Handler used for processing write connection operations
+   */
+  int (*write_handler) (struct MHD_Connection *connection);
+
+  /**
+   * Handler used for processing idle connection operations
+   */
+  int (*idle_handler) (struct MHD_Connection *connection);
+
+  /**
+   * Function used for reading HTTP request stream.
+   */
+  ReceiveCallback recv_cls;
+
+  /**
+   * Function used for writing HTTP response stream.
+   */
+  TransmitCallback send_cls;
+
+#if HTTPS_SUPPORT
+  /**
+   * State required for HTTPS/SSL/TLS support.
+   */
+  gnutls_session_t tls_session;
+
+  /**
+   * Memory location to return for protocol session info.
+   */
+  int protocol;
+
+  /**
+   * Memory location to return for protocol session info.
+   */
+  int cipher;
+
+  /**
+   * Could it be that we are ready to read due to TLS buffers
+   * even though the socket is not?
+   */
+  int tls_read_ready;
+#endif
+
+  /**
+   * Is the connection suspended?
+   */
+  int suspended;
+
+  /**
+   * Is the connection wanting to resume?
+   */
+  int resuming;
+};
+
+/**
+ * Signature of function called to log URI accesses.
+ *
+ * @param cls closure
+ * @param uri uri being accessed
+ * @param con connection handle
+ * @return new closure
+ */
+typedef void *
+(*LogCallback)(void * cls,
+               const char * uri,
+               struct MHD_Connection *con);
+
+/**
+ * Signature of function called to unescape URIs.  See also
+ * #MHD_http_unescape().
+ *
+ * @param cls closure
+ * @param conn connection handle
+ * @param uri 0-terminated string to unescape (should be updated)
+ * @return length of the resulting string
+ */
+typedef size_t
+(*UnescapeCallback)(void *cls,
+                    struct MHD_Connection *conn,
+                    char *uri);
+
+
+/**
+ * State kept for each MHD daemon.  All connections are kept in two
+ * doubly-linked lists.  The first one reflects the state of the
+ * connection in terms of what operations we are waiting for (read,
+ * write, locally blocked, cleanup) whereas the second is about its
+ * timeout state (default or custom).
+ */
+struct MHD_Daemon
+{
+
+  /**
+   * Callback function for all requests.
+   */
+  MHD_AccessHandlerCallback default_handler;
+
+  /**
+   * Closure argument to default_handler.
+   */
+  void *default_handler_cls;
+
+  /**
+   * Head of doubly-linked list of our current, active connections.
+   */
+  struct MHD_Connection *connections_head;
+
+  /**
+   * Tail of doubly-linked list of our current, active connections.
+   */
+  struct MHD_Connection *connections_tail;
+
+  /**
+   * Head of doubly-linked list of our current but suspended connections.
+   */
+  struct MHD_Connection *suspended_connections_head;
+
+  /**
+   * Tail of doubly-linked list of our current but suspended connections.
+   */
+  struct MHD_Connection *suspended_connections_tail;
+
+  /**
+   * Head of doubly-linked list of connections to clean up.
+   */
+  struct MHD_Connection *cleanup_head;
+
+  /**
+   * Tail of doubly-linked list of connections to clean up.
+   */
+  struct MHD_Connection *cleanup_tail;
+
+#if EPOLL_SUPPORT
+  /**
+   * Head of EDLL of connections ready for processing (in epoll mode).
+   */
+  struct MHD_Connection *eready_head;
+
+  /**
+   * Tail of EDLL of connections ready for processing (in epoll mode)
+   */
+  struct MHD_Connection *eready_tail;
+#endif
+
+  /**
+   * Head of the XDLL of ALL connections with a default ('normal')
+   * timeout, sorted by timeout (earliest at the tail, most recently
+   * used connection at the head).  MHD can just look at the tail of
+   * this list to determine the timeout for all of its elements;
+   * whenever there is an event of a connection, the connection is
+   * moved back to the tail of the list.
+   *
+   * All connections by default start in this list; if a custom
+   * timeout that does not match 'connection_timeout' is set, they
+   * are moved to the 'manual_timeout_head'-XDLL.
+   */
+  struct MHD_Connection *normal_timeout_head;
+
+  /**
+   * Tail of the XDLL of ALL connections with a default timeout,
+   * sorted by timeout (earliest timeout at the tail).
+   */
+  struct MHD_Connection *normal_timeout_tail;
+
+  /**
+   * Head of the XDLL of ALL connections with a non-default/custom
+   * timeout, unsorted.  MHD will do a O(n) scan over this list to
+   * determine the current timeout.
+   */
+  struct MHD_Connection *manual_timeout_head;
+
+  /**
+   * Tail of the XDLL of ALL connections with a non-default/custom
+   * timeout, unsorted.
+   */
+  struct MHD_Connection *manual_timeout_tail;
+
+  /**
+   * Function to call to check if we should accept or reject an
+   * incoming request.  May be NULL.
+   */
+  MHD_AcceptPolicyCallback apc;
+
+  /**
+   * Closure argument to apc.
+   */
+  void *apc_cls;
+
+  /**
+   * Function to call when we are done processing
+   * a particular request.  May be NULL.
+   */
+  MHD_RequestCompletedCallback notify_completed;
+
+  /**
+   * Closure argument to notify_completed.
+   */
+  void *notify_completed_cls;
+
+  /**
+   * Function to call when we are starting/stopping
+   * a connection.  May be NULL.
+   */
+  MHD_NotifyConnectionCallback notify_connection;
+
+  /**
+   * Closure argument to notify_connection.
+   */
+  void *notify_connection_cls;
+
+  /**
+   * Function to call with the full URI at the
+   * beginning of request processing.  May be NULL.
+   * <p>
+   * Returns the initial pointer to internal state
+   * kept by the client for the request.
+   */
+  LogCallback uri_log_callback;
+
+  /**
+   * Closure argument to @e uri_log_callback.
+   */
+  void *uri_log_callback_cls;
+
+  /**
+   * Function to call when we unescape escape sequences.
+   */
+  UnescapeCallback unescape_callback;
+
+  /**
+   * Closure for @e unescape_callback.
+   */
+  void *unescape_callback_cls;
+
+#if HAVE_MESSAGES
+  /**
+   * Function for logging error messages (if we
+   * support error reporting).
+   */
+  void (*custom_error_log) (void *cls, const char *fmt, va_list va);
+
+  /**
+   * Closure argument to custom_error_log.
+   */
+  void *custom_error_log_cls;
+#endif
+
+  /**
+   * Pointer to master daemon (NULL if this is the master)
+   */
+  struct MHD_Daemon *master;
+
+  /**
+   * Worker daemons (one per thread)
+   */
+  struct MHD_Daemon *worker_pool;
+
+  /**
+   * Table storing number of connections per IP
+   */
+  void *per_ip_connection_count;
+
+  /**
+   * Size of the per-connection memory pools.
+   */
+  size_t pool_size;
+
+  /**
+   * Increment for growth of the per-connection memory pools.
+   */
+  size_t pool_increment;
+
+  /**
+   * Size of threads created by MHD.
+   */
+  size_t thread_stack_size;
+
+  /**
+   * Number of worker daemons
+   */
+  unsigned int worker_pool_size;
+
+  /**
+   * The select thread handle (if we have internal select)
+   */
+  MHD_thread_handle_ pid;
+
+  /**
+   * Mutex for per-IP connection counts.
+   */
+  MHD_mutex_ per_ip_connection_mutex;
+
+  /**
+   * Mutex for (modifying) access to the "cleanup" connection DLL.
+   */
+  MHD_mutex_ cleanup_connection_mutex;
+
+  /**
+   * Listen socket.
+   */
+  MHD_socket socket_fd;
+
+  /**
+   * Whether to allow/disallow/ignore reuse of listening address.
+   * The semantics is the following:
+   * 0: ignore (user did not ask for neither allow/disallow, use SO_REUSEADDR)
+   * >0: allow (use SO_REUSEPORT on most platforms, SO_REUSEADDR on Windows)
+   * <0: disallow (mostly no action, SO_EXCLUSIVEADDRUSE on Windows)
+   */
+  int listening_address_reuse;
+
+#if EPOLL_SUPPORT
+  /**
+   * File descriptor associated with our epoll loop.
+   */
+  int epoll_fd;
+
+  /**
+   * MHD_YES if the listen socket is in the 'epoll' set,
+   * MHD_NO if not.
+   */
+  int listen_socket_in_epoll;
+#endif
+
+  /**
+   * Pipe we use to signal shutdown, unless
+   * 'HAVE_LISTEN_SHUTDOWN' is defined AND we have a listen
+   * socket (which we can then 'shutdown' to stop listening).
+   * MHD can be build with usage of socketpair instead of
+   * pipe (forced on W32).
+   */
+  MHD_pipe wpipe[2];
+
+  /**
+   * Are we shutting down?
+   */
+  int shutdown;
+
+  /*
+   * Do we need to process resuming connections?
+   */
+  int resuming;
+
+  /**
+   * Number of active parallel connections.
+   */
+  unsigned int connections;
+
+  /**
+   * Limit on the number of parallel connections.
+   */
+  unsigned int connection_limit;
+
+  /**
+   * After how many seconds of inactivity should
+   * connections time out?  Zero for no timeout.
+   */
+  unsigned int connection_timeout;
+
+  /**
+   * Maximum number of connections per IP, or 0 for
+   * unlimited.
+   */
+  unsigned int per_ip_connection_limit;
+
+  /**
+   * Daemon's flags (bitfield).
+   */
+  enum MHD_FLAG options;
+
+  /**
+   * Listen port.
+   */
+  uint16_t port;
+
+#if HTTPS_SUPPORT
+  /**
+   * Desired cipher algorithms.
+   */
+  gnutls_priority_t priority_cache;
+
+  /**
+   * What kind of credentials are we offering
+   * for SSL/TLS?
+   */
+  gnutls_credentials_type_t cred_type;
+
+  /**
+   * Server x509 credentials
+   */
+  gnutls_certificate_credentials_t x509_cred;
+
+  /**
+   * Diffie-Hellman parameters
+   */
+  gnutls_dh_params_t dh_params;
+
+#if GNUTLS_VERSION_MAJOR >= 3
+  /**
+   * Function that can be used to obtain the certificate.  Needed
+   * for SNI support.  See #MHD_OPTION_HTTPS_CERT_CALLBACK.
+   */
+  gnutls_certificate_retrieve_function2 *cert_callback;
+#endif
+
+  /**
+   * Pointer to our SSL/TLS key (in ASCII) in memory.
+   */
+  const char *https_mem_key;
+
+  /**
+   * Pointer to our SSL/TLS certificate (in ASCII) in memory.
+   */
+  const char *https_mem_cert;
+
+  /**
+   * Pointer to 0-terminated HTTPS passphrase in memory.
+   */
+  const char *https_key_password;
+
+  /**
+   * Pointer to our SSL/TLS certificate authority (in ASCII) in memory.
+   */
+  const char *https_mem_trust;
+
+  /**
+   * Our Diffie-Hellman parameters in memory.
+   */
+  gnutls_dh_params_t https_mem_dhparams;
+
+  /**
+   * #MHD_YES if we have initialized @e https_mem_dhparams.
+   */
+  int have_dhparams;
+
+  /**
+   * For how many connections do we have 'tls_read_ready' set to MHD_YES?
+   * Used to avoid O(n) traversal over all connections when determining
+   * event-loop timeout (as it needs to be zero if there is any connection
+   * which might have ready data within TLS).
+   */
+  unsigned int num_tls_read_ready;
+
+#endif
+
+#ifdef DAUTH_SUPPORT
+
+  /**
+   * Character array of random values.
+   */
+  const char *digest_auth_random;
+
+  /**
+   * An array that contains the map nonce-nc.
+   */
+  struct MHD_NonceNc *nnc;
+
+  /**
+   * A rw-lock for synchronizing access to `nnc'.
+   */
+  MHD_mutex_ nnc_lock;
+
+  /**
+   * Size of `digest_auth_random.
+   */
+  size_t digest_auth_rand_size;
+
+  /**
+   * Size of the nonce-nc array.
+   */
+  unsigned int nonce_nc_size;
+
+#endif
+
+#ifdef TCP_FASTOPEN
+  /**
+   * The queue size for incoming SYN + DATA packets.
+   */
+  unsigned int fastopen_queue_size;
+#endif
+};
+
+
+#if EXTRA_CHECKS
+#define EXTRA_CHECK(a) do { if (!(a)) abort(); } while (0)
+#else
+#define EXTRA_CHECK(a)
+#endif
+
+
+/**
+ * Insert an element at the head of a DLL. Assumes that head, tail and
+ * element are structs with prev and next fields.
+ *
+ * @param head pointer to the head of the DLL
+ * @param tail pointer to the tail of the DLL
+ * @param element element to insert
+ */
+#define DLL_insert(head,tail,element) do { \
+  EXTRA_CHECK (NULL == (element)->next); \
+  EXTRA_CHECK (NULL == (element)->prev); \
+  (element)->next = (head); \
+  (element)->prev = NULL; \
+  if ((tail) == NULL) \
+    (tail) = element; \
+  else \
+    (head)->prev = element; \
+  (head) = (element); } while (0)
+
+
+/**
+ * Remove an element from a DLL. Assumes
+ * that head, tail and element are structs
+ * with prev and next fields.
+ *
+ * @param head pointer to the head of the DLL
+ * @param tail pointer to the tail of the DLL
+ * @param element element to remove
+ */
+#define DLL_remove(head,tail,element) do { \
+  EXTRA_CHECK ( (NULL != (element)->next) || ((element) == (tail)));  \
+  EXTRA_CHECK ( (NULL != (element)->prev) || ((element) == (head)));  \
+  if ((element)->prev == NULL) \
+    (head) = (element)->next;  \
+  else \
+    (element)->prev->next = (element)->next; \
+  if ((element)->next == NULL) \
+    (tail) = (element)->prev;  \
+  else \
+    (element)->next->prev = (element)->prev; \
+  (element)->next = NULL; \
+  (element)->prev = NULL; } while (0)
+
+
+
+/**
+ * Insert an element at the head of a XDLL. Assumes that head, tail and
+ * element are structs with prevX and nextX fields.
+ *
+ * @param head pointer to the head of the XDLL
+ * @param tail pointer to the tail of the XDLL
+ * @param element element to insert
+ */
+#define XDLL_insert(head,tail,element) do { \
+  EXTRA_CHECK (NULL == (element)->nextX); \
+  EXTRA_CHECK (NULL == (element)->prevX); \
+  (element)->nextX = (head); \
+  (element)->prevX = NULL; \
+  if (NULL == (tail)) \
+    (tail) = element; \
+  else \
+    (head)->prevX = element; \
+  (head) = (element); } while (0)
+
+
+/**
+ * Remove an element from a XDLL. Assumes
+ * that head, tail and element are structs
+ * with prevX and nextX fields.
+ *
+ * @param head pointer to the head of the XDLL
+ * @param tail pointer to the tail of the XDLL
+ * @param element element to remove
+ */
+#define XDLL_remove(head,tail,element) do { \
+  EXTRA_CHECK ( (NULL != (element)->nextX) || ((element) == (tail)));  \
+  EXTRA_CHECK ( (NULL != (element)->prevX) || ((element) == (head)));  \
+  if (NULL == (element)->prevX) \
+    (head) = (element)->nextX;  \
+  else \
+    (element)->prevX->nextX = (element)->nextX; \
+  if (NULL == (element)->nextX) \
+    (tail) = (element)->prevX;  \
+  else \
+    (element)->nextX->prevX = (element)->prevX; \
+  (element)->nextX = NULL; \
+  (element)->prevX = NULL; } while (0)
+
+
+/**
+ * Insert an element at the head of a EDLL. Assumes that head, tail and
+ * element are structs with prevE and nextE fields.
+ *
+ * @param head pointer to the head of the EDLL
+ * @param tail pointer to the tail of the EDLL
+ * @param element element to insert
+ */
+#define EDLL_insert(head,tail,element) do { \
+  (element)->nextE = (head); \
+  (element)->prevE = NULL; \
+  if ((tail) == NULL) \
+    (tail) = element; \
+  else \
+    (head)->prevE = element; \
+  (head) = (element); } while (0)
+
+
+/**
+ * Remove an element from a EDLL. Assumes
+ * that head, tail and element are structs
+ * with prevE and nextE fields.
+ *
+ * @param head pointer to the head of the EDLL
+ * @param tail pointer to the tail of the EDLL
+ * @param element element to remove
+ */
+#define EDLL_remove(head,tail,element) do { \
+  if ((element)->prevE == NULL) \
+    (head) = (element)->nextE;  \
+  else \
+    (element)->prevE->nextE = (element)->nextE; \
+  if ((element)->nextE == NULL) \
+    (tail) = (element)->prevE;  \
+  else \
+    (element)->nextE->prevE = (element)->prevE; \
+  (element)->nextE = NULL; \
+  (element)->prevE = NULL; } while (0)
+
+
+/**
+ * Equivalent to `time(NULL)` but tries to use some sort of monotonic
+ * clock that isn't affected by someone setting the system real time
+ * clock.
+ *
+ * @return 'current' time
+ */
+time_t
+MHD_monotonic_time(void);
+
+
+/**
+ * Convert all occurences of '+' to ' '.
+ *
+ * @param arg string that is modified (in place), must be 0-terminated
+ */
+void
+MHD_unescape_plus (char *arg);
+
+
+#endif
diff --git a/src/microhttpd/md5.c b/src/microhttpd/md5.c
new file mode 100644
index 0000000..e35d5f2
--- /dev/null
+++ b/src/microhttpd/md5.c
@@ -0,0 +1,267 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.	This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* Brutally hacked by John Walker back from ANSI C to K&R (no
+   prototypes) to maintain the tradition that Netfone will compile
+   with Sun's original "cc". */
+
+#include "md5.h"
+
+
+#ifndef HIGHFIRST
+#define byteReverse(buf, len)	/* Nothing */
+#else
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void
+byteReverse(unsigned char *buf,
+	    unsigned longs)
+{
+    uint32_t t;
+    do {
+	t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+	    ((unsigned) buf[1] << 8 | buf[0]);
+	*(uint32_t *) buf = t;
+	buf += 4;
+    } while (--longs);
+}
+#endif
+
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+MD5Transform(uint32_t buf[4],
+	     uint32_t in[16])
+{
+  uint32_t a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void
+MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void
+MD5Update(struct MD5Context *ctx,
+	  const void *data,
+	  unsigned len)
+{
+    const unsigned char *buf = data;
+    uint32_t t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+	ctx->bits[1]++; 	/* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+	unsigned char *p = (unsigned char *) ctx->in + t;
+
+	t = 64 - t;
+	if (len < t) {
+	    memcpy(p, buf, len);
+	    return;
+	}
+	memcpy(p, buf, t);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+	buf += t;
+	len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+	memcpy(ctx->in, buf, 64);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+	buf += 64;
+	len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void
+MD5Final (unsigned char digest[16],
+          struct MD5Context *ctx)
+{
+  unsigned count;
+  unsigned char *p;
+
+  /* Compute number of bytes mod 64 */
+  count = (ctx->bits[0] >> 3) & 0x3F;
+
+  /* Set the first char of padding to 0x80.  This is safe since there is
+     always at least one byte free */
+  p = ctx->in + count;
+  *p++ = 0x80;
+
+  /* Bytes of padding needed to make 64 bytes */
+  count = 64 - 1 - count;
+
+  /* Pad out to 56 mod 64 */
+  if (count < 8)
+    {
+      /* Two lots of padding:  Pad the first block to 64 bytes */
+      memset(p, 0, count);
+      byteReverse(ctx->in, 16);
+      MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+      /* Now fill the next block with 56 bytes */
+      memset(ctx->in, 0, 56);
+    }
+  else
+    {
+      /* Pad block to 56 bytes */
+      memset(p, 0, count - 8);
+    }
+  byteReverse(ctx->in, 14);
+
+  /* Append length in bits and transform */
+  ((uint32_t *) ctx->in)[14] = ctx->bits[0];
+  ((uint32_t *) ctx->in)[15] = ctx->bits[1];
+
+  MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+  byteReverse((unsigned char *) ctx->buf, 4);
+  memcpy(digest, ctx->buf, 16);
+  memset(ctx, 0, sizeof(struct MD5Context));        /* In case it's sensitive */
+}
+
+/* end of md5.c */
diff --git a/src/microhttpd/md5.h b/src/microhttpd/md5.h
new file mode 100644
index 0000000..5e77774
--- /dev/null
+++ b/src/microhttpd/md5.h
@@ -0,0 +1,52 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.	This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* Brutally hacked by John Walker back from ANSI C to K&R (no
+   prototypes) to maintain the tradition that Netfone will compile
+   with Sun's original "cc". */
+
+#ifndef MD5_H
+#define MD5_H
+
+#include "platform.h"
+#ifdef WORDS_BIGENDIAN
+#define HIGHFIRST
+#endif
+
+#define MD5_DIGEST_SIZE 16
+
+struct MD5Context
+{
+  uint32_t buf[4];
+  uint32_t bits[2];
+  unsigned char in[64];
+};
+
+
+void
+MD5Init(struct MD5Context *ctx);
+
+void
+MD5Update(struct MD5Context *ctx,
+	  const void *buf,
+	  unsigned len);
+
+void
+MD5Final(unsigned char digest[MD5_DIGEST_SIZE],
+         struct MD5Context *ctx);
+
+#endif /* !MD5_H */
diff --git a/src/microhttpd/memorypool.c b/src/microhttpd/memorypool.c
new file mode 100644
index 0000000..2f39931
--- /dev/null
+++ b/src/microhttpd/memorypool.c
@@ -0,0 +1,284 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2009, 2010 Daniel Pittman and Christian Grothoff
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file memorypool.c
+ * @brief memory pool
+ * @author Christian Grothoff
+ */
+#include "memorypool.h"
+
+/* define MAP_ANONYMOUS for Mac OS X */
+#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void*)-1)
+#endif
+
+/**
+ * Align to 2x word size (as GNU libc does).
+ */
+#define ALIGN_SIZE (2 * sizeof(void*))
+
+/**
+ * Round up 'n' to a multiple of ALIGN_SIZE.
+ */
+#define ROUND_TO_ALIGN(n) ((n+(ALIGN_SIZE-1)) & (~(ALIGN_SIZE-1)))
+
+
+/**
+ * Handle for a memory pool.  Pools are not reentrant and must not be
+ * used by multiple threads.
+ */
+struct MemoryPool
+{
+
+  /**
+   * Pointer to the pool's memory
+   */
+  char *memory;
+
+  /**
+   * Size of the pool.
+   */
+  size_t size;
+
+  /**
+   * Offset of the first unallocated byte.
+   */
+  size_t pos;
+
+  /**
+   * Offset of the last unallocated byte.
+   */
+  size_t end;
+
+  /**
+   * #MHD_NO if pool was malloc'ed, #MHD_YES if mmapped (VirtualAlloc'ed for W32).
+   */
+  int is_mmap;
+};
+
+
+/**
+ * Create a memory pool.
+ *
+ * @param max maximum size of the pool
+ * @return NULL on error
+ */
+struct MemoryPool *
+MHD_pool_create (size_t max)
+{
+  struct MemoryPool *pool;
+
+  pool = malloc (sizeof (struct MemoryPool));
+  if (NULL == pool)
+    return NULL;
+#if defined(MAP_ANONYMOUS) || defined(_WIN32)
+  if (max <= 32 * 1024)
+    pool->memory = MAP_FAILED;
+  else
+#if defined(MAP_ANONYMOUS) && !defined(_WIN32)
+    pool->memory = mmap (NULL, max, PROT_READ | PROT_WRITE,
+			 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+#elif defined(_WIN32)
+    pool->memory = VirtualAlloc(NULL, max, MEM_COMMIT | MEM_RESERVE,
+        PAGE_READWRITE);
+#endif
+#else
+  pool->memory = MAP_FAILED;
+#endif
+  if ((pool->memory == MAP_FAILED) || (pool->memory == NULL))
+    {
+      pool->memory = malloc (max);
+      if (pool->memory == NULL)
+        {
+          free (pool);
+          return NULL;
+        }
+      pool->is_mmap = MHD_NO;
+    }
+  else
+    {
+      pool->is_mmap = MHD_YES;
+    }
+  pool->pos = 0;
+  pool->end = max;
+  pool->size = max;
+  return pool;
+}
+
+
+/**
+ * Destroy a memory pool.
+ *
+ * @param pool memory pool to destroy
+ */
+void
+MHD_pool_destroy (struct MemoryPool *pool)
+{
+  if (pool == NULL)
+    return;
+  if (pool->is_mmap == MHD_NO)
+    free (pool->memory);
+  else
+#if defined(MAP_ANONYMOUS) && !defined(_WIN32)
+    munmap (pool->memory, pool->size);
+#elif defined(_WIN32)
+    VirtualFree(pool->memory, 0, MEM_RELEASE);
+#else
+    abort();
+#endif
+  free (pool);
+}
+
+
+/**
+ * Allocate size bytes from the pool.
+ *
+ * @param pool memory pool to use for the operation
+ * @param size number of bytes to allocate
+ * @param from_end allocate from end of pool (set to #MHD_YES);
+ *        use this for small, persistent allocations that
+ *        will never be reallocated
+ * @return NULL if the pool cannot support size more
+ *         bytes
+ */
+void *
+MHD_pool_allocate (struct MemoryPool *pool,
+		   size_t size, int from_end)
+{
+  void *ret;
+  size_t asize;
+
+  asize = ROUND_TO_ALIGN (size);
+  if ( (0 == asize) && (0 != size) )
+    return NULL; /* size too close to SIZE_MAX */
+  if ((pool->pos + asize > pool->end) || (pool->pos + asize < pool->pos))
+    return NULL;
+  if (from_end == MHD_YES)
+    {
+      ret = &pool->memory[pool->end - asize];
+      pool->end -= asize;
+    }
+  else
+    {
+      ret = &pool->memory[pool->pos];
+      pool->pos += asize;
+    }
+  return ret;
+}
+
+
+/**
+ * Reallocate a block of memory obtained from the pool.
+ * This is particularly efficient when growing or
+ * shrinking the block that was last (re)allocated.
+ * If the given block is not the most recenlty
+ * (re)allocated block, the memory of the previous
+ * allocation may be leaked until the pool is
+ * destroyed (and copying the data maybe required).
+ *
+ * @param pool memory pool to use for the operation
+ * @param old the existing block
+ * @param old_size the size of the existing block
+ * @param new_size the new size of the block
+ * @return new address of the block, or
+ *         NULL if the pool cannot support @a new_size
+ *         bytes (old continues to be valid for @a old_size)
+ */
+void *
+MHD_pool_reallocate (struct MemoryPool *pool,
+                     void *old,
+		     size_t old_size,
+		     size_t new_size)
+{
+  void *ret;
+  size_t asize;
+
+  asize = ROUND_TO_ALIGN (new_size);
+  if ( (0 == asize) && (0 != new_size) )
+    return NULL; /* new_size too close to SIZE_MAX */
+  if ((pool->end < old_size) || (pool->end < asize))
+    return NULL;                /* unsatisfiable or bogus request */
+
+  if ((pool->pos >= old_size) && (&pool->memory[pool->pos - old_size] == old))
+    {
+      /* was the previous allocation - optimize! */
+      if (pool->pos + asize - old_size <= pool->end)
+        {
+          /* fits */
+          pool->pos += asize - old_size;
+          if (asize < old_size)      /* shrinking - zero again! */
+            memset (&pool->memory[pool->pos], 0, old_size - asize);
+          return old;
+        }
+      /* does not fit */
+      return NULL;
+    }
+  if (asize <= old_size)
+    return old;                 /* cannot shrink, no need to move */
+  if ((pool->pos + asize >= pool->pos) &&
+      (pool->pos + asize <= pool->end))
+    {
+      /* fits */
+      ret = &pool->memory[pool->pos];
+      memcpy (ret, old, old_size);
+      pool->pos += asize;
+      return ret;
+    }
+  /* does not fit */
+  return NULL;
+}
+
+
+/**
+ * Clear all entries from the memory pool except
+ * for @a keep of the given @a size.
+ *
+ * @param pool memory pool to use for the operation
+ * @param keep pointer to the entry to keep (maybe NULL)
+ * @param size how many bytes need to be kept at this address
+ * @return addr new address of @a keep (if it had to change)
+ */
+void *
+MHD_pool_reset (struct MemoryPool *pool,
+		void *keep,
+		size_t size)
+{
+  if (NULL != keep)
+    {
+      if (keep != pool->memory)
+        {
+          memmove (pool->memory, keep, size);
+          keep = pool->memory;
+        }
+    }
+  pool->end = pool->size;
+  memset (&pool->memory[size],
+	  0,
+	  pool->size - size);
+  if (NULL != keep)
+    pool->pos = ROUND_TO_ALIGN(size);
+  return keep;
+}
+
+
+/* end of memorypool.c */
diff --git a/src/microhttpd/memorypool.h b/src/microhttpd/memorypool.h
new file mode 100644
index 0000000..4db952a
--- /dev/null
+++ b/src/microhttpd/memorypool.h
@@ -0,0 +1,114 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2009 Daniel Pittman and Christian Grothoff
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file memorypool.h
+ * @brief memory pool; mostly used for efficient (de)allocation
+ *        for each connection and bounding memory use for each
+ *        request
+ * @author Christian Grothoff
+ */
+
+#ifndef MEMORYPOOL_H
+#define MEMORYPOOL_H
+
+#include "internal.h"
+
+/**
+ * Opaque handle for a memory pool.
+ * Pools are not reentrant and must not be used
+ * by multiple threads.
+ */
+struct MemoryPool;
+
+
+/**
+ * Create a memory pool.
+ *
+ * @param max maximum size of the pool
+ * @return NULL on error
+ */
+struct MemoryPool *
+MHD_pool_create (size_t max);
+
+
+/**
+ * Destroy a memory pool.
+ *
+ * @param pool memory pool to destroy
+ */
+void
+MHD_pool_destroy (struct MemoryPool *pool);
+
+
+/**
+ * Allocate size bytes from the pool.
+ *
+ * @param pool memory pool to use for the operation
+ * @param size number of bytes to allocate
+ * @param from_end allocate from end of pool (set to MHD_YES);
+ *        use this for small, persistent allocations that
+ *        will never be reallocated
+ * @return NULL if the pool cannot support size more
+ *         bytes
+ */
+void *
+MHD_pool_allocate (struct MemoryPool *pool,
+		   size_t size, int from_end);
+
+
+/**
+ * Reallocate a block of memory obtained from the pool.
+ * This is particularly efficient when growing or
+ * shrinking the block that was last (re)allocated.
+ * If the given block is not the most recenlty
+ * (re)allocated block, the memory of the previous
+ * allocation may be leaked until the pool is
+ * destroyed (and copying the data maybe required).
+ *
+ * @param pool memory pool to use for the operation
+ * @param old the existing block
+ * @param old_size the size of the existing block
+ * @param new_size the new size of the block
+ * @return new address of the block, or
+ *         NULL if the pool cannot support new_size
+ *         bytes (old continues to be valid for old_size)
+ */
+void *
+MHD_pool_reallocate (struct MemoryPool *pool,
+		     void *old,
+		     size_t old_size,
+		     size_t new_size);
+
+
+/**
+ * Clear all entries from the memory pool except
+ * for "keep" of the given "size".
+ *
+ * @param pool memory pool to use for the operation
+ * @param keep pointer to the entry to keep (maybe NULL)
+ * @param size how many bytes need to be kept at this address
+ * @return addr new address of "keep" (if it had to change)
+ */
+void *
+MHD_pool_reset (struct MemoryPool *pool,
+		void *keep,
+		size_t size);
+
+#endif
diff --git a/src/microhttpd/microhttpd_dll_res.rc.in b/src/microhttpd/microhttpd_dll_res.rc.in
new file mode 100644
index 0000000..923f3a9
--- /dev/null
+++ b/src/microhttpd/microhttpd_dll_res.rc.in
@@ -0,0 +1,35 @@
+/* W32 resources for .dll */
+
+#include <winresrc.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+VS_VERSION_INFO VERSIONINFO
+  FILEVERSION @PACKAGE_VERSION_MAJOR@,@PACKAGE_VERSION_MINOR@,@PACKAGE_VERSION_SUBMINOR@,0
+  PRODUCTVERSION @PACKAGE_VERSION_MAJOR@,@PACKAGE_VERSION_MINOR@,@PACKAGE_VERSION_SUBMINOR@,0
+  FILEFLAGSMASK  VS_FFI_FILEFLAGSMASK
+  FILEFLAGS      0
+  FILEOS         VOS_NT_WINDOWS32
+  FILETYPE       VFT_DLL
+  FILESUBTYPE    VFT2_UNKNOWN
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "04090000"  /* Lang = US English, Charset = ASCII */
+        BEGIN
+            VALUE "ProductName", "GNU libmicrohttpd\0"
+            VALUE "ProductVersion", "@PACKAGE_VERSION@\0"
+            VALUE "FileVersion", "@PACKAGE_VERSION@\0"
+            VALUE "FileDescription", "GNU libmicrohttpd dll for Windows (GCC build)\0"
+            VALUE "InternalName", "libmicrohttpd\0"
+            VALUE "OriginalFilename", "libmicrohttpd.dll\0"
+            VALUE "CompanyName", "Free Software Foundation\0"
+            VALUE "LegalCopyright",  "Copyright (C) 2007-2015 Christian Grothoff and project contributors\0"
+            VALUE "Comments", "http://www.gnu.org/software/libmicrohttpd/\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x0409, 0  /* US English, ASCII */
+    END
+END
+
diff --git a/src/microhttpd/postprocessor.c b/src/microhttpd/postprocessor.c
new file mode 100644
index 0000000..d371f3d
--- /dev/null
+++ b/src/microhttpd/postprocessor.c
@@ -0,0 +1,1191 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007-2013 Daniel Pittman and Christian Grothoff
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file postprocessor.c
+ * @brief  Methods for parsing POST data
+ * @author Christian Grothoff
+ */
+
+#include "internal.h"
+
+/**
+ * Size of on-stack buffer that we use for un-escaping of the value.
+ * We use a pretty small value to be nice to the stack on embedded
+ * systems.
+ */
+#define XBUF_SIZE 512
+
+/**
+ * States in the PP parser's state machine.
+ */
+enum PP_State
+{
+  /* general states */
+  PP_Error,
+  PP_Done,
+  PP_Init,
+  PP_NextBoundary,
+
+  /* url encoding-states */
+  PP_ProcessValue,
+  PP_ExpectNewLine,
+
+  /* post encoding-states  */
+  PP_ProcessEntryHeaders,
+  PP_PerformCheckMultipart,
+  PP_ProcessValueToBoundary,
+  PP_PerformCleanup,
+
+  /* nested post-encoding states */
+  PP_Nested_Init,
+  PP_Nested_PerformMarking,
+  PP_Nested_ProcessEntryHeaders,
+  PP_Nested_ProcessValueToBoundary,
+  PP_Nested_PerformCleanup
+
+};
+
+
+enum RN_State
+{
+  /**
+   * No RN-preprocessing in this state.
+   */
+  RN_Inactive = 0,
+
+  /**
+   * If the next character is CR, skip it.  Otherwise,
+   * just go inactive.
+   */
+  RN_OptN = 1,
+
+  /**
+   * Expect LFCR (and only LFCR).  As always, we also
+   * expect only LF or only CR.
+   */
+  RN_Full = 2,
+
+  /**
+   * Expect either LFCR or '--'LFCR.  If '--'LFCR, transition into dash-state
+   * for the main state machine
+   */
+  RN_Dash = 3,
+
+  /**
+   * Got a single dash, expect second dash.
+   */
+  RN_Dash2 = 4
+};
+
+
+/**
+ * Bits for the globally known fields that
+ * should not be deleted when we exit the
+ * nested state.
+ */
+enum NE_State
+{
+  NE_none = 0,
+  NE_content_name = 1,
+  NE_content_type = 2,
+  NE_content_filename = 4,
+  NE_content_transfer_encoding = 8
+};
+
+
+/**
+ * Internal state of the post-processor.  Note that the fields
+ * are sorted by type to enable optimal packing by the compiler.
+ */
+struct MHD_PostProcessor
+{
+
+  /**
+   * The connection for which we are doing
+   * POST processing.
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * Function to call with POST data.
+   */
+  MHD_PostDataIterator ikvi;
+
+  /**
+   * Extra argument to ikvi.
+   */
+  void *cls;
+
+  /**
+   * Encoding as given by the headers of the
+   * connection.
+   */
+  const char *encoding;
+
+  /**
+   * Primary boundary (points into encoding string)
+   */
+  const char *boundary;
+
+  /**
+   * Nested boundary (if we have multipart/mixed encoding).
+   */
+  char *nested_boundary;
+
+  /**
+   * Pointer to the name given in disposition.
+   */
+  char *content_name;
+
+  /**
+   * Pointer to the (current) content type.
+   */
+  char *content_type;
+
+  /**
+   * Pointer to the (current) filename.
+   */
+  char *content_filename;
+
+  /**
+   * Pointer to the (current) encoding.
+   */
+  char *content_transfer_encoding;
+
+  /**
+   * Unprocessed value bytes due to escape
+   * sequences (URL-encoding only).
+   */
+  char xbuf[8];
+
+  /**
+   * Size of our buffer for the key.
+   */
+  size_t buffer_size;
+
+  /**
+   * Current position in the key buffer.
+   */
+  size_t buffer_pos;
+
+  /**
+   * Current position in xbuf.
+   */
+  size_t xbuf_pos;
+
+  /**
+   * Current offset in the value being processed.
+   */
+  uint64_t value_offset;
+
+  /**
+   * strlen(boundary) -- if boundary != NULL.
+   */
+  size_t blen;
+
+  /**
+   * strlen(nested_boundary) -- if nested_boundary != NULL.
+   */
+  size_t nlen;
+
+  /**
+   * Do we have to call the 'ikvi' callback when processing the
+   * multipart post body even if the size of the payload is zero?
+   * Set to #MHD_YES whenever we parse a new multiparty entry header,
+   * and to #MHD_NO the first time we call the 'ikvi' callback.
+   * Used to ensure that we do always call 'ikvi' even if the
+   * payload is empty (but not more than once).
+   */
+  int must_ikvi;
+
+  /**
+   * State of the parser.
+   */
+  enum PP_State state;
+
+  /**
+   * Side-state-machine: skip LRCR (or just LF).
+   * Set to 0 if we are not in skip mode.  Set to 2
+   * if a LFCR is expected, set to 1 if a CR should
+   * be skipped if it is the next character.
+   */
+  enum RN_State skip_rn;
+
+  /**
+   * If we are in skip_rn with "dash" mode and
+   * do find 2 dashes, what state do we go into?
+   */
+  enum PP_State dash_state;
+
+  /**
+   * Which headers are global? (used to tell which
+   * headers were only valid for the nested multipart).
+   */
+  enum NE_State have;
+
+};
+
+
+/**
+ * Create a `struct MHD_PostProcessor`.
+ *
+ * A `struct MHD_PostProcessor` can be used to (incrementally) parse
+ * the data portion of a POST request.  Note that some buggy browsers
+ * fail to set the encoding type.  If you want to support those, you
+ * may have to call #MHD_set_connection_value with the proper encoding
+ * type before creating a post processor (if no supported encoding
+ * type is set, this function will fail).
+ *
+ * @param connection the connection on which the POST is
+ *        happening (used to determine the POST format)
+ * @param buffer_size maximum number of bytes to use for
+ *        internal buffering (used only for the parsing,
+ *        specifically the parsing of the keys).  A
+ *        tiny value (256-1024) should be sufficient.
+ *        Do NOT use a value smaller than 256.  For good
+ *        performance, use 32 or 64k (i.e. 65536).
+ * @param iter iterator to be called with the parsed data,
+ *        Must NOT be NULL.
+ * @param iter_cls first argument to @a iter
+ * @return NULL on error (out of memory, unsupported encoding),
+ *         otherwise a PP handle
+ * @ingroup request
+ */
+struct MHD_PostProcessor *
+MHD_create_post_processor (struct MHD_Connection *connection,
+                           size_t buffer_size,
+                           MHD_PostDataIterator iter, void *iter_cls)
+{
+  struct MHD_PostProcessor *ret;
+  const char *encoding;
+  const char *boundary;
+  size_t blen;
+
+  if ((buffer_size < 256) || (connection == NULL) || (iter == NULL))
+    mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL);
+  encoding = MHD_lookup_connection_value (connection,
+                                          MHD_HEADER_KIND,
+                                          MHD_HTTP_HEADER_CONTENT_TYPE);
+  if (encoding == NULL)
+    return NULL;
+  boundary = NULL;
+  if (!MHD_str_equal_caseless_n_ (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, encoding,
+                        strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
+    {
+      if (!MHD_str_equal_caseless_n_ (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, encoding,
+                       strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
+        return NULL;
+      boundary =
+        &encoding[strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)];
+      /* Q: should this be "strcasestr"? */
+      boundary = strstr (boundary, "boundary=");
+      if (NULL == boundary)
+	return NULL; /* failed to determine boundary */
+      boundary += strlen ("boundary=");
+      blen = strlen (boundary);
+      if ((blen == 0) || (blen * 2 + 2 > buffer_size))
+        return NULL;            /* (will be) out of memory or invalid boundary */
+      if ( (boundary[0] == '"') && (boundary[blen - 1] == '"') )
+	{
+	  /* remove enclosing quotes */
+	  ++boundary;
+	  blen -= 2;
+	}
+    }
+  else
+    blen = 0;
+  buffer_size += 4; /* round up to get nice block sizes despite boundary search */
+
+  /* add +1 to ensure we ALWAYS have a zero-termination at the end */
+  if (NULL == (ret = malloc (sizeof (struct MHD_PostProcessor) + buffer_size + 1)))
+    return NULL;
+  memset (ret, 0, sizeof (struct MHD_PostProcessor) + buffer_size + 1);
+  ret->connection = connection;
+  ret->ikvi = iter;
+  ret->cls = iter_cls;
+  ret->encoding = encoding;
+  ret->buffer_size = buffer_size;
+  ret->state = PP_Init;
+  ret->blen = blen;
+  ret->boundary = boundary;
+  ret->skip_rn = RN_Inactive;
+  return ret;
+}
+
+
+/**
+ * Process url-encoded POST data.
+ *
+ * @param pp post processor context
+ * @param post_data upload data
+ * @param post_data_len number of bytes in @a post_data
+ * @return #MHD_YES on success, #MHD_NO if there was an error processing the data
+ */
+static int
+post_process_urlencoded (struct MHD_PostProcessor *pp,
+                         const char *post_data,
+			 size_t post_data_len)
+{
+  size_t equals;
+  size_t amper;
+  size_t poff;
+  size_t xoff;
+  size_t delta;
+  int end_of_value_found;
+  char *buf;
+  char xbuf[XBUF_SIZE + 1];
+
+  buf = (char *) &pp[1];
+  poff = 0;
+  while (poff < post_data_len)
+    {
+      switch (pp->state)
+        {
+        case PP_Error:
+          return MHD_NO;
+        case PP_Done:
+          /* did not expect to receive more data */
+          pp->state = PP_Error;
+          return MHD_NO;
+        case PP_Init:
+          equals = 0;
+          while ((equals + poff < post_data_len) &&
+                 (post_data[equals + poff] != '='))
+            equals++;
+          if (equals + pp->buffer_pos > pp->buffer_size)
+            {
+              pp->state = PP_Error;     /* out of memory */
+              return MHD_NO;
+            }
+          memcpy (&buf[pp->buffer_pos], &post_data[poff], equals);
+          pp->buffer_pos += equals;
+          if (equals + poff == post_data_len)
+            return MHD_YES;     /* no '=' yet */
+          buf[pp->buffer_pos] = '\0';   /* 0-terminate key */
+          pp->buffer_pos = 0;   /* reset for next key */
+	  MHD_unescape_plus (buf);
+          MHD_http_unescape (buf);
+          poff += equals + 1;
+          pp->state = PP_ProcessValue;
+          pp->value_offset = 0;
+          break;
+        case PP_ProcessValue:
+          /* obtain rest of value from previous iteration */
+          memcpy (xbuf, pp->xbuf, pp->xbuf_pos);
+          xoff = pp->xbuf_pos;
+          pp->xbuf_pos = 0;
+
+          /* find last position in input buffer that is part of the value */
+          amper = 0;
+          while ((amper + poff < post_data_len) &&
+                 (amper < XBUF_SIZE) &&
+                 (post_data[amper + poff] != '&') &&
+                 (post_data[amper + poff] != '\n') &&
+                 (post_data[amper + poff] != '\r'))
+            amper++;
+          end_of_value_found = ((amper + poff < post_data_len) &&
+                                ((post_data[amper + poff] == '&') ||
+                                 (post_data[amper + poff] == '\n') ||
+                                 (post_data[amper + poff] == '\r')));
+          /* compute delta, the maximum number of bytes that we will be able to
+             process right now (either amper-limited of xbuf-size limited) */
+          delta = amper;
+          if (delta > XBUF_SIZE - xoff)
+            delta = XBUF_SIZE - xoff;
+
+          /* move input into processing buffer */
+          memcpy (&xbuf[xoff], &post_data[poff], delta);
+          xoff += delta;
+          poff += delta;
+
+          /* find if escape sequence is at the end of the processing buffer;
+             if so, exclude those from processing (reduce delta to point at
+             end of processed region) */
+          delta = xoff;
+          if ((delta > 0) && (xbuf[delta - 1] == '%'))
+            delta--;
+          else if ((delta > 1) && (xbuf[delta - 2] == '%'))
+            delta -= 2;
+
+          /* if we have an incomplete escape sequence, save it to
+             pp->xbuf for later */
+          if (delta < xoff)
+            {
+              memcpy (pp->xbuf, &xbuf[delta], xoff - delta);
+              pp->xbuf_pos = xoff - delta;
+              xoff = delta;
+            }
+
+          /* If we have nothing to do (delta == 0) and
+             not just because the value is empty (are
+             waiting for more data), go for next iteration */
+          if ((xoff == 0) && (poff == post_data_len))
+            continue;
+
+          /* unescape */
+          xbuf[xoff] = '\0';    /* 0-terminate in preparation */
+	  MHD_unescape_plus (xbuf);
+          xoff = MHD_http_unescape (xbuf);
+          /* finally: call application! */
+	  pp->must_ikvi = MHD_NO;
+          if (MHD_NO == pp->ikvi (pp->cls, MHD_POSTDATA_KIND, (const char *) &pp[1],    /* key */
+                                  NULL, NULL, NULL, xbuf, pp->value_offset,
+                                  xoff))
+            {
+              pp->state = PP_Error;
+              return MHD_NO;
+            }
+          pp->value_offset += xoff;
+
+          /* are we done with the value? */
+          if (end_of_value_found)
+            {
+              /* we found the end of the value! */
+              if ((post_data[poff] == '\n') || (post_data[poff] == '\r'))
+                {
+                  pp->state = PP_ExpectNewLine;
+                }
+              else if (post_data[poff] == '&')
+                {
+                  poff++;       /* skip '&' */
+                  pp->state = PP_Init;
+                }
+            }
+          break;
+        case PP_ExpectNewLine:
+          if ((post_data[poff] == '\n') || (post_data[poff] == '\r'))
+            {
+              poff++;
+              /* we are done, report error if we receive any more... */
+              pp->state = PP_Done;
+              return MHD_YES;
+            }
+          return MHD_NO;
+        default:
+          mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL);          /* should never happen! */
+        }
+    }
+  return MHD_YES;
+}
+
+
+/**
+ * If the given line matches the prefix, strdup the
+ * rest of the line into the suffix ptr.
+ *
+ * @param prefix prefix to match
+ * @param line line to match prefix in
+ * @param suffix set to a copy of the rest of the line, starting at the end of the match
+ * @return #MHD_YES if there was a match, #MHD_NO if not
+ */
+static int
+try_match_header (const char *prefix, char *line, char **suffix)
+{
+  if (NULL != *suffix)
+    return MHD_NO;
+  while (*line != 0)
+    {
+      if (MHD_str_equal_caseless_n_ (prefix, line, strlen (prefix)))
+        {
+          *suffix = strdup (&line[strlen (prefix)]);
+          return MHD_YES;
+        }
+      ++line;
+    }
+  return MHD_NO;
+}
+
+
+/**
+ *
+ * @param pp post processor context
+ * @param boundary boundary to look for
+ * @param blen number of bytes in boundary
+ * @param ioffptr set to the end of the boundary if found,
+ *                otherwise incremented by one (FIXME: quirky API!)
+ * @param next_state state to which we should advance the post processor
+ *                   if the boundary is found
+ * @param next_dash_state dash_state to which we should advance the
+ *                   post processor if the boundary is found
+ * @return #MHD_NO if the boundary is not found, #MHD_YES if we did find it
+ */
+static int
+find_boundary (struct MHD_PostProcessor *pp,
+               const char *boundary,
+               size_t blen,
+               size_t *ioffptr,
+               enum PP_State next_state, enum PP_State next_dash_state)
+{
+  char *buf = (char *) &pp[1];
+  const char *dash;
+
+  if (pp->buffer_pos < 2 + blen)
+    {
+      if (pp->buffer_pos == pp->buffer_size)
+        pp->state = PP_Error;   /* out of memory */
+      // ++(*ioffptr);
+      return MHD_NO;            /* not enough data */
+    }
+  if ((0 != memcmp ("--", buf, 2)) || (0 != memcmp (&buf[2], boundary, blen)))
+    {
+      if (pp->state != PP_Init)
+        {
+          /* garbage not allowed */
+          pp->state = PP_Error;
+        }
+      else
+        {
+          /* skip over garbage (RFC 2046, 5.1.1) */
+          dash = memchr (buf, '-', pp->buffer_pos);
+          if (NULL == dash)
+            (*ioffptr) += pp->buffer_pos; /* skip entire buffer */
+          else
+            if (dash == buf)
+              (*ioffptr)++; /* at least skip one byte */
+            else
+              (*ioffptr) += dash - buf; /* skip to first possible boundary */
+        }
+      return MHD_NO;            /* expected boundary */
+    }
+  /* remove boundary from buffer */
+  (*ioffptr) += 2 + blen;
+  /* next: start with headers */
+  pp->skip_rn = RN_Dash;
+  pp->state = next_state;
+  pp->dash_state = next_dash_state;
+  return MHD_YES;
+}
+
+
+/**
+ * In buf, there maybe an expression '$key="$value"'.  If that is the
+ * case, copy a copy of $value to destination.
+ *
+ * If destination is already non-NULL, do nothing.
+ */
+static void
+try_get_value (const char *buf,
+	       const char *key,
+	       char **destination)
+{
+  const char *spos;
+  const char *bpos;
+  const char *endv;
+  size_t klen;
+  size_t vlen;
+
+  if (NULL != *destination)
+    return;
+  bpos = buf;
+  klen = strlen (key);
+  while (NULL != (spos = strstr (bpos, key)))
+    {
+      if ((spos[klen] != '=') || ((spos != buf) && (spos[-1] != ' ')))
+        {
+          /* no match */
+          bpos = spos + 1;
+          continue;
+        }
+      if (spos[klen + 1] != '"')
+        return;                 /* not quoted */
+      if (NULL == (endv = strchr (&spos[klen + 2], '\"')))
+        return;                 /* no end-quote */
+      vlen = endv - spos - klen - 1;
+      *destination = malloc (vlen);
+      if (NULL == *destination)
+        return;                 /* out of memory */
+      (*destination)[vlen - 1] = '\0';
+      memcpy (*destination, &spos[klen + 2], vlen - 1);
+      return;                   /* success */
+    }
+}
+
+
+/**
+ * Go over the headers of the part and update
+ * the fields in "pp" according to what we find.
+ * If we are at the end of the headers (as indicated
+ * by an empty line), transition into next_state.
+ *
+ * @param pp post processor context
+ * @param ioffptr set to how many bytes have been
+ *                processed
+ * @param next_state state to which the post processor should
+ *                be advanced if we find the end of the headers
+ * @return #MHD_YES if we can continue processing,
+ *         #MHD_NO on error or if we do not have
+ *                enough data yet
+ */
+static int
+process_multipart_headers (struct MHD_PostProcessor *pp,
+                           size_t *ioffptr, enum PP_State next_state)
+{
+  char *buf = (char *) &pp[1];
+  size_t newline;
+
+  newline = 0;
+  while ((newline < pp->buffer_pos) &&
+         (buf[newline] != '\r') && (buf[newline] != '\n'))
+    newline++;
+  if (newline == pp->buffer_size)
+    {
+      pp->state = PP_Error;
+      return MHD_NO;            /* out of memory */
+    }
+  if (newline == pp->buffer_pos)
+    return MHD_NO;              /* will need more data */
+  if (0 == newline)
+    {
+      /* empty line - end of headers */
+      pp->skip_rn = RN_Full;
+      pp->state = next_state;
+      return MHD_YES;
+    }
+  /* got an actual header */
+  if (buf[newline] == '\r')
+    pp->skip_rn = RN_OptN;
+  buf[newline] = '\0';
+  if (MHD_str_equal_caseless_n_ ("Content-disposition: ",
+                        buf, strlen ("Content-disposition: ")))
+    {
+      try_get_value (&buf[strlen ("Content-disposition: ")],
+                     "name", &pp->content_name);
+      try_get_value (&buf[strlen ("Content-disposition: ")],
+                     "filename", &pp->content_filename);
+    }
+  else
+    {
+      try_match_header ("Content-type: ", buf, &pp->content_type);
+      try_match_header ("Content-Transfer-Encoding: ",
+                        buf, &pp->content_transfer_encoding);
+    }
+  (*ioffptr) += newline + 1;
+  return MHD_YES;
+}
+
+
+/**
+ * We have the value until we hit the given boundary;
+ * process accordingly.
+ *
+ * @param pp post processor context
+ * @param ioffptr incremented based on the number of bytes processed
+ * @param boundary the boundary to look for
+ * @param blen strlen(boundary)
+ * @param next_state what state to go into after the
+ *        boundary was found
+ * @param next_dash_state state to go into if the next
+ *        boundary ends with "--"
+ * @return #MHD_YES if we can continue processing,
+ *         #MHD_NO on error or if we do not have
+ *                enough data yet
+ */
+static int
+process_value_to_boundary (struct MHD_PostProcessor *pp,
+                           size_t *ioffptr,
+                           const char *boundary,
+                           size_t blen,
+                           enum PP_State next_state,
+                           enum PP_State next_dash_state)
+{
+  char *buf = (char *) &pp[1];
+  size_t newline;
+  const char *r;
+
+  /* all data in buf until the boundary
+     (\r\n--+boundary) is part of the value */
+  newline = 0;
+  while (1)
+    {
+      while (newline + 4 < pp->buffer_pos)
+        {
+          r = memchr (&buf[newline], '\r', pp->buffer_pos - newline - 4);
+          if (NULL == r)
+          {
+            newline = pp->buffer_pos - 4;
+            break;
+          }
+          newline = r - buf;
+          if (0 == memcmp ("\r\n--", &buf[newline], 4))
+            break;
+          newline++;
+        }
+      if (newline + pp->blen + 4 <= pp->buffer_pos)
+        {
+          /* can check boundary */
+          if (0 != memcmp (&buf[newline + 4], boundary, pp->blen))
+            {
+              /* no boundary, "\r\n--" is part of content, skip */
+              newline += 4;
+              continue;
+            }
+          else
+            {
+              /* boundary found, process until newline then
+                 skip boundary and go back to init */
+              pp->skip_rn = RN_Dash;
+              pp->state = next_state;
+              pp->dash_state = next_dash_state;
+              (*ioffptr) += pp->blen + 4;       /* skip boundary as well */
+              buf[newline] = '\0';
+              break;
+            }
+        }
+      else
+        {
+          /* cannot check for boundary, process content that
+             we have and check again later; except, if we have
+             no content, abort (out of memory) */
+          if ((0 == newline) && (pp->buffer_pos == pp->buffer_size))
+            {
+              pp->state = PP_Error;
+              return MHD_NO;
+            }
+          break;
+        }
+    }
+  /* newline is either at beginning of boundary or
+     at least at the last character that we are sure
+     is not part of the boundary */
+  if ( ( (MHD_YES == pp->must_ikvi) ||
+	 (0 != newline) ) &&
+       (MHD_NO == pp->ikvi (pp->cls,
+			    MHD_POSTDATA_KIND,
+			    pp->content_name,
+			    pp->content_filename,
+			    pp->content_type,
+			    pp->content_transfer_encoding,
+			    buf, pp->value_offset, newline)) )
+    {
+      pp->state = PP_Error;
+      return MHD_NO;
+    }
+  pp->must_ikvi = MHD_NO;
+  pp->value_offset += newline;
+  (*ioffptr) += newline;
+  return MHD_YES;
+}
+
+
+/**
+ *
+ * @param pp post processor context
+ */
+static void
+free_unmarked (struct MHD_PostProcessor *pp)
+{
+  if ((NULL != pp->content_name) && (0 == (pp->have & NE_content_name)))
+    {
+      free (pp->content_name);
+      pp->content_name = NULL;
+    }
+  if ((NULL != pp->content_type) && (0 == (pp->have & NE_content_type)))
+    {
+      free (pp->content_type);
+      pp->content_type = NULL;
+    }
+  if ((NULL != pp->content_filename) &&
+      (0 == (pp->have & NE_content_filename)))
+    {
+      free (pp->content_filename);
+      pp->content_filename = NULL;
+    }
+  if ((NULL != pp->content_transfer_encoding) &&
+      (0 == (pp->have & NE_content_transfer_encoding)))
+    {
+      free (pp->content_transfer_encoding);
+      pp->content_transfer_encoding = NULL;
+    }
+}
+
+
+/**
+ * Decode multipart POST data.
+ *
+ * @param pp post processor context
+ * @param post_data data to decode
+ * @param post_data_len number of bytes in @a post_data
+ * @return #MHD_NO on error,
+ */
+static int
+post_process_multipart (struct MHD_PostProcessor *pp,
+                        const char *post_data,
+			size_t post_data_len)
+{
+  char *buf;
+  size_t max;
+  size_t ioff;
+  size_t poff;
+  int state_changed;
+
+  buf = (char *) &pp[1];
+  ioff = 0;
+  poff = 0;
+  state_changed = 1;
+  while ((poff < post_data_len) ||
+         ((pp->buffer_pos > 0) && (state_changed != 0)))
+    {
+      /* first, move as much input data
+         as possible to our internal buffer */
+      max = pp->buffer_size - pp->buffer_pos;
+      if (max > post_data_len - poff)
+        max = post_data_len - poff;
+      memcpy (&buf[pp->buffer_pos], &post_data[poff], max);
+      poff += max;
+      pp->buffer_pos += max;
+      if ((max == 0) && (state_changed == 0) && (poff < post_data_len))
+        {
+          pp->state = PP_Error;
+          return MHD_NO;        /* out of memory */
+        }
+      state_changed = 0;
+
+      /* first state machine for '\r'-'\n' and '--' handling */
+      switch (pp->skip_rn)
+        {
+        case RN_Inactive:
+          break;
+        case RN_OptN:
+          if (buf[0] == '\n')
+            {
+              ioff++;
+              pp->skip_rn = RN_Inactive;
+              goto AGAIN;
+            }
+          /* fall-through! */
+        case RN_Dash:
+          if (buf[0] == '-')
+            {
+              ioff++;
+              pp->skip_rn = RN_Dash2;
+              goto AGAIN;
+            }
+          pp->skip_rn = RN_Full;
+          /* fall-through! */
+        case RN_Full:
+          if (buf[0] == '\r')
+            {
+              if ((pp->buffer_pos > 1) && (buf[1] == '\n'))
+                {
+                  pp->skip_rn = RN_Inactive;
+                  ioff += 2;
+                }
+              else
+                {
+                  pp->skip_rn = RN_OptN;
+                  ioff++;
+                }
+              goto AGAIN;
+            }
+          if (buf[0] == '\n')
+            {
+              ioff++;
+              pp->skip_rn = RN_Inactive;
+              goto AGAIN;
+            }
+          pp->skip_rn = RN_Inactive;
+          pp->state = PP_Error;
+          return MHD_NO;        /* no '\r\n' */
+        case RN_Dash2:
+          if (buf[0] == '-')
+            {
+              ioff++;
+              pp->skip_rn = RN_Full;
+              pp->state = pp->dash_state;
+              goto AGAIN;
+            }
+          pp->state = PP_Error;
+          break;
+        }
+
+      /* main state engine */
+      switch (pp->state)
+        {
+        case PP_Error:
+          return MHD_NO;
+        case PP_Done:
+          /* did not expect to receive more data */
+          pp->state = PP_Error;
+          return MHD_NO;
+        case PP_Init:
+          /**
+           * Per RFC2046 5.1.1 NOTE TO IMPLEMENTORS, consume anything
+           * prior to the first multipart boundary:
+           *
+           * > There appears to be room for additional information prior
+           * > to the first boundary delimiter line and following the
+           * > final boundary delimiter line.  These areas should
+           * > generally be left blank, and implementations must ignore
+           * > anything that appears before the first boundary delimiter
+           * > line or after the last one.
+           */
+          (void) find_boundary (pp,
+				pp->boundary,
+				pp->blen,
+				&ioff,
+				PP_ProcessEntryHeaders, PP_Done);
+          break;
+        case PP_NextBoundary:
+          if (MHD_NO == find_boundary (pp,
+                                       pp->boundary,
+                                       pp->blen,
+                                       &ioff,
+                                       PP_ProcessEntryHeaders, PP_Done))
+            {
+              if (pp->state == PP_Error)
+                return MHD_NO;
+              goto END;
+            }
+          break;
+        case PP_ProcessEntryHeaders:
+	  pp->must_ikvi = MHD_YES;
+          if (MHD_NO ==
+              process_multipart_headers (pp, &ioff, PP_PerformCheckMultipart))
+            {
+              if (pp->state == PP_Error)
+                return MHD_NO;
+              else
+                goto END;
+            }
+          state_changed = 1;
+          break;
+        case PP_PerformCheckMultipart:
+          if ((pp->content_type != NULL) &&
+              (MHD_str_equal_caseless_n_ (pp->content_type,
+                                 "multipart/mixed",
+                                 strlen ("multipart/mixed"))))
+            {
+              pp->nested_boundary = strstr (pp->content_type, "boundary=");
+              if (pp->nested_boundary == NULL)
+                {
+                  pp->state = PP_Error;
+                  return MHD_NO;
+                }
+              pp->nested_boundary =
+                strdup (&pp->nested_boundary[strlen ("boundary=")]);
+              if (pp->nested_boundary == NULL)
+                {
+                  /* out of memory */
+                  pp->state = PP_Error;
+                  return MHD_NO;
+                }
+              /* free old content type, we will need that field
+                 for the content type of the nested elements */
+              free (pp->content_type);
+              pp->content_type = NULL;
+              pp->nlen = strlen (pp->nested_boundary);
+              pp->state = PP_Nested_Init;
+              state_changed = 1;
+              break;
+            }
+          pp->state = PP_ProcessValueToBoundary;
+          pp->value_offset = 0;
+          state_changed = 1;
+          break;
+        case PP_ProcessValueToBoundary:
+          if (MHD_NO == process_value_to_boundary (pp,
+                                                   &ioff,
+                                                   pp->boundary,
+                                                   pp->blen,
+                                                   PP_PerformCleanup,
+                                                   PP_Done))
+            {
+              if (pp->state == PP_Error)
+                return MHD_NO;
+              break;
+            }
+          break;
+        case PP_PerformCleanup:
+          /* clean up state of one multipart form-data element! */
+          pp->have = NE_none;
+          free_unmarked (pp);
+          if (pp->nested_boundary != NULL)
+            {
+              free (pp->nested_boundary);
+              pp->nested_boundary = NULL;
+            }
+          pp->state = PP_ProcessEntryHeaders;
+          state_changed = 1;
+          break;
+        case PP_Nested_Init:
+          if (pp->nested_boundary == NULL)
+            {
+              pp->state = PP_Error;
+              return MHD_NO;
+            }
+          if (MHD_NO == find_boundary (pp,
+                                       pp->nested_boundary,
+                                       pp->nlen,
+                                       &ioff,
+                                       PP_Nested_PerformMarking,
+                                       PP_NextBoundary /* or PP_Error? */ ))
+            {
+              if (pp->state == PP_Error)
+                return MHD_NO;
+              goto END;
+            }
+          break;
+        case PP_Nested_PerformMarking:
+          /* remember what headers were given
+             globally */
+          pp->have = NE_none;
+          if (pp->content_name != NULL)
+            pp->have |= NE_content_name;
+          if (pp->content_type != NULL)
+            pp->have |= NE_content_type;
+          if (pp->content_filename != NULL)
+            pp->have |= NE_content_filename;
+          if (pp->content_transfer_encoding != NULL)
+            pp->have |= NE_content_transfer_encoding;
+          pp->state = PP_Nested_ProcessEntryHeaders;
+          state_changed = 1;
+          break;
+        case PP_Nested_ProcessEntryHeaders:
+          pp->value_offset = 0;
+          if (MHD_NO ==
+              process_multipart_headers (pp, &ioff,
+                                         PP_Nested_ProcessValueToBoundary))
+            {
+              if (pp->state == PP_Error)
+                return MHD_NO;
+              else
+                goto END;
+            }
+          state_changed = 1;
+          break;
+        case PP_Nested_ProcessValueToBoundary:
+          if (MHD_NO == process_value_to_boundary (pp,
+                                                   &ioff,
+                                                   pp->nested_boundary,
+                                                   pp->nlen,
+                                                   PP_Nested_PerformCleanup,
+                                                   PP_NextBoundary))
+            {
+              if (pp->state == PP_Error)
+                return MHD_NO;
+              break;
+            }
+          break;
+        case PP_Nested_PerformCleanup:
+          free_unmarked (pp);
+          pp->state = PP_Nested_ProcessEntryHeaders;
+          state_changed = 1;
+          break;
+        default:
+          mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL);          /* should never happen! */
+        }
+    AGAIN:
+      if (ioff > 0)
+        {
+          memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
+          pp->buffer_pos -= ioff;
+          ioff = 0;
+          state_changed = 1;
+        }
+    }
+END:
+  if (ioff != 0)
+    {
+      memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
+      pp->buffer_pos -= ioff;
+    }
+  if (poff < post_data_len)
+    {
+      pp->state = PP_Error;
+      return MHD_NO;            /* serious error */
+    }
+  return MHD_YES;
+}
+
+
+/**
+ * Parse and process POST data.  Call this function when POST data is
+ * available (usually during an #MHD_AccessHandlerCallback) with the
+ * "upload_data" and "upload_data_size".  Whenever possible, this will
+ * then cause calls to the #MHD_PostDataIterator.
+ *
+ * @param pp the post processor
+ * @param post_data @a post_data_len bytes of POST data
+ * @param post_data_len length of @a post_data
+ * @return #MHD_YES on success, #MHD_NO on error
+ *         (out-of-memory, iterator aborted, parse error)
+ * @ingroup request
+ */
+int
+MHD_post_process (struct MHD_PostProcessor *pp,
+                  const char *post_data, size_t post_data_len)
+{
+  if (0 == post_data_len)
+    return MHD_YES;
+  if (NULL == pp)
+    return MHD_NO;
+  if (MHD_str_equal_caseless_n_ (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, pp->encoding,
+                         strlen(MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
+    return post_process_urlencoded (pp, post_data, post_data_len);
+  if (MHD_str_equal_caseless_n_ (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, pp->encoding,
+                   strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
+    return post_process_multipart (pp, post_data, post_data_len);
+  /* this should never be reached */
+  return MHD_NO;
+}
+
+
+/**
+ * Release PostProcessor resources.
+ *
+ * @param pp post processor context to destroy
+ * @return #MHD_YES if processing completed nicely,
+ *         #MHD_NO if there were spurious characters / formatting
+ *                problems; it is common to ignore the return
+ *                value of this function
+ * @ingroup request
+ */
+int
+MHD_destroy_post_processor (struct MHD_PostProcessor *pp)
+{
+  int ret;
+
+  if (NULL == pp)
+    return MHD_YES;
+  if (PP_ProcessValue == pp->state)
+  {
+    /* key without terminated value left at the end of the
+       buffer; fake receiving a termination character to
+       ensure it is also processed */
+    post_process_urlencoded (pp, "\n", 1);
+  }
+  /* These internal strings need cleaning up since
+     the post-processing may have been interrupted
+     at any stage */
+  if ((pp->xbuf_pos > 0) ||
+      ( (pp->state != PP_Done) &&
+	(pp->state != PP_ExpectNewLine)))
+    ret = MHD_NO;
+  else
+    ret = MHD_YES;
+  pp->have = NE_none;
+  free_unmarked (pp);
+  if (pp->nested_boundary != NULL)
+    free (pp->nested_boundary);
+  free (pp);
+  return ret;
+}
+
+/* end of postprocessor.c */
diff --git a/src/microhttpd/reason_phrase.c b/src/microhttpd/reason_phrase.c
new file mode 100644
index 0000000..3afe8af
--- /dev/null
+++ b/src/microhttpd/reason_phrase.c
@@ -0,0 +1,160 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2011 Christian Grothoff
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file reason_phrase.c
+ * @brief  Tables of the string response phrases
+ * @author Elliot Glaysher
+ * @author Christian Grothoff (minor code clean up)
+ */
+#include "platform.h"
+#include "reason_phrase.h"
+
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static const char *invalid_hundred[] = { NULL };
+
+static const char *const one_hundred[] = {
+  "Continue",
+  "Switching Protocols",
+  "Processing"
+};
+
+static const char *const two_hundred[] = {
+  "OK",
+  "Created",
+  "Accepted",
+  "Non-Authoritative Information",
+  "No Content",
+  "Reset Content",
+  "Partial Content",
+  "Multi Status"
+};
+
+static const char *const three_hundred[] = {
+  "Multiple Choices",
+  "Moved Permanently",
+  "Moved Temporarily",
+  "See Other",
+  "Not Modified",
+  "Use Proxy",
+  "Switch Proxy",
+  "Temporary Redirect"
+};
+
+static const char *const four_hundred[] = {
+  "Bad Request",
+  "Unauthorized",
+  "Payment Required",
+  "Forbidden",
+  "Not Found",
+  "Method Not Allowed",
+  "Not Acceptable",
+  "Proxy Authentication Required",
+  "Request Time-out",
+  "Conflict",
+  "Gone",
+  "Length Required",
+  "Precondition Failed",
+  "Request Entity Too Large",
+  "Request-URI Too Large",
+  "Unsupported Media Type",
+  "Requested Range Not Satisfiable",
+  "Expectation Failed",
+  "Unknown",
+  "Unknown",
+  "Unknown", /* 420 */
+  "Unknown",
+  "Unprocessable Entity",
+  "Locked",
+  "Failed Dependency",
+  "Unordered Collection",
+  "Upgrade Required",
+  "Unknown",
+  "Unknown",
+  "Unknown",
+  "Unknown", /* 430 */
+  "Unknown",
+  "Unknown",
+  "Unknown",
+  "Unknown",
+  "Unknown", /* 435 */
+  "Unknown",
+  "Unknown",
+  "Unknown",
+  "Unknown",
+  "Unknown", /* 440 */
+  "Unknown",
+  "Unknown",
+  "Unknown",
+  "No Response",
+  "Unknown", /* 445 */
+  "Unknown",
+  "Unknown",
+  "Unknown",
+  "Retry With",
+  "Blocked by Windows Parental Controls", /* 450 */
+  "Unavailable For Legal Reasons"
+};
+
+static const char *const five_hundred[] = {
+  "Internal Server Error",
+  "Not Implemented",
+  "Bad Gateway",
+  "Service Unavailable",
+  "Gateway Time-out",
+  "HTTP Version not supported",
+  "Variant Also Negotiates",
+  "Insufficient Storage",
+  "Unknown",
+  "Bandwidth Limit Exceeded",
+  "Not Extended"
+};
+
+
+struct MHD_Reason_Block
+{
+  unsigned int max;
+  const char *const*data;
+};
+
+#define BLOCK(m) { (sizeof(m) / sizeof(char*)), m }
+
+static const struct MHD_Reason_Block reasons[] = {
+  BLOCK (invalid_hundred),
+  BLOCK (one_hundred),
+  BLOCK (two_hundred),
+  BLOCK (three_hundred),
+  BLOCK (four_hundred),
+  BLOCK (five_hundred),
+};
+
+
+const char *
+MHD_get_reason_phrase_for (unsigned int code)
+{
+  if ( (code >= 100) &&
+       (code < 600) &&
+       (reasons[code / 100].max > (code % 100)) )
+    return reasons[code / 100].data[code % 100];
+  return "Unknown";
+}
diff --git a/src/microhttpd/reason_phrase.h b/src/microhttpd/reason_phrase.h
new file mode 100644
index 0000000..e442232
--- /dev/null
+++ b/src/microhttpd/reason_phrase.h
@@ -0,0 +1,37 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Lymba
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file reason_phrase.c
+ * @brief  Tables of the string response phrases
+ * @author Elliot Glaysher
+ */
+
+#ifndef REASON_PHRASE_H
+#define REASON_PHRASE_H
+
+/**
+ * Returns the string reason phrase for a response code.
+ *
+ * If we don't have a string for a status code, we give the first
+ * message in that status code class.
+ */
+const char *MHD_get_reason_phrase_for (unsigned int code);
+
+#endif
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c
new file mode 100644
index 0000000..47a439e
--- /dev/null
+++ b/src/microhttpd/response.c
@@ -0,0 +1,525 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2009, 2010 Daniel Pittman and Christian Grothoff
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file response.c
+ * @brief  Methods for managing response objects
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ */
+
+#include "internal.h"
+#include "response.h"
+
+#if defined(_WIN32) && defined(MHD_W32_MUTEX_)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif /* _WIN32 && MHD_W32_MUTEX_ */
+#if defined(_WIN32)
+#include <io.h> /* for lseek(), read() */
+#endif /* _WIN32 */
+
+
+/**
+ * Add a header or footer line to the response.
+ *
+ * @param response response to add a header to
+ * @param kind header or footer
+ * @param header the header to add
+ * @param content value to add
+ * @return #MHD_NO on error (i.e. invalid header or content format).
+ */
+static int
+add_response_entry (struct MHD_Response *response,
+		    enum MHD_ValueKind kind,
+		    const char *header,
+		    const char *content)
+{
+  struct MHD_HTTP_Header *hdr;
+
+  if ( (NULL == response) ||
+       (NULL == header) ||
+       (NULL == content) ||
+       (0 == strlen (header)) ||
+       (0 == strlen (content)) ||
+       (NULL != strchr (header, '\t')) ||
+       (NULL != strchr (header, '\r')) ||
+       (NULL != strchr (header, '\n')) ||
+       (NULL != strchr (content, '\t')) ||
+       (NULL != strchr (content, '\r')) ||
+       (NULL != strchr (content, '\n')) )
+    return MHD_NO;
+  if (NULL == (hdr = malloc (sizeof (struct MHD_HTTP_Header))))
+    return MHD_NO;
+  if (NULL == (hdr->header = strdup (header)))
+    {
+      free (hdr);
+      return MHD_NO;
+    }
+  if (NULL == (hdr->value = strdup (content)))
+    {
+      free (hdr->header);
+      free (hdr);
+      return MHD_NO;
+    }
+  hdr->kind = kind;
+  hdr->next = response->first_header;
+  response->first_header = hdr;
+  return MHD_YES;
+}
+
+
+/**
+ * Add a header line to the response.
+ *
+ * @param response response to add a header to
+ * @param header the header to add
+ * @param content value to add
+ * @return #MHD_NO on error (i.e. invalid header or content format).
+ * @ingroup response
+ */
+int
+MHD_add_response_header (struct MHD_Response *response,
+                         const char *header, const char *content)
+{
+  return add_response_entry (response,
+			     MHD_HEADER_KIND,
+			     header,
+			     content);
+}
+
+
+/**
+ * Add a footer line to the response.
+ *
+ * @param response response to remove a header from
+ * @param footer the footer to delete
+ * @param content value to delete
+ * @return #MHD_NO on error (i.e. invalid footer or content format).
+ * @ingroup response
+ */
+int
+MHD_add_response_footer (struct MHD_Response *response,
+                         const char *footer, const char *content)
+{
+  return add_response_entry (response,
+			     MHD_FOOTER_KIND,
+			     footer,
+			     content);
+}
+
+
+/**
+ * Delete a header (or footer) line from the response.
+ *
+ * @param response response to remove a header from
+ * @param header the header to delete
+ * @param content value to delete
+ * @return #MHD_NO on error (no such header known)
+ * @ingroup response
+ */
+int
+MHD_del_response_header (struct MHD_Response *response,
+                         const char *header,
+			 const char *content)
+{
+  struct MHD_HTTP_Header *pos;
+  struct MHD_HTTP_Header *prev;
+
+  if ( (NULL == header) || (NULL == content) )
+    return MHD_NO;
+  prev = NULL;
+  pos = response->first_header;
+  while (pos != NULL)
+    {
+      if ((0 == strcmp (header, pos->header)) &&
+          (0 == strcmp (content, pos->value)))
+        {
+          free (pos->header);
+          free (pos->value);
+          if (NULL == prev)
+            response->first_header = pos->next;
+          else
+            prev->next = pos->next;
+          free (pos);
+          return MHD_YES;
+        }
+      prev = pos;
+      pos = pos->next;
+    }
+  return MHD_NO;
+}
+
+
+/**
+ * Get all of the headers (and footers) added to a response.
+ *
+ * @param response response to query
+ * @param iterator callback to call on each header;
+ *        maybe NULL (then just count headers)
+ * @param iterator_cls extra argument to @a iterator
+ * @return number of entries iterated over
+ * @ingroup response
+ */
+int
+MHD_get_response_headers (struct MHD_Response *response,
+                          MHD_KeyValueIterator iterator, void *iterator_cls)
+{
+  struct MHD_HTTP_Header *pos;
+  int numHeaders = 0;
+
+  for (pos = response->first_header; NULL != pos; pos = pos->next)
+    {
+      numHeaders++;
+      if ((NULL != iterator) &&
+          (MHD_YES != iterator (iterator_cls,
+                                pos->kind, pos->header, pos->value)))
+        break;
+    }
+  return numHeaders;
+}
+
+
+/**
+ * Get a particular header (or footer) from the response.
+ *
+ * @param response response to query
+ * @param key which header to get
+ * @return NULL if header does not exist
+ * @ingroup response
+ */
+const char *
+MHD_get_response_header (struct MHD_Response *response,
+			 const char *key)
+{
+  struct MHD_HTTP_Header *pos;
+
+  if (NULL == key)
+    return NULL;
+  for (pos = response->first_header; NULL != pos; pos = pos->next)
+    if (0 == strcmp (key, pos->header))
+      return pos->value;
+  return NULL;
+}
+
+
+/**
+ * Create a response object.  The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the data portion of the response, #MHD_SIZE_UNKNOWN for unknown
+ * @param block_size preferred block size for querying crc (advisory only,
+ *                   MHD may still call @a crc using smaller chunks); this
+ *                   is essentially the buffer size used for IO, clients
+ *                   should pick a value that is appropriate for IO and
+ *                   memory performance requirements
+ * @param crc callback to use to obtain response data
+ * @param crc_cls extra argument to @a crc
+ * @param crfc callback to call to free @a crc_cls resources
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @ingroup response
+ */
+struct MHD_Response *
+MHD_create_response_from_callback (uint64_t size,
+                                   size_t block_size,
+                                   MHD_ContentReaderCallback crc,
+                                   void *crc_cls,
+                                   MHD_ContentReaderFreeCallback crfc)
+{
+  struct MHD_Response *response;
+
+  if ((NULL == crc) || (0 == block_size))
+    return NULL;
+  if (NULL == (response = malloc (sizeof (struct MHD_Response) + block_size)))
+    return NULL;
+  memset (response, 0, sizeof (struct MHD_Response));
+  response->fd = -1;
+  response->data = (void *) &response[1];
+  response->data_buffer_size = block_size;
+  if (MHD_YES != MHD_mutex_create_ (&response->mutex))
+    {
+      free (response);
+      return NULL;
+    }
+  response->crc = crc;
+  response->crfc = crfc;
+  response->crc_cls = crc_cls;
+  response->reference_count = 1;
+  response->total_size = size;
+  return response;
+}
+
+
+/**
+ * Set special flags and options for a response.
+ *
+ * @param response the response to modify
+ * @param flags to set for the response
+ * @param ... #MHD_RO_END terminated list of options
+ * @return #MHD_YES on success, #MHD_NO on error
+ */
+int
+MHD_set_response_options (struct MHD_Response *response,
+                          enum MHD_ResponseFlags flags,
+                          ...)
+{
+  va_list ap;
+  int ret;
+  enum MHD_ResponseOptions ro;
+
+  ret = MHD_YES;
+  response->flags = flags;
+  va_start (ap, flags);
+  while (MHD_RO_END != (ro = va_arg (ap, enum MHD_ResponseOptions)))
+  {
+    switch (ro)
+    {
+    default:
+      ret = MHD_NO;
+      break;
+    }
+  }
+  va_end (ap);
+  return ret;
+}
+
+
+/**
+ * Given a file descriptor, read data from the file
+ * to generate the response.
+ *
+ * @param cls pointer to the response
+ * @param pos offset in the file to access
+ * @param buf where to write the data
+ * @param max number of bytes to write at most
+ * @return number of bytes written
+ */
+static ssize_t
+file_reader (void *cls, uint64_t pos, char *buf, size_t max)
+{
+  struct MHD_Response *response = cls;
+  ssize_t n;
+
+  (void) lseek (response->fd, pos + response->fd_off, SEEK_SET);
+  n = read (response->fd, buf, max);
+  if (0 == n)
+    return MHD_CONTENT_READER_END_OF_STREAM;
+  if (n < 0)
+    return MHD_CONTENT_READER_END_WITH_ERROR;
+  return n;
+}
+
+
+/**
+ * Destroy file reader context.  Closes the file
+ * descriptor.
+ *
+ * @param cls pointer to file descriptor
+ */
+static void
+free_callback (void *cls)
+{
+  struct MHD_Response *response = cls;
+
+  (void) close (response->fd);
+  response->fd = -1;
+}
+
+
+/**
+ * Create a response object.  The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the data portion of the response
+ * @param fd file descriptor referring to a file on disk with the
+ *        data; will be closed when response is destroyed;
+ *        fd should be in 'blocking' mode
+ * @param offset offset to start reading from in the file;
+ *        Be careful! `off_t` may have been compiled to be a
+ *        64-bit variable for MHD, in which case your application
+ *        also has to be compiled using the same options! Read
+ *        the MHD manual for more details.
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @ingroup response
+ */
+struct MHD_Response *
+MHD_create_response_from_fd_at_offset (size_t size,
+				       int fd,
+				       off_t offset)
+{
+  struct MHD_Response *response;
+
+  response = MHD_create_response_from_callback (size,
+						4 * 1024,
+						&file_reader,
+						NULL,
+						&free_callback);
+  if (NULL == response)
+    return NULL;
+  response->fd = fd;
+  response->fd_off = offset;
+  response->crc_cls = response;
+  return response;
+}
+
+
+/**
+ * Create a response object.  The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the data portion of the response
+ * @param fd file descriptor referring to a file on disk with the data
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @ingroup response
+ */
+struct MHD_Response *
+MHD_create_response_from_fd (size_t size,
+			     int fd)
+{
+  return MHD_create_response_from_fd_at_offset (size, fd, 0);
+}
+
+
+/**
+ * Create a response object.  The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the @a data portion of the response
+ * @param data the data itself
+ * @param must_free libmicrohttpd should free data when done
+ * @param must_copy libmicrohttpd must make a copy of @a data
+ *        right away, the data maybe released anytime after
+ *        this call returns
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @deprecated use #MHD_create_response_from_buffer instead
+ * @ingroup response
+ */
+struct MHD_Response *
+MHD_create_response_from_data (size_t size,
+                               void *data, int must_free, int must_copy)
+{
+  struct MHD_Response *response;
+  void *tmp;
+
+  if ((NULL == data) && (size > 0))
+    return NULL;
+  if (NULL == (response = malloc (sizeof (struct MHD_Response))))
+    return NULL;
+  memset (response, 0, sizeof (struct MHD_Response));
+  response->fd = -1;
+  if (MHD_YES != MHD_mutex_create_ (&response->mutex))
+    {
+      free (response);
+      return NULL;
+    }
+  if ((must_copy) && (size > 0))
+    {
+      if (NULL == (tmp = malloc (size)))
+        {
+          (void) MHD_mutex_destroy_ (&response->mutex);
+          free (response);
+          return NULL;
+        }
+      memcpy (tmp, data, size);
+      must_free = MHD_YES;
+      data = tmp;
+    }
+  response->crc = NULL;
+  response->crfc = must_free ? &free : NULL;
+  response->crc_cls = must_free ? data : NULL;
+  response->reference_count = 1;
+  response->total_size = size;
+  response->data = data;
+  response->data_size = size;
+  return response;
+}
+
+
+/**
+ * Create a response object.  The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the data portion of the response
+ * @param buffer size bytes containing the response's data portion
+ * @param mode flags for buffer management
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @ingroup response
+ */
+struct MHD_Response *
+MHD_create_response_from_buffer (size_t size,
+				 void *buffer,
+				 enum MHD_ResponseMemoryMode mode)
+{
+  return MHD_create_response_from_data (size,
+					buffer,
+					mode == MHD_RESPMEM_MUST_FREE,
+					mode == MHD_RESPMEM_MUST_COPY);
+}
+
+
+/**
+ * Destroy a response object and associated resources.  Note that
+ * libmicrohttpd may keep some of the resources around if the response
+ * is still in the queue for some clients, so the memory may not
+ * necessarily be freed immediatley.
+ *
+ * @param response response to destroy
+ * @ingroup response
+ */
+void
+MHD_destroy_response (struct MHD_Response *response)
+{
+  struct MHD_HTTP_Header *pos;
+
+  if (NULL == response)
+    return;
+  (void) MHD_mutex_lock_ (&response->mutex);
+  if (0 != --(response->reference_count))
+    {
+      (void) MHD_mutex_unlock_ (&response->mutex);
+      return;
+    }
+  (void) MHD_mutex_unlock_ (&response->mutex);
+  (void) MHD_mutex_destroy_ (&response->mutex);
+  if (response->crfc != NULL)
+    response->crfc (response->crc_cls);
+  while (NULL != response->first_header)
+    {
+      pos = response->first_header;
+      response->first_header = pos->next;
+      free (pos->header);
+      free (pos->value);
+      free (pos);
+    }
+  free (response);
+}
+
+
+void
+MHD_increment_response_rc (struct MHD_Response *response)
+{
+  (void) MHD_mutex_lock_ (&response->mutex);
+  (response->reference_count)++;
+  (void) MHD_mutex_unlock_ (&response->mutex);
+}
+
+
+/* end of response.c */
diff --git a/src/microhttpd/response.h b/src/microhttpd/response.h
new file mode 100644
index 0000000..5785ec9
--- /dev/null
+++ b/src/microhttpd/response.h
@@ -0,0 +1,38 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Daniel Pittman and Christian Grothoff
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file response.h
+ * @brief  Methods for managing response objects
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ */
+
+#ifndef RESPONSE_H
+#define RESPONSE_H
+
+/**
+ * Increment response RC.  Should this be part of the
+ * public API?
+ */
+void
+MHD_increment_response_rc (struct MHD_Response *response);
+
+
+#endif
diff --git a/src/microhttpd/test_daemon.c b/src/microhttpd/test_daemon.c
new file mode 100644
index 0000000..13450ba
--- /dev/null
+++ b/src/microhttpd/test_daemon.c
@@ -0,0 +1,167 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_daemon.c
+ * @brief  Testcase for libmicrohttpd starts and stops
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "microhttpd.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+
+static int
+testStartError ()
+{
+  struct MHD_Daemon *d;
+
+  d = MHD_start_daemon (MHD_USE_DEBUG, 0, NULL, NULL, NULL, NULL);
+  if (d != NULL)
+    return 1;
+  return 0;
+}
+
+static int
+apc_nothing (void *cls, const struct sockaddr *addr, socklen_t addrlen)
+{
+  return MHD_NO;
+}
+
+static int
+apc_all (void *cls, const struct sockaddr *addr, socklen_t addrlen)
+{
+  return MHD_YES;
+}
+
+static int
+ahc_nothing (void *cls,
+             struct MHD_Connection *connection,
+             const char *url,
+             const char *method,
+             const char *version,
+             const char *upload_data, size_t *upload_data_size,
+             void **unused)
+{
+  return MHD_NO;
+}
+
+static int
+testStartStop ()
+{
+  struct MHD_Daemon *d;
+
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1080,
+                        &apc_nothing,
+                        NULL, &ahc_nothing, NULL, MHD_OPTION_END);
+  if (d == NULL)
+    return 2;
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+static int
+testExternalRun ()
+{
+  struct MHD_Daemon *d;
+  fd_set rs;
+  MHD_socket maxfd;
+  int i;
+
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        1081,
+                        &apc_all, NULL, &ahc_nothing, NULL, MHD_OPTION_END);
+
+  if (d == NULL)
+    return 4;
+  i = 0;
+  while (i < 15)
+    {
+      maxfd = 0;
+      FD_ZERO (&rs);
+      if (MHD_YES != MHD_get_fdset (d, &rs, &rs, &rs, &maxfd))
+	{
+	  MHD_stop_daemon (d);
+	  return 256;
+	}
+      if (MHD_run (d) == MHD_NO)
+        {
+          MHD_stop_daemon (d);
+          return 8;
+        }
+      i++;
+    }
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+static int
+testThread ()
+{
+  struct MHD_Daemon *d;
+  d = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SELECT_INTERNALLY,
+                        1082,
+                        &apc_all, NULL, &ahc_nothing, NULL, MHD_OPTION_END);
+
+  if (d == NULL)
+    return 16;
+  if (MHD_run (d) != MHD_NO)
+    return 32;
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+static int
+testMultithread ()
+{
+  struct MHD_Daemon *d;
+  d = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_THREAD_PER_CONNECTION,
+                        1083,
+                        &apc_all, NULL, &ahc_nothing, NULL, MHD_OPTION_END);
+
+  if (d == NULL)
+    return 64;
+  if (MHD_run (d) != MHD_NO)
+    return 128;
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  int errorCount = 0;
+  errorCount += testStartError ();
+  errorCount += testStartStop ();
+  errorCount += testExternalRun ();
+  errorCount += testThread ();
+  errorCount += testMultithread ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/microhttpd/test_postprocessor.c b/src/microhttpd/test_postprocessor.c
new file mode 100644
index 0000000..66bab81
--- /dev/null
+++ b/src/microhttpd/test_postprocessor.c
@@ -0,0 +1,349 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007,2013 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 3, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_postprocessor.c
+ * @brief  Testcase for postprocessor
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "microhttpd.h"
+#include "internal.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+/**
+ * Array of values that the value checker "wants".
+ * Each series of checks should be terminated by
+ * five NULL-entries.
+ */
+const char *want[] = {
+#define URL_DATA "abc=def&x=5"
+#define URL_START 0
+  "abc", NULL, NULL, NULL, "def",
+  "x", NULL, NULL, NULL, "5",
+#define URL_END (URL_START + 10)
+  NULL, NULL, NULL, NULL, NULL,
+#define FORM_DATA "--AaB03x\r\ncontent-disposition: form-data; name=\"field1\"\r\n\r\nJoe Blow\r\n--AaB03x\r\ncontent-disposition: form-data; name=\"pics\"; filename=\"file1.txt\"\r\nContent-Type: text/plain\r\nContent-Transfer-Encoding: binary\r\n\r\nfiledata\r\n--AaB03x--\r\n"
+#define FORM_START (URL_END + 5)
+  "field1", NULL, NULL, NULL, "Joe Blow",
+  "pics", "file1.txt", "text/plain", "binary", "filedata",
+#define FORM_END (FORM_START + 10)
+  NULL, NULL, NULL, NULL, NULL,
+#define FORM_NESTED_DATA "--AaB03x\r\ncontent-disposition: form-data; name=\"field1\"\r\n\r\nJane Blow\r\n--AaB03x\r\ncontent-disposition: form-data; name=\"pics\"\r\nContent-type: multipart/mixed, boundary=BbC04y\r\n\r\n--BbC04y\r\nContent-disposition: attachment; filename=\"file1.txt\"\r\nContent-Type: text/plain\r\n\r\nfiledata1\r\n--BbC04y\r\nContent-disposition: attachment; filename=\"file2.gif\"\r\nContent-type: image/gif\r\nContent-Transfer-Encoding: binary\r\n\r\nfiledata2\r\n--BbC04y--\r\n--AaB03x--"
+#define FORM_NESTED_START (FORM_END + 5)
+  "field1", NULL, NULL, NULL, "Jane Blow",
+  "pics", "file1.txt", "text/plain", NULL, "filedata1",
+  "pics", "file2.gif", "image/gif", "binary", "filedata2",
+#define FORM_NESTED_END (FORM_NESTED_START + 15)
+  NULL, NULL, NULL, NULL, NULL,
+#define URL_EMPTY_VALUE_DATA "key1=value1&key2=&key3="
+#define URL_EMPTY_VALUE_START (FORM_NESTED_END + 5)
+  "key1", NULL, NULL, NULL, "value1",
+  "key2", NULL, NULL, NULL, "",
+  "key3", NULL, NULL, NULL, "",
+#define URL_EMPTY_VALUE_END (URL_EMPTY_VALUE_START + 15)
+  NULL, NULL, NULL, NULL, NULL
+};
+
+static int
+mismatch (const char *a, const char *b)
+{
+  if (a == b)
+    return 0;
+  if ((a == NULL) || (b == NULL))
+    return 1;
+  return 0 != strcmp (a, b);
+}
+
+static int
+value_checker (void *cls,
+               enum MHD_ValueKind kind,
+               const char *key,
+               const char *filename,
+               const char *content_type,
+               const char *transfer_encoding,
+               const char *data, uint64_t off, size_t size)
+{
+  int *want_off = cls;
+  int idx = *want_off;
+
+#if 0
+  fprintf (stderr,
+           "VC: `%s' `%s' `%s' `%s' `%.*s'\n",
+           key, filename, content_type, transfer_encoding,
+           (int) size,
+           data);
+#endif
+  if ( (0 != off) && (0 == size) )
+    return MHD_YES;
+  if ((idx < 0) ||
+      (want[idx] == NULL) ||
+      (0 != strcmp (key, want[idx])) ||
+      (mismatch (filename, want[idx + 1])) ||
+      (mismatch (content_type, want[idx + 2])) ||
+      (mismatch (transfer_encoding, want[idx + 3])) ||
+      (0 != memcmp (data, &want[idx + 4][off], size)))
+    {
+      *want_off = -1;
+      return MHD_NO;
+    }
+  if (off + size == strlen (want[idx + 4]))
+    *want_off = idx + 5;
+  return MHD_YES;
+
+}
+
+
+static int
+test_urlencoding ()
+{
+  struct MHD_Connection connection;
+  struct MHD_HTTP_Header header;
+  struct MHD_PostProcessor *pp;
+  unsigned int want_off = URL_START;
+  int i;
+  int delta;
+  size_t size;
+
+  memset (&connection, 0, sizeof (struct MHD_Connection));
+  memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+  connection.headers_received = &header;
+  header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+  header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
+  header.kind = MHD_HEADER_KIND;
+  pp = MHD_create_post_processor (&connection,
+                                  1024, &value_checker, &want_off);
+  i = 0;
+  size = strlen (URL_DATA);
+  while (i < size)
+    {
+      delta = 1 + MHD_random_ () % (size - i);
+      MHD_post_process (pp, &URL_DATA[i], delta);
+      i += delta;
+    }
+  MHD_destroy_post_processor (pp);
+  if (want_off != URL_END)
+    return 1;
+  return 0;
+}
+
+
+static int
+test_multipart_garbage ()
+{
+  struct MHD_Connection connection;
+  struct MHD_HTTP_Header header;
+  struct MHD_PostProcessor *pp;
+  unsigned int want_off;
+  size_t size = strlen (FORM_DATA);
+  size_t splitpoint;
+  char xdata[size + 3];
+
+  /* fill in evil garbage at the beginning */
+  xdata[0] = '-';
+  xdata[1] = 'x';
+  xdata[2] = '\r';
+  memcpy (&xdata[3], FORM_DATA, size);
+  size += 3;
+
+  size = strlen (FORM_DATA);
+  for (splitpoint = 1; splitpoint < size; splitpoint++)
+  {
+    want_off = FORM_START;
+    memset (&connection, 0, sizeof (struct MHD_Connection));
+    memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+    connection.headers_received = &header;
+    header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+    header.value =
+      MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
+    header.kind = MHD_HEADER_KIND;
+    pp = MHD_create_post_processor (&connection,
+                                    1024, &value_checker, &want_off);
+    MHD_post_process (pp, xdata, splitpoint);
+    MHD_post_process (pp, &xdata[splitpoint], size - splitpoint);
+    MHD_destroy_post_processor (pp);
+    if (want_off != FORM_END)
+      return (int) splitpoint;
+  }
+  return 0;
+}
+
+
+static int
+test_multipart_splits ()
+{
+  struct MHD_Connection connection;
+  struct MHD_HTTP_Header header;
+  struct MHD_PostProcessor *pp;
+  unsigned int want_off;
+  size_t size;
+  size_t splitpoint;
+
+  size = strlen (FORM_DATA);
+  for (splitpoint = 1; splitpoint < size; splitpoint++)
+  {
+    want_off = FORM_START;
+    memset (&connection, 0, sizeof (struct MHD_Connection));
+    memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+    connection.headers_received = &header;
+    header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+    header.value =
+      MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
+    header.kind = MHD_HEADER_KIND;
+    pp = MHD_create_post_processor (&connection,
+                                    1024, &value_checker, &want_off);
+    MHD_post_process (pp, FORM_DATA, splitpoint);
+    MHD_post_process (pp, &FORM_DATA[splitpoint], size - splitpoint);
+    MHD_destroy_post_processor (pp);
+    if (want_off != FORM_END)
+      return (int) splitpoint;
+  }
+  return 0;
+}
+
+
+static int
+test_multipart ()
+{
+  struct MHD_Connection connection;
+  struct MHD_HTTP_Header header;
+  struct MHD_PostProcessor *pp;
+  unsigned int want_off = FORM_START;
+  int i;
+  int delta;
+  size_t size;
+
+  memset (&connection, 0, sizeof (struct MHD_Connection));
+  memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+  connection.headers_received = &header;
+  header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+  header.value =
+    MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
+  header.kind = MHD_HEADER_KIND;
+  pp = MHD_create_post_processor (&connection,
+                                  1024, &value_checker, &want_off);
+  i = 0;
+  size = strlen (FORM_DATA);
+  while (i < size)
+    {
+      delta = 1 + MHD_random_ () % (size - i);
+      MHD_post_process (pp, &FORM_DATA[i], delta);
+      i += delta;
+    }
+  MHD_destroy_post_processor (pp);
+  if (want_off != FORM_END)
+    return 2;
+  return 0;
+}
+
+
+static int
+test_nested_multipart ()
+{
+  struct MHD_Connection connection;
+  struct MHD_HTTP_Header header;
+  struct MHD_PostProcessor *pp;
+  unsigned int want_off = FORM_NESTED_START;
+  int i;
+  int delta;
+  size_t size;
+
+  memset (&connection, 0, sizeof (struct MHD_Connection));
+  memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+  connection.headers_received = &header;
+  header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+  header.value =
+    MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
+  header.kind = MHD_HEADER_KIND;
+  pp = MHD_create_post_processor (&connection,
+                                  1024, &value_checker, &want_off);
+  i = 0;
+  size = strlen (FORM_NESTED_DATA);
+  while (i < size)
+    {
+      delta = 1 + MHD_random_ () % (size - i);
+      MHD_post_process (pp, &FORM_NESTED_DATA[i], delta);
+      i += delta;
+    }
+  MHD_destroy_post_processor (pp);
+  if (want_off != FORM_NESTED_END)
+    return 4;
+  return 0;
+}
+
+
+static int
+test_empty_value ()
+{
+  struct MHD_Connection connection;
+  struct MHD_HTTP_Header header;
+  struct MHD_PostProcessor *pp;
+  unsigned int want_off = URL_EMPTY_VALUE_START;
+  int i;
+  int delta;
+  size_t size;
+
+  memset (&connection, 0, sizeof (struct MHD_Connection));
+  memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+  connection.headers_received = &header;
+  header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+  header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
+  header.kind = MHD_HEADER_KIND;
+  pp = MHD_create_post_processor (&connection,
+                                  1024, &value_checker, &want_off);
+  i = 0;
+  size = strlen (URL_EMPTY_VALUE_DATA);
+  while (i < size)
+    {
+      delta = 1 + MHD_random_ () % (size - i);
+      MHD_post_process (pp, &URL_EMPTY_VALUE_DATA[i], delta);
+      i += delta;
+    }
+  MHD_destroy_post_processor (pp);
+  if (want_off != URL_EMPTY_VALUE_END)
+    return 8;
+  return 0;
+}
+
+
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  errorCount += test_multipart_splits ();
+  errorCount += test_multipart_garbage ();
+  errorCount += test_urlencoding ();
+  errorCount += test_multipart ();
+  errorCount += test_nested_multipart ();
+  errorCount += test_empty_value ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/microhttpd/test_postprocessor_amp.c b/src/microhttpd/test_postprocessor_amp.c
new file mode 100644
index 0000000..73f72f9
--- /dev/null
+++ b/src/microhttpd/test_postprocessor_amp.c
@@ -0,0 +1,47 @@
+#include "platform.h"
+#include "microhttpd.h"
+#include "internal.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+
+int check_post(void *cls, enum MHD_ValueKind kind, const char* key,
+                 const char* filename, const char* content_type,
+                 const char* content_encoding, const char* data,
+                 uint64_t off, size_t size)
+{
+  if ((0 != strcmp(key, "a")) && (0 != strcmp(key, "b")))
+    {
+      printf("ERROR: got unexpected '%s'\n", key);
+    }
+
+  return MHD_YES;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Connection connection;
+  struct MHD_HTTP_Header header;
+  struct MHD_PostProcessor *pp;
+
+  memset (&connection, 0, sizeof (struct MHD_Connection));
+  memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+  connection.headers_received = &header;
+  header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+  header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
+  header.kind = MHD_HEADER_KIND;
+
+  pp = MHD_create_post_processor (&connection,
+                                  4096, &check_post, NULL);
+
+  const char* post = "a=xx+xx+xxx+xxxxx+xxxx+xxxxxxxx+xxx+xxxxxx+xxx+xxx+xxxxxxx+xxxxx%0A+++++++xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%0A+++++++--%3E%0A++++++++++++++%3Cxxxxx+xxxxx%3D%22xxx%25%22%3E%0A+++++++++++%3Cxx%3E%0A+++++++++++++++%3Cxx+xxxxxxx%3D%22x%22+xxxxx%3D%22xxxxx%22%3E%0A+++++++++++++++++++%3Cxxxxx+xxxxx%3D%22xxx%25%22%3E%0A+++++++++++++++++++++++%3Cxx%3E%0A+++++++++++++++++++++++++++%3Cxx+xxxxx%3D%22xxxx%22%3E%0A+++++++++++++++++++++++++++++++%3Cx+xxxxx%3D%22xxxx-xxxxx%3Axxxxx%22%3Exxxxx%3A%3C%2Fx%3E%0A%0A+++++++++++++++++++++++++++++++%3Cx+xxxxx%3D%22xxxx-xxxxx%3Axxxxx%22%3Exxx%3A%3C%2Fx%3E%0A%0A+++++++++++++++++++++++++++++++%3Cx+xxxxx%3D%22xxxx-xxxxx%3Axxxxx%3B+xxxx-xxxxxx%3A+xxxx%3B%22%3Exxxxx+xxxxx%3A%3C%2Fx%3E%0A+++++++++++++++++++++++++++%3C%2Fxx%3E%0A+++++++++++++++++++++++%3C%2Fxx%3E%0A+++++++++++++++++++%3C%2Fxxxxx%3E%0A+++++++++++++++%3C%2Fxx%3E%0A+++++++++++++++%3Cxx+xxxxx%3D%22xxxx-xxxxx%3A+xxxxx%3B+xxxxx%3A+xxxx%22%3E%26xxxxx%3B+%3Cxxxx%0A+++++++++++++++++++++++xxxxx%3D%22xxxxxxxxxxxxxxx%22%3Exxxx.xx%3C%2Fxxxx%3E%0A+++++++++++++++%3C%2Fxx%3E%0A+++++++++++%3C%2Fxx%3E%0A++++++++++++++++++++++++++%3Cxx%3E%0A+++++++++++++++++++%3Cxx+xxxxx%3D%22xxxx-xxxxx%3A+xxxxx%3B+xxxxx%3A+xxxx%22%3E%26xxxxx%3B+%3Cxxxx%0A+++++++++++++++++++++++++++xxxxx%3D%22xxxxxxxxxxxxxxx%22%3Exxx.xx%3C%2Fxxxx%3E%0A+++++++++++++++++++%3C%2Fxx%3E%0A+++++++++++++++%3C%2Fxx%3E%0A++++++++++++++++++++++%3Cxx%3E%0A+++++++++++++++%3Cxx+xxxxx%3D%22xxxx-xxxxx%3A+xxxxx%3Bxxxx-xxxxxx%3A+xxxx%3B+xxxxx%3A+xxxx%22%3E%26xxxxx%3B+%3Cxxxx%0A+++++++++++++++++++++++xxxxx%3D%22xxxxxxxxxxxxxxx%22%3Exxxx.xx%3C%2Fxxxx%3E%3C%2Fxx%3E%0A+++++++++++%3C%2Fxx%3E%0A+++++++%3C%2Fxxxxx%3E%0A+++++++%3C%21--%0A+++++++xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%0A+++++++xxx+xx+xxxxx+xxxxxxx+xxxxxxx%0A+++++++xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%0A+++++++--%3E%0A+++%3C%2Fxxx%3E%0A%0A%0A%0A+++%3Cxxx+xxxxx%3D%22xxxxxxxxx%22+xx%3D%22xxxxxxxxx%22%3E%3C%2Fxxx%3E%0A%0A+++%3Cxxx+xx%3D%22xxxx%22+xxxxx%3D%22xxxx%22%3E%0A+++++++%3Cxxxxx+xxxxx%3D%22xxxxxxxxx%22%3E%0A+++++++++++%3Cxx%3E%0A+++++++++++++++%3Cxx+xxxxxxx%3D%22x%22+xx%3D%22xxxxxxxxxxxxx%22+xxxxx%3D%22xxxxxxxxxxxxx%22%3E%0A+++++++++++++++++++%3Cxxx+xx%3D%22xxxxxx%22%3E%3C%2Fxxx%3E%0A+++++++++++++++%3C%2Fxx%3E%0A+++++++++++%3C%2Fxx%3E%0A+++++++++++%3Cxx%3E%0A+++++++++++++++%3Cxx+xx%3D%22xxxxxxxxxxxxxxxxx%22+xxxxx%3D%22xxxxxxxxxxxxxxxxx%22%3E%3C%2Fxx%3E%0A+++++++++++++++%3Cxx+xx%3D%22xxxxxxxxxxxxxx%22+xxxxx%3D%22xxxxxxxxxxxxxx%22%3E%0A+++++++++++++++++++%3Cxxx+xx%3D%22xxxxxxx%22%3E%3C%2Fxxx%3E%0A+++++++++++++++%3C%2Fxx%3E%0A+++++++++++%3C%2Fxx%3E%0A+++++++++++%3Cxx%3E%0A+++++++++++++++%3Cxx+xxxxxxx%3D%22x%22+xx%3D%22xxxxxxxxxxxxx%22+xxxxx%3D%22xxxxxxxxxxxxx%22%3E%0A+++++++++++++++++++%3Cxxx+xx%3D%22xxxxxx%22%3E%3C%2Fxxx%3E%0A+++++++++++++++%3C%2Fxx%3E%0A+++++++++++%3C%2Fxx%3E%0A+++++++%3C%2Fxxxxx%3E%0A+++%3C%2Fxxx%3E%0A%3C%2Fxxx%3E%0A%0A%3Cxxx+xx%3D%22xxxxxx%22%3E%3C%2Fxxx%3E%0A%0A%3C%2Fxxxx%3E%0A%3C%2Fxxxx%3E+&b=value";
+
+  MHD_post_process (pp, post, strlen(post));
+  MHD_destroy_post_processor (pp);
+
+  return 0;
+}
+
diff --git a/src/microhttpd/test_postprocessor_large.c b/src/microhttpd/test_postprocessor_large.c
new file mode 100644
index 0000000..0af4ab9
--- /dev/null
+++ b/src/microhttpd/test_postprocessor_large.c
@@ -0,0 +1,105 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2008 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_postprocessor_large.c
+ * @brief  Testcase with very large input for postprocessor
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "microhttpd.h"
+#include "internal.h"
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+static int
+value_checker (void *cls,
+               enum MHD_ValueKind kind,
+               const char *key,
+               const char *filename,
+               const char *content_type,
+               const char *transfer_encoding,
+               const char *data, uint64_t off, size_t size)
+{
+  unsigned int *pos = cls;
+#if 0
+  fprintf (stderr,
+           "VC: %llu %u `%s' `%s' `%s' `%s' `%.*s'\n",
+           off, size,
+           key, filename, content_type, transfer_encoding, size, data);
+#endif
+  if (size == 0)
+    return MHD_YES;
+  *pos += size;
+  return MHD_YES;
+
+}
+
+
+static int
+test_simple_large ()
+{
+  struct MHD_Connection connection;
+  struct MHD_HTTP_Header header;
+  struct MHD_PostProcessor *pp;
+  int i;
+  int delta;
+  size_t size;
+  char data[102400];
+  unsigned int pos;
+
+  pos = 0;
+  memset (data, 'A', sizeof (data));
+  memcpy (data, "key=", 4);
+  data[sizeof (data) - 1] = '\0';
+  memset (&connection, 0, sizeof (struct MHD_Connection));
+  memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+  connection.headers_received = &header;
+  header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+  header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
+  header.kind = MHD_HEADER_KIND;
+  pp = MHD_create_post_processor (&connection, 1024, &value_checker, &pos);
+  i = 0;
+  size = strlen (data);
+  while (i < size)
+    {
+      delta = 1 + MHD_random_ () % (size - i);
+      MHD_post_process (pp, &data[i], delta);
+      i += delta;
+    }
+  MHD_destroy_post_processor (pp);
+  if (pos != sizeof (data) - 5) /* minus 0-termination and 'key=' */
+    return 1;
+  return 0;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  errorCount += test_simple_large ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/microhttpd/tsearch.c b/src/microhttpd/tsearch.c
new file mode 100644
index 0000000..8889f32
--- /dev/null
+++ b/src/microhttpd/tsearch.c
@@ -0,0 +1,125 @@
+/*	$NetBSD: tsearch.c,v 1.3 1999/09/16 11:45:37 lukem Exp $	*/
+
+/*
+ * Tree search generalized from Knuth (6.2.2) Algorithm T just like
+ * the AT&T man page says.
+ *
+ * The node_t structure is for internal use only, lint doesn't grok it.
+ *
+ * Written by reading the System V Interface Definition, not the code.
+ *
+ * Totally public domain.
+ */
+
+#ifndef _MSC_FULL_VER
+#include <sys/cdefs.h>
+#endif //! _MSC_FULL_VER
+#define _SEARCH_PRIVATE
+#include "tsearch.h"
+#include <stdlib.h>
+
+/* find or insert datum into search tree */
+void *
+tsearch(vkey, vrootp, compar)
+	const void *vkey;		/* key to be located */
+	void **vrootp;			/* address of tree root */
+	int (*compar)(const void *, const void *);
+{
+	node_t *q;
+	node_t **rootp = (node_t **)vrootp;
+
+	if (rootp == NULL)
+		return NULL;
+
+	while (*rootp != NULL) {	/* Knuth's T1: */
+		int r;
+
+		if ((r = (*compar)(vkey, (*rootp)->key)) == 0)	/* T2: */
+			return *rootp;		/* we found it! */
+
+		rootp = (r < 0) ?
+		    &(*rootp)->llink :		/* T3: follow left branch */
+		    &(*rootp)->rlink;		/* T4: follow right branch */
+	}
+
+	q = malloc(sizeof(node_t));		/* T5: key not found */
+	if (q != 0) {				/* make new node */
+		*rootp = q;			/* link new node to old */
+		/* LINTED const castaway ok */
+		q->key = (void *)vkey;		/* initialize new node */
+		q->llink = q->rlink = NULL;
+	}
+	return q;
+}
+
+/* find a node, or return 0 */
+void *
+tfind(vkey, vrootp, compar)
+	const void *vkey;		/* key to be found */
+	void * const *vrootp;		/* address of the tree root */
+	int (*compar)(const void *, const void *);
+{
+	node_t **rootp = (node_t **)vrootp;
+
+	if (rootp == NULL)
+		return NULL;
+
+	while (*rootp != NULL) {		/* T1: */
+		int r;
+
+		if ((r = (*compar)(vkey, (*rootp)->key)) == 0)	/* T2: */
+			return *rootp;		/* key found */
+		rootp = (r < 0) ?
+		    &(*rootp)->llink :		/* T3: follow left branch */
+		    &(*rootp)->rlink;		/* T4: follow right branch */
+	}
+	return NULL;
+}
+
+/*
+ * delete node with given key
+ *
+ * vkey:   key to be deleted
+ * vrootp: address of the root of the tree
+ * compar: function to carry out node comparisons
+ */
+void *
+tdelete(const void * __restrict vkey, void ** __restrict vrootp,
+    int (*compar)(const void *, const void *))
+{
+	node_t **rootp = (node_t **)vrootp;
+	node_t *p, *q, *r;
+	int cmp;
+
+	if (rootp == NULL || (p = *rootp) == NULL)
+		return NULL;
+
+	while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0) {
+		p = *rootp;
+		rootp = (cmp < 0) ?
+		    &(*rootp)->llink :		/* follow llink branch */
+		    &(*rootp)->rlink;		/* follow rlink branch */
+		if (*rootp == NULL)
+			return NULL;		/* key not found */
+	}
+	r = (*rootp)->rlink;			/* D1: */
+	if ((q = (*rootp)->llink) == NULL)	/* Left NULL? */
+		q = r;
+	else if (r != NULL) {			/* Right link is NULL? */
+		if (r->llink == NULL) {		/* D2: Find successor */
+			r->llink = q;
+			q = r;
+		} else {			/* D3: Find NULL link */
+			for (q = r->llink; q->llink != NULL; q = r->llink)
+				r = q;
+			r->llink = q->rlink;
+			q->llink = (*rootp)->llink;
+			q->rlink = (*rootp)->rlink;
+		}
+	}
+	free(*rootp);				/* D4: Free node */
+	*rootp = q;				/* link parent to new node */
+	return p;
+}
+
+/* end of tsearch.c */
diff --git a/src/microhttpd/tsearch.h b/src/microhttpd/tsearch.h
new file mode 100644
index 0000000..ec9c92d
--- /dev/null
+++ b/src/microhttpd/tsearch.h
@@ -0,0 +1,50 @@
+/*-
+ * Written by J.T. Conklin <jtc@netbsd.org>
+ * Public domain.
+ *
+ *	$NetBSD: search.h,v 1.12 1999/02/22 10:34:28 christos Exp $
+ * $FreeBSD: release/9.0.0/include/search.h 105250 2002-10-16 14:29:23Z robert $
+ */
+
+#ifndef _SEARCH_H_
+#define _SEARCH_H_
+
+#ifndef _MSC_FULL_VER
+#include <sys/cdefs.h>
+#endif  /* _MSC_FULL_VER */
+#if !defined(__BEGIN_DECLS) || !defined(__END_DECLS)
+#if defined(__cplusplus)
+#define	__BEGIN_DECLS	extern "C" {
+#define	__END_DECLS	};
+#else  /* !__cplusplus */
+#define	__BEGIN_DECLS
+#define	__END_DECLS
+#endif /* !__cplusplus */
+#endif /* !__BEGIN_DECLS || !__END_DECLS */
+#include <sys/types.h>
+
+typedef	enum {
+	preorder,
+	postorder,
+	endorder,
+	leaf
+} VISIT;
+
+#ifdef _SEARCH_PRIVATE
+typedef	struct node {
+	char         *key;
+	struct node  *llink, *rlink;
+} node_t;
+#endif
+
+__BEGIN_DECLS
+void	*tdelete(const void * __restrict, void ** __restrict,
+	    int (*)(const void *, const void *));
+void	*tfind(const void *, void * const *,
+	    int (*)(const void *, const void *));
+void	*tsearch(const void *, void **, int (*)(const void *, const void *));
+void	 twalk(const void *, void (*)(const void *, VISIT, int));
+void	 tdestroy(void *, void (*)(void *));
+__END_DECLS
+
+#endif /* !_SEARCH_H_ */
diff --git a/src/microspdy/Makefile.am b/src/microspdy/Makefile.am
new file mode 100644
index 0000000..7fb2c37
--- /dev/null
+++ b/src/microspdy/Makefile.am
@@ -0,0 +1,40 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = \
+  -I$(top_srcdir)/src/include \
+  -I$(top_srcdir)/src/microspdy
+
+AM_CFLAGS = $(HIDDEN_VISIBILITY_CFLAGS)
+
+
+lib_LTLIBRARIES = \
+  libmicrospdy.la
+
+libmicrospdy_la_SOURCES = \
+  io.h io.c \
+  io_openssl.h io_openssl.c \
+  io_raw.h io_raw.c \
+  structures.h structures.c \
+  internal.h internal.c \
+  daemon.h daemon.c \
+  stream.h stream.c \
+  compression.h compression.c \
+  session.h session.c \
+  applicationlayer.c applicationlayer.h \
+  alstructures.c alstructures.h 
+libmicrospdy_la_LIBADD = \
+  $(SPDY_LIBDEPS)
+
+libmicrospdy_la_LDFLAGS = \
+  $(SPDY_LIB_LDFLAGS)
+
+libmicrospdy_la_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(SPDY_LIB_CPPFLAGS) \
+  -DBUILDING_MHD_LIB=1
+
+libmicrospdy_la_CFLAGS = -Wextra \
+  $(AM_CFLAGS) $(SPDY_LIB_CFLAGS)
+
+
+if USE_COVERAGE
+  AM_CFLAGS += --coverage
+endif
diff --git a/src/microspdy/Makefile.in b/src/microspdy/Makefile.in
new file mode 100644
index 0000000..d1a4588
--- /dev/null
+++ b/src/microspdy/Makefile.in
@@ -0,0 +1,820 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 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 = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+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@
+@USE_COVERAGE_TRUE@am__append_1 = --coverage
+subdir = src/microspdy
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_compile_flags.m4 \
+	$(top_srcdir)/m4/ax_append_flag.m4 \
+	$(top_srcdir)/m4/ax_check_compile_flag.m4 \
+	$(top_srcdir)/m4/ax_check_link_flag.m4 \
+	$(top_srcdir)/m4/ax_check_openssl.m4 \
+	$(top_srcdir)/m4/ax_count_cpus.m4 \
+	$(top_srcdir)/m4/ax_have_epoll.m4 \
+	$(top_srcdir)/m4/ax_pthread.m4 \
+	$(top_srcdir)/m4/ax_require_defined.m4 \
+	$(top_srcdir)/m4/libcurl.m4 $(top_srcdir)/m4/libgcrypt.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)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/MHD_config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(libdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libmicrospdy_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am_libmicrospdy_la_OBJECTS = libmicrospdy_la-io.lo \
+	libmicrospdy_la-io_openssl.lo libmicrospdy_la-io_raw.lo \
+	libmicrospdy_la-structures.lo libmicrospdy_la-internal.lo \
+	libmicrospdy_la-daemon.lo libmicrospdy_la-stream.lo \
+	libmicrospdy_la-compression.lo libmicrospdy_la-session.lo \
+	libmicrospdy_la-applicationlayer.lo \
+	libmicrospdy_la-alstructures.lo
+libmicrospdy_la_OBJECTS = $(am_libmicrospdy_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 = 
+libmicrospdy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(libmicrospdy_la_CFLAGS) $(CFLAGS) $(libmicrospdy_la_LDFLAGS) \
+	$(LDFLAGS) -o $@
+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__depfiles_maybe = depfiles
+am__mv = mv -f
+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 = $(libmicrospdy_la_SOURCES)
+DIST_SOURCES = $(libmicrospdy_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
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPU_COUNT = @CPU_COUNT@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_CPPFLAGS = @GNUTLS_CPPFLAGS@
+GNUTLS_LDFLAGS = @GNUTLS_LDFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+HAVE_CURL_BINARY = @HAVE_CURL_BINARY@
+HAVE_MAKEINFO_BINARY = @HAVE_MAKEINFO_BINARY@
+HIDDEN_VISIBILITY_CFLAGS = @HIDDEN_VISIBILITY_CFLAGS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSPDY_VERSION_AGE = @LIBSPDY_VERSION_AGE@
+LIBSPDY_VERSION_CURRENT = @LIBSPDY_VERSION_CURRENT@
+LIBSPDY_VERSION_REVISION = @LIBSPDY_VERSION_REVISION@
+LIBTOOL = @LIBTOOL@
+LIB_VERSION_AGE = @LIB_VERSION_AGE@
+LIB_VERSION_CURRENT = @LIB_VERSION_CURRENT@
+LIB_VERSION_REVISION = @LIB_VERSION_REVISION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MHD_LIBDEPS = @MHD_LIBDEPS@
+MHD_LIBDEPS_PKGCFG = @MHD_LIBDEPS_PKGCFG@
+MHD_LIB_CFLAGS = @MHD_LIB_CFLAGS@
+MHD_LIB_CPPFLAGS = @MHD_LIB_CPPFLAGS@
+MHD_LIB_LDFLAGS = @MHD_LIB_LDFLAGS@
+MHD_REQ_PRIVATE = @MHD_REQ_PRIVATE@
+MKDIR_P = @MKDIR_P@
+MS_LIB_TOOL = @MS_LIB_TOOL@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_INCLUDES = @OPENSSL_INCLUDES@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@
+PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@
+PACKAGE_VERSION_SUBMINOR = @PACKAGE_VERSION_SUBMINOR@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPDY_LIBDEPS = @SPDY_LIBDEPS@
+SPDY_LIB_CFLAGS = @SPDY_LIB_CFLAGS@
+SPDY_LIB_CPPFLAGS = @SPDY_LIB_CPPFLAGS@
+SPDY_LIB_LDFLAGS = @SPDY_LIB_LDFLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+_libcurl_config = @_libcurl_config@
+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_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@
+ax_pthread_config = @ax_pthread_config@
+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@
+have_socat = @have_socat@
+have_zzuf = @have_zzuf@
+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@
+lt_cv_objdir = @lt_cv_objdir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = \
+  -I$(top_srcdir)/src/include \
+  -I$(top_srcdir)/src/microspdy
+
+AM_CFLAGS = $(HIDDEN_VISIBILITY_CFLAGS) $(am__append_1)
+lib_LTLIBRARIES = \
+  libmicrospdy.la
+
+libmicrospdy_la_SOURCES = \
+  io.h io.c \
+  io_openssl.h io_openssl.c \
+  io_raw.h io_raw.c \
+  structures.h structures.c \
+  internal.h internal.c \
+  daemon.h daemon.c \
+  stream.h stream.c \
+  compression.h compression.c \
+  session.h session.c \
+  applicationlayer.c applicationlayer.h \
+  alstructures.c alstructures.h 
+
+libmicrospdy_la_LIBADD = \
+  $(SPDY_LIBDEPS)
+
+libmicrospdy_la_LDFLAGS = \
+  $(SPDY_LIB_LDFLAGS)
+
+libmicrospdy_la_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(SPDY_LIB_CPPFLAGS) \
+  -DBUILDING_MHD_LIB=1
+
+libmicrospdy_la_CFLAGS = -Wextra \
+  $(AM_CFLAGS) $(SPDY_LIB_CFLAGS)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .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) --gnu src/microspdy/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/microspdy/Makefile
+.PRECIOUS: 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__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	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):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+	}
+
+uninstall-libLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+	done
+
+clean-libLTLIBRARIES:
+	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+	@list='$(lib_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}; \
+	}
+
+libmicrospdy.la: $(libmicrospdy_la_OBJECTS) $(libmicrospdy_la_DEPENDENCIES) $(EXTRA_libmicrospdy_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(libmicrospdy_la_LINK) -rpath $(libdir) $(libmicrospdy_la_OBJECTS) $(libmicrospdy_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrospdy_la-alstructures.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrospdy_la-applicationlayer.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrospdy_la-compression.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrospdy_la-daemon.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrospdy_la-internal.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrospdy_la-io.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrospdy_la-io_openssl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrospdy_la-io_raw.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrospdy_la-session.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrospdy_la-stream.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmicrospdy_la-structures.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libmicrospdy_la-io.lo: io.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -MT libmicrospdy_la-io.lo -MD -MP -MF $(DEPDIR)/libmicrospdy_la-io.Tpo -c -o libmicrospdy_la-io.lo `test -f 'io.c' || echo '$(srcdir)/'`io.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrospdy_la-io.Tpo $(DEPDIR)/libmicrospdy_la-io.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='io.c' object='libmicrospdy_la-io.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -c -o libmicrospdy_la-io.lo `test -f 'io.c' || echo '$(srcdir)/'`io.c
+
+libmicrospdy_la-io_openssl.lo: io_openssl.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -MT libmicrospdy_la-io_openssl.lo -MD -MP -MF $(DEPDIR)/libmicrospdy_la-io_openssl.Tpo -c -o libmicrospdy_la-io_openssl.lo `test -f 'io_openssl.c' || echo '$(srcdir)/'`io_openssl.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrospdy_la-io_openssl.Tpo $(DEPDIR)/libmicrospdy_la-io_openssl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='io_openssl.c' object='libmicrospdy_la-io_openssl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -c -o libmicrospdy_la-io_openssl.lo `test -f 'io_openssl.c' || echo '$(srcdir)/'`io_openssl.c
+
+libmicrospdy_la-io_raw.lo: io_raw.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -MT libmicrospdy_la-io_raw.lo -MD -MP -MF $(DEPDIR)/libmicrospdy_la-io_raw.Tpo -c -o libmicrospdy_la-io_raw.lo `test -f 'io_raw.c' || echo '$(srcdir)/'`io_raw.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrospdy_la-io_raw.Tpo $(DEPDIR)/libmicrospdy_la-io_raw.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='io_raw.c' object='libmicrospdy_la-io_raw.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -c -o libmicrospdy_la-io_raw.lo `test -f 'io_raw.c' || echo '$(srcdir)/'`io_raw.c
+
+libmicrospdy_la-structures.lo: structures.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -MT libmicrospdy_la-structures.lo -MD -MP -MF $(DEPDIR)/libmicrospdy_la-structures.Tpo -c -o libmicrospdy_la-structures.lo `test -f 'structures.c' || echo '$(srcdir)/'`structures.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrospdy_la-structures.Tpo $(DEPDIR)/libmicrospdy_la-structures.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='structures.c' object='libmicrospdy_la-structures.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -c -o libmicrospdy_la-structures.lo `test -f 'structures.c' || echo '$(srcdir)/'`structures.c
+
+libmicrospdy_la-internal.lo: internal.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -MT libmicrospdy_la-internal.lo -MD -MP -MF $(DEPDIR)/libmicrospdy_la-internal.Tpo -c -o libmicrospdy_la-internal.lo `test -f 'internal.c' || echo '$(srcdir)/'`internal.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrospdy_la-internal.Tpo $(DEPDIR)/libmicrospdy_la-internal.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='internal.c' object='libmicrospdy_la-internal.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -c -o libmicrospdy_la-internal.lo `test -f 'internal.c' || echo '$(srcdir)/'`internal.c
+
+libmicrospdy_la-daemon.lo: daemon.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -MT libmicrospdy_la-daemon.lo -MD -MP -MF $(DEPDIR)/libmicrospdy_la-daemon.Tpo -c -o libmicrospdy_la-daemon.lo `test -f 'daemon.c' || echo '$(srcdir)/'`daemon.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrospdy_la-daemon.Tpo $(DEPDIR)/libmicrospdy_la-daemon.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='daemon.c' object='libmicrospdy_la-daemon.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -c -o libmicrospdy_la-daemon.lo `test -f 'daemon.c' || echo '$(srcdir)/'`daemon.c
+
+libmicrospdy_la-stream.lo: stream.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -MT libmicrospdy_la-stream.lo -MD -MP -MF $(DEPDIR)/libmicrospdy_la-stream.Tpo -c -o libmicrospdy_la-stream.lo `test -f 'stream.c' || echo '$(srcdir)/'`stream.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrospdy_la-stream.Tpo $(DEPDIR)/libmicrospdy_la-stream.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='stream.c' object='libmicrospdy_la-stream.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -c -o libmicrospdy_la-stream.lo `test -f 'stream.c' || echo '$(srcdir)/'`stream.c
+
+libmicrospdy_la-compression.lo: compression.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -MT libmicrospdy_la-compression.lo -MD -MP -MF $(DEPDIR)/libmicrospdy_la-compression.Tpo -c -o libmicrospdy_la-compression.lo `test -f 'compression.c' || echo '$(srcdir)/'`compression.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrospdy_la-compression.Tpo $(DEPDIR)/libmicrospdy_la-compression.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='compression.c' object='libmicrospdy_la-compression.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -c -o libmicrospdy_la-compression.lo `test -f 'compression.c' || echo '$(srcdir)/'`compression.c
+
+libmicrospdy_la-session.lo: session.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -MT libmicrospdy_la-session.lo -MD -MP -MF $(DEPDIR)/libmicrospdy_la-session.Tpo -c -o libmicrospdy_la-session.lo `test -f 'session.c' || echo '$(srcdir)/'`session.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrospdy_la-session.Tpo $(DEPDIR)/libmicrospdy_la-session.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='session.c' object='libmicrospdy_la-session.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -c -o libmicrospdy_la-session.lo `test -f 'session.c' || echo '$(srcdir)/'`session.c
+
+libmicrospdy_la-applicationlayer.lo: applicationlayer.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -MT libmicrospdy_la-applicationlayer.lo -MD -MP -MF $(DEPDIR)/libmicrospdy_la-applicationlayer.Tpo -c -o libmicrospdy_la-applicationlayer.lo `test -f 'applicationlayer.c' || echo '$(srcdir)/'`applicationlayer.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrospdy_la-applicationlayer.Tpo $(DEPDIR)/libmicrospdy_la-applicationlayer.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='applicationlayer.c' object='libmicrospdy_la-applicationlayer.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -c -o libmicrospdy_la-applicationlayer.lo `test -f 'applicationlayer.c' || echo '$(srcdir)/'`applicationlayer.c
+
+libmicrospdy_la-alstructures.lo: alstructures.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -MT libmicrospdy_la-alstructures.lo -MD -MP -MF $(DEPDIR)/libmicrospdy_la-alstructures.Tpo -c -o libmicrospdy_la-alstructures.lo `test -f 'alstructures.c' || echo '$(srcdir)/'`alstructures.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libmicrospdy_la-alstructures.Tpo $(DEPDIR)/libmicrospdy_la-alstructures.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='alstructures.c' object='libmicrospdy_la-alstructures.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrospdy_la_CPPFLAGS) $(CPPFLAGS) $(libmicrospdy_la_CFLAGS) $(CFLAGS) -c -o libmicrospdy_la-alstructures.lo `test -f 'alstructures.c' || echo '$(srcdir)/'`alstructures.c
+
+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: $(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:
+	for dir in "$(DESTDIR)$(libdir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+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-libLTLIBRARIES clean-libtool \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-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-libLTLIBRARIES
+
+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 -rf ./$(DEPDIR)
+	-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: uninstall-libLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libLTLIBRARIES clean-libtool 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-libLTLIBRARIES 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 uninstall-libLTLIBRARIES
+
+
+# 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/src/microspdy/alstructures.c b/src/microspdy/alstructures.c
new file mode 100644
index 0000000..b588a1c
--- /dev/null
+++ b/src/microspdy/alstructures.c
@@ -0,0 +1,41 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file alstructures.c
+ * @brief  structures only for the application layer
+ * @author Andrey Uzunov
+ */
+ 
+#include "platform.h"
+#include "alstructures.h"
+#include "internal.h"
+
+void
+SPDY_destroy_request (struct SPDY_Request *request)
+{
+	if(NULL == request)
+	{
+		SPDYF_DEBUG("request is NULL");
+		return;
+	}
+	//strings into request struct are just references to strings in
+	//headers, so no need to free them twice
+	SPDY_name_value_destroy(request->headers);
+	free(request);
+}
diff --git a/src/microspdy/alstructures.h b/src/microspdy/alstructures.h
new file mode 100644
index 0000000..2eb36e8
--- /dev/null
+++ b/src/microspdy/alstructures.h
@@ -0,0 +1,79 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file alstructures.h
+ * @brief  structures only for the application layer
+ * @author Andrey Uzunov
+ */
+
+#ifndef ALSTRUCTURES_H
+#define ALSTRUCTURES_H
+
+#include "platform.h"
+
+
+/**
+ * Represents a SPDY request.
+ */
+struct SPDY_Request
+{
+	/**
+	 * SPDY stream in whose context the request was received
+	 */
+	struct SPDYF_Stream *stream;
+	
+	/**
+	 * Other HTTP headers from the request
+	 */
+	struct SPDY_NameValue *headers;
+	
+	/**
+	 * HTTP method
+	 */
+	char *method;
+	
+	/**
+	 * HTTP path
+	 */
+	char *path;
+	
+	/**
+	 * HTTP version just like in HTTP request/response: 
+	 * 			"HTTP/1.0" or "HTTP/1.1" currently
+	 */
+	char *version;
+	
+	/**
+	 * called host as in HTTP
+	 */
+	char *host;
+	
+	/**
+	 * The scheme used ("http" or "https")
+	 */
+	char *scheme;
+
+	/**
+	 * Extra field to be used by the user with set/get func for whatever
+	 * purpose he wants.
+	 */
+	void *user_cls;
+};
+
+#endif
diff --git a/src/microspdy/applicationlayer.c b/src/microspdy/applicationlayer.c
new file mode 100644
index 0000000..bf16b78
--- /dev/null
+++ b/src/microspdy/applicationlayer.c
@@ -0,0 +1,748 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file applicationlayer.c
+ * @brief  SPDY application or HTTP layer
+ * @author Andrey Uzunov
+ */
+ 
+#include "platform.h"
+#include "applicationlayer.h"
+#include "alstructures.h"
+#include "structures.h"
+#include "internal.h"
+#include "daemon.h"
+#include "session.h"
+
+
+void
+spdy_callback_response_done(void *cls,
+						struct SPDY_Response *response,
+						struct SPDY_Request *request,
+						enum SPDY_RESPONSE_RESULT status,
+						bool streamopened)
+{
+	(void)cls;
+	(void)status;
+	(void)streamopened;
+  
+	SPDY_destroy_request(request);
+	SPDY_destroy_response(response);
+}
+
+
+/**
+ * Callback called when new stream is created. It extracts the info from
+ * the stream to create (HTTP) request object and pass it to the client.
+ *
+ * @param cls
+ * @param stream the new SPDY stream
+ * @return SPDY_YES on success, SPDY_NO on memomry error
+ */
+static int
+spdy_handler_new_stream (void *cls,
+						struct SPDYF_Stream * stream)
+{
+	(void)cls;
+	unsigned int i;
+	char *method = NULL;
+	char *path = NULL;
+	char *version = NULL;
+	char *host = NULL;
+	char *scheme = NULL;
+	struct SPDY_Request * request = NULL;
+	struct SPDY_NameValue * headers = NULL;
+	struct SPDY_NameValue * iterator = stream->headers;
+	struct SPDY_Daemon *daemon;
+	
+	daemon = stream->session->daemon;
+	
+	//if the user doesn't care, ignore it
+	if(NULL == daemon->new_request_cb)
+		return SPDY_YES;
+	
+	if(NULL == (headers=SPDY_name_value_create()))
+		goto free_and_fail;
+	
+	if(NULL==(request = malloc(sizeof(struct SPDY_Request))))
+		goto free_and_fail;
+	
+	memset(request, 0, sizeof(struct SPDY_Request));
+	request->stream = stream;
+	
+	/* extract the mandatory fields from stream->headers' structure
+	 * to pass them to the client */
+	while(iterator != NULL)
+	{
+		if(strcmp(":method",iterator->name) == 0)
+		{
+			if(1 != iterator->num_values)
+				break;
+			method = iterator->value[0];
+		}
+		else if(strcmp(":path",iterator->name) == 0)
+		{
+			if(1 != iterator->num_values)
+				break;
+			path = iterator->value[0];
+		}
+		else if(strcmp(":version",iterator->name) == 0)
+		{
+			if(1 != iterator->num_values)
+				break;
+			version = iterator->value[0];
+		}
+		else if(strcmp(":host",iterator->name) == 0)
+		{
+			//TODO can it have more values?
+			if(1 != iterator->num_values)
+				break;
+			host = iterator->value[0];
+		}
+		else if(strcmp(":scheme",iterator->name) == 0)
+		{
+			if(1 != iterator->num_values)
+				break;
+			scheme = iterator->value[0];
+		}
+		else
+			for(i=0; i<iterator->num_values; ++i)
+				if (SPDY_YES != SPDY_name_value_add(headers,iterator->name,iterator->value[i]))
+        {
+          SPDY_destroy_request(request);
+					goto free_and_fail;
+        }
+		
+		iterator = iterator->next;
+	}
+	
+	request->method=method;
+  request->path=path;
+  request->version=version;
+  request->host=host;
+  request->scheme=scheme;
+  request->headers=headers;
+	
+	//check request validity, all these fields are mandatory for a request
+	if(NULL == method || strlen(method) == 0
+		|| NULL == path || strlen(path) == 0
+		|| NULL == version || strlen(version) == 0
+		|| NULL == host || strlen(host) == 0
+		|| NULL == scheme || strlen(scheme) == 0
+		)
+	{
+		//TODO HTTP 400 Bad Request must be answered
+		
+		SPDYF_DEBUG("Bad request");
+		
+		SPDY_destroy_request(request);
+		
+		return SPDY_YES;
+	}
+	
+	//call client's callback function to notify
+	daemon->new_request_cb(daemon->cls,
+						request,
+						stream->priority,
+                        method,
+                        path,
+                        version,
+                        host,
+                        scheme,
+						headers,
+            !stream->is_in_closed);
+            
+  stream->cls = request;
+
+	return SPDY_YES;
+
+	//for GOTO
+	free_and_fail:
+	
+	SPDY_name_value_destroy(headers);
+	return SPDY_NO;
+}
+
+
+/**
+ * TODO
+ */
+static int
+spdy_handler_new_data (void * cls,
+					 struct SPDYF_Stream *stream,
+					 const void * buf,
+					 size_t size,
+					 bool more)
+{
+  return stream->session->daemon->received_data_cb(cls, stream->cls, buf, size, more);
+}
+
+
+
+/**
+ * Callback to be called when the response queue object was handled and 
+ * the data was already sent or discarded. 
+ *
+ * @param cls
+ * @param response_queue the object which is being handled
+ * @param status shows if actually the response was sent or it was
+ * 			discarded by the lib for any reason (e.g., closing session,
+ * 			closing stream, stopping daemon, etc.). It is possible that
+ * 			status indicates an error but parts of the response headers
+ * 			and/or body (in one
+ * 			or several frames) were already sent to the client.
+ */
+static void
+spdy_handler_response_queue_result(void * cls,
+								struct SPDYF_Response_Queue *response_queue,
+								enum SPDY_RESPONSE_RESULT status)
+{
+	int streamopened;
+	struct SPDY_Request *request = (struct SPDY_Request *)cls;
+	
+	SPDYF_ASSERT( ( (NULL == response_queue->data_frame) &&
+			(NULL != response_queue->control_frame) ) ||
+		      ( (NULL != response_queue->data_frame) &&
+			(NULL == response_queue->control_frame) ),
+		     "response queue must have either control frame or data frame");
+	
+	streamopened = !response_queue->stream->is_out_closed;
+	
+	response_queue->rrcb(response_queue->rrcb_cls, response_queue->response, request, status, streamopened);
+}
+
+
+int
+(SPDY_init) (enum SPDY_IO_SUBSYSTEM io_subsystem, ...)
+{
+	SPDYF_ASSERT(SPDYF_BUFFER_SIZE >= SPDY_MAX_SUPPORTED_FRAME_SIZE,
+		"Buffer size is less than max supported frame size!");
+	SPDYF_ASSERT(SPDY_MAX_SUPPORTED_FRAME_SIZE >= 32,
+		"Max supported frame size must be bigger than the minimal value!");
+	SPDYF_ASSERT(SPDY_IO_SUBSYSTEM_NONE == spdyf_io_initialized,
+		"SPDY_init must be called only once per program or after SPDY_deinit");
+    
+  if(SPDY_IO_SUBSYSTEM_OPENSSL & io_subsystem)
+  {
+    SPDYF_openssl_global_init();
+    spdyf_io_initialized |= SPDY_IO_SUBSYSTEM_OPENSSL;
+  }
+  else if(SPDY_IO_SUBSYSTEM_RAW & io_subsystem)
+  {
+    SPDYF_raw_global_init();
+    spdyf_io_initialized |= SPDY_IO_SUBSYSTEM_RAW;
+  }
+  
+	SPDYF_ASSERT(SPDY_IO_SUBSYSTEM_NONE != spdyf_io_initialized,
+		"SPDY_init could not find even one IO subsystem");
+    
+	return SPDY_YES;
+}
+
+
+void
+SPDY_deinit ()
+{
+	SPDYF_ASSERT(SPDY_IO_SUBSYSTEM_NONE != spdyf_io_initialized,
+		"SPDY_init has not been called!");
+    
+  if(SPDY_IO_SUBSYSTEM_OPENSSL & spdyf_io_initialized)
+    SPDYF_openssl_global_deinit();
+  else if(SPDY_IO_SUBSYSTEM_RAW & spdyf_io_initialized)
+    SPDYF_raw_global_deinit();
+  
+  spdyf_io_initialized = SPDY_IO_SUBSYSTEM_NONE;
+}
+
+
+void 
+SPDY_run (struct SPDY_Daemon *daemon)
+{
+	if(NULL == daemon)
+	{
+		SPDYF_DEBUG("daemon is NULL");
+		return;
+	}
+	
+	SPDYF_run(daemon);
+}
+
+
+int
+SPDY_get_timeout (struct SPDY_Daemon *daemon, 
+		     unsigned long long *timeout)
+{
+	if(NULL == daemon)
+	{
+		SPDYF_DEBUG("daemon is NULL");
+		return SPDY_INPUT_ERROR;
+	}
+	
+	return SPDYF_get_timeout(daemon,timeout);
+}
+
+
+int
+SPDY_get_fdset (struct SPDY_Daemon *daemon,
+				fd_set *read_fd_set,
+				fd_set *write_fd_set, 
+				fd_set *except_fd_set)
+{
+	if(NULL == daemon
+		|| NULL == read_fd_set
+		|| NULL == write_fd_set
+		|| NULL == except_fd_set)
+	{
+		SPDYF_DEBUG("a parameter is NULL");
+		return SPDY_INPUT_ERROR;
+	}
+	
+	return SPDYF_get_fdset(daemon,
+				read_fd_set,
+				write_fd_set, 
+				except_fd_set,
+				false);
+}
+
+
+struct SPDY_Daemon *
+SPDY_start_daemon (uint16_t port,
+				const char *certfile,
+				const char *keyfile,
+		     SPDY_NewSessionCallback nscb,
+		     SPDY_SessionClosedCallback sccb,
+		     SPDY_NewRequestCallback nrcb,
+		     SPDY_NewDataCallback npdcb,
+		     void * cls,
+		     ...)
+{
+	struct SPDY_Daemon *daemon;
+	va_list valist;
+	
+	if(SPDY_IO_SUBSYSTEM_NONE == spdyf_io_initialized)
+	{
+		SPDYF_DEBUG("library not initialized");
+		return NULL;
+	}
+  /*
+   * for now make this checks in framing layer
+	if(NULL == certfile)
+	{
+		SPDYF_DEBUG("certfile is NULL");
+		return NULL;
+	}
+	if(NULL == keyfile)
+	{
+		SPDYF_DEBUG("keyfile is NULL");
+		return NULL;
+	}
+  */
+	
+	va_start(valist, cls);
+	daemon = SPDYF_start_daemon_va ( port,
+				certfile,
+				keyfile,
+		      nscb,
+		      sccb,
+		      nrcb,
+		      npdcb,
+		      &spdy_handler_new_stream,
+		      &spdy_handler_new_data,
+		      cls,
+		      NULL,
+		      valist
+		     );
+	va_end(valist);
+	
+	return daemon;
+}
+
+
+void 
+SPDY_stop_daemon (struct SPDY_Daemon *daemon)
+{	
+	if(NULL == daemon)
+	{
+		SPDYF_DEBUG("daemon is NULL");
+		return;
+	}
+
+	SPDYF_stop_daemon(daemon);
+}
+
+
+struct SPDY_Response *
+SPDY_build_response(int status,
+					const char * statustext,
+					const char * version,
+					struct SPDY_NameValue * headers,
+					const void * data,
+					size_t size)
+{
+	struct SPDY_Response *response = NULL;
+	struct SPDY_NameValue ** all_headers = NULL; //TODO maybe array in stack is enough
+	char *fullstatus = NULL;
+	int ret;
+	int num_hdr_containers = 1;
+	
+	if(NULL == version)
+	{
+		SPDYF_DEBUG("version is NULL");
+		return NULL;
+	}
+	
+	if(NULL == (response = malloc(sizeof(struct SPDY_Response))))
+		goto free_and_fail;
+	memset(response, 0, sizeof(struct SPDY_Response));
+	
+	if(NULL != headers && !SPDYF_name_value_is_empty(headers))
+		num_hdr_containers = 2;
+	
+	if(NULL == (all_headers = malloc(num_hdr_containers * sizeof(struct SPDY_NameValue *))))
+		goto free_and_fail;
+	memset(all_headers, 0, num_hdr_containers * sizeof(struct SPDY_NameValue *));
+	
+	if(2 == num_hdr_containers)
+		all_headers[1] = headers;
+	
+	if(NULL == (all_headers[0] = SPDY_name_value_create()))
+		goto free_and_fail;
+	
+	if(NULL == statustext)
+		ret = asprintf(&fullstatus, "%i", status);
+	else
+		ret = asprintf(&fullstatus, "%i %s", status, statustext); 
+	if(-1 == ret)
+		goto free_and_fail;
+		
+	if(SPDY_YES != SPDY_name_value_add(all_headers[0], ":status", fullstatus))
+		goto free_and_fail;
+		
+	free(fullstatus);
+	fullstatus = NULL;
+	
+	if(SPDY_YES != SPDY_name_value_add(all_headers[0], ":version", version))
+		goto free_and_fail;
+	
+	if(0 >= (response->headers_size = SPDYF_name_value_to_stream(all_headers,
+												num_hdr_containers,
+												&(response->headers))))
+		goto free_and_fail;
+		
+	SPDY_name_value_destroy(all_headers[0]);
+	free(all_headers);
+  all_headers = NULL;
+	
+	if(size > 0)
+	{
+		//copy the data to the response object
+		if(NULL == (response->data = malloc(size)))
+		{
+			free(response->headers);
+			goto free_and_fail;
+		}
+		memcpy(response->data, data, size);
+		response->data_size = size;
+	}
+	
+	return response;
+	
+	//for GOTO
+	free_and_fail:
+	
+	free(fullstatus);
+	if(NULL != all_headers)
+		SPDY_name_value_destroy(all_headers[0]);
+	free(all_headers);
+	free(response);
+	
+	return NULL;
+}
+
+
+struct SPDY_Response *
+SPDY_build_response_with_callback(int status,
+					const char * statustext,
+					const char * version,
+					struct SPDY_NameValue * headers,
+					SPDY_ResponseCallback rcb,
+					void *rcb_cls,
+					uint32_t block_size)
+{
+	struct SPDY_Response *response;
+	
+	if(NULL == rcb)
+	{
+		SPDYF_DEBUG("rcb is NULL");
+		return NULL;
+	}
+	if(block_size > SPDY_MAX_SUPPORTED_FRAME_SIZE)
+	{
+		SPDYF_DEBUG("block_size is wrong");
+		return NULL;
+	}
+	
+	if(0 == block_size)
+		block_size = SPDY_MAX_SUPPORTED_FRAME_SIZE;
+	
+	response = SPDY_build_response(status,
+					statustext,
+					version,
+					headers,
+					NULL,
+					0);
+	
+	if(NULL == response)
+	{
+		return NULL;
+	}
+			
+	response->rcb = rcb;
+	response->rcb_cls = rcb_cls;
+	response->rcb_block_size = block_size;
+	
+	return response;
+}
+
+
+int
+SPDY_queue_response (struct SPDY_Request * request,
+					struct SPDY_Response *response,
+					bool closestream,
+					bool consider_priority,
+					SPDY_ResponseResultCallback rrcb,
+					void * rrcb_cls)
+{
+	struct SPDYF_Response_Queue *headers_to_queue;
+	struct SPDYF_Response_Queue *body_to_queue;
+	SPDYF_ResponseQueueResultCallback frqcb = NULL;
+	void *frqcb_cls = NULL;
+	int int_consider_priority = consider_priority ? SPDY_YES : SPDY_NO;
+	
+	if(NULL == request)
+	{
+		SPDYF_DEBUG("request is NULL");
+		return SPDY_INPUT_ERROR;
+	}
+	if(NULL == response)
+	{
+		SPDYF_DEBUG("request is NULL");
+		return SPDY_INPUT_ERROR;
+	}
+	
+	if(request->stream->is_out_closed
+		|| SPDY_SESSION_STATUS_CLOSING == request->stream->session->status)
+		return SPDY_NO;
+	
+	if(NULL != rrcb)
+	{
+		frqcb_cls = request;
+		frqcb = &spdy_handler_response_queue_result;
+	}
+	
+	if(response->data_size > 0)
+	{	
+		//SYN_REPLY and DATA will be queued
+		
+		if(NULL == (headers_to_queue = SPDYF_response_queue_create(false,
+							response->headers,
+							response->headers_size,
+							response,
+							request->stream,
+							false,
+							NULL,
+							NULL,
+							NULL,
+							NULL)))
+		{
+			return SPDY_NO;
+		}
+		
+		if(NULL == (body_to_queue = SPDYF_response_queue_create(true,
+							response->data,
+							response->data_size,
+							response,
+							request->stream,
+							closestream,
+							frqcb,
+							frqcb_cls,
+							rrcb,
+							rrcb_cls)))
+		{
+			SPDYF_response_queue_destroy(headers_to_queue);
+			return SPDY_NO;
+		}
+							
+		SPDYF_queue_response (headers_to_queue,
+							request->stream->session,
+							int_consider_priority);
+							
+		SPDYF_queue_response (body_to_queue,
+							request->stream->session,
+							int_consider_priority);
+	}
+	else if(NULL == response->rcb)
+	{
+		//no "body" will be queued, e.g. HTTP 404 without body
+		
+		if(NULL == (headers_to_queue = SPDYF_response_queue_create(false,
+							response->headers,
+							response->headers_size,
+							response,
+							request->stream,
+							closestream,
+							frqcb,
+							frqcb_cls,
+							rrcb,
+							rrcb_cls)))
+		{
+			return SPDY_NO;
+		}
+							
+		SPDYF_queue_response (headers_to_queue,
+							request->stream->session,
+							int_consider_priority);
+	}
+	else
+	{
+		//response with callbacks
+		
+		if(NULL == (headers_to_queue = SPDYF_response_queue_create(false,
+							response->headers,
+							response->headers_size,
+							response,
+							request->stream,
+							false,
+							NULL,
+							NULL,
+							NULL,
+							NULL)))
+		{
+			return SPDY_NO;
+		}
+		
+		if(NULL == (body_to_queue = SPDYF_response_queue_create(true,
+							response->data,
+							response->data_size,
+							response,
+							request->stream,
+							closestream,
+							frqcb,
+							frqcb_cls,
+							rrcb,
+							rrcb_cls)))
+		{
+			SPDYF_response_queue_destroy(headers_to_queue);
+			return SPDY_NO;
+		}
+							
+		SPDYF_queue_response (headers_to_queue,
+							request->stream->session,
+							int_consider_priority);
+							
+		SPDYF_queue_response (body_to_queue,
+							request->stream->session,
+							int_consider_priority);
+	}
+		
+	return SPDY_YES;
+}
+
+
+socklen_t
+SPDY_get_remote_addr(struct SPDY_Session * session,
+					 struct sockaddr ** addr)
+{
+	if(NULL == session)
+	{
+		SPDYF_DEBUG("session is NULL");
+		return 0;
+	}
+	
+	*addr = session->addr;
+	
+	return session->addr_len;
+}
+
+
+struct SPDY_Session *
+SPDY_get_session_for_request(const struct SPDY_Request * request)
+{
+	if(NULL == request)
+	{
+		SPDYF_DEBUG("request is NULL");
+		return NULL;
+	}
+	
+	return request->stream->session;
+}
+
+
+void *
+SPDY_get_cls_from_session(struct SPDY_Session * session)
+{
+	if(NULL == session)
+	{
+		SPDYF_DEBUG("session is NULL");
+		return NULL;
+	}
+	
+	return session->user_cls;
+}
+
+
+void
+SPDY_set_cls_to_session(struct SPDY_Session * session,
+							void * cls)
+{
+	if(NULL == session)
+	{
+		SPDYF_DEBUG("session is NULL");
+		return;
+	}
+	
+	session->user_cls = cls;
+}
+
+
+void *
+SPDY_get_cls_from_request(struct SPDY_Request * request)
+{
+	if(NULL == request)
+	{
+		SPDYF_DEBUG("request is NULL");
+		return NULL;
+	}
+	
+	return request->user_cls;
+}
+
+
+void
+SPDY_set_cls_to_request(struct SPDY_Request * request,
+							void * cls)
+{
+	if(NULL == request)
+	{
+		SPDYF_DEBUG("request is NULL");
+		return;
+	}
+	
+	request->user_cls = cls;
+}
diff --git a/src/microspdy/applicationlayer.h b/src/microspdy/applicationlayer.h
new file mode 100644
index 0000000..a36760f
--- /dev/null
+++ b/src/microspdy/applicationlayer.h
@@ -0,0 +1,31 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file applicationlayer.h
+ * @brief  SPDY application or HTTP layer
+ * @author Andrey Uzunov
+ */
+
+#ifndef APPLICATIONLAYER_H
+#define APPLICATIONLAYER_H
+
+#include "platform.h"
+
+
+#endif
diff --git a/src/microspdy/compression.c b/src/microspdy/compression.c
new file mode 100644
index 0000000..532ab64
--- /dev/null
+++ b/src/microspdy/compression.c
@@ -0,0 +1,441 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file compression.c
+ * @brief  zlib handling functions
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include "structures.h"
+#include "internal.h"
+#include "compression.h"
+
+/* spdy ver 3 specific dictionary used by zlib */
+static const unsigned char
+spdyf_zlib_dictionary[] = {
+	0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69,   // - - - - o p t i
+	0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68,   // o n s - - - - h
+	0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70,   // e a d - - - - p
+	0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70,   // o s t - - - - p
+	0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65,   // u t - - - - d e
+	0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05,   // l e t e - - - -
+	0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00,   // t r a c e - - -
+	0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00,   // - a c c e p t -
+	0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70,   // - - - a c c e p
+	0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,   // t - c h a r s e
+	0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63,   // t - - - - a c c
+	0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,   // e p t - e n c o
+	0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f,   // d i n g - - - -
+	0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c,   // a c c e p t - l
+	0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00,   // a n g u a g e -
+	0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70,   // - - - a c c e p
+	0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73,   // t - r a n g e s
+	0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00,   // - - - - a g e -
+	0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77,   // - - - a l l o w
+	0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68,   // - - - - a u t h
+	0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,   // o r i z a t i o
+	0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63,   // n - - - - c a c
+	0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72,   // h e - c o n t r
+	0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f,   // o l - - - - c o
+	0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,   // n n e c t i o n
+	0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74,   // - - - - c o n t
+	0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65,   // e n t - b a s e
+	0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74,   // - - - - c o n t
+	0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,   // e n t - e n c o
+	0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10,   // d i n g - - - -
+	0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,   // c o n t e n t -
+	0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65,   // l a n g u a g e
+	0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74,   // - - - - c o n t
+	0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67,   // e n t - l e n g
+	0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f,   // t h - - - - c o
+	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f,   // n t e n t - l o
+	0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00,   // c a t i o n - -
+	0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,   // - - c o n t e n
+	0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00,   // t - m d 5 - - -
+	0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,   // - c o n t e n t
+	0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00,   // - r a n g e - -
+	0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,   // - - c o n t e n
+	0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00,   // t - t y p e - -
+	0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00,   // - - d a t e - -
+	0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00,   // - - e t a g - -
+	0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74,   // - - e x p e c t
+	0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69,   // - - - - e x p i
+	0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66,   // r e s - - - - f
+	0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68,   // r o m - - - - h
+	0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69,   // o s t - - - - i
+	0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00,   // f - m a t c h -
+	0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f,   // - - - i f - m o
+	0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73,   // d i f i e d - s
+	0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d,   // i n c e - - - -
+	0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d,   // i f - n o n e -
+	0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00,   // m a t c h - - -
+	0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67,   // - i f - r a n g
+	0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d,   // e - - - - i f -
+	0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69,   // u n m o d i f i
+	0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65,   // e d - s i n c e
+	0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74,   // - - - - l a s t
+	0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65,   // - m o d i f i e
+	0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63,   // d - - - - l o c
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00,   // a t i o n - - -
+	0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72,   // - m a x - f o r
+	0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00,   // w a r d s - - -
+	0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00,   // - p r a g m a -
+	0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79,   // - - - p r o x y
+	0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,   // - a u t h e n t
+	0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00,   // i c a t e - - -
+	0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61,   // - p r o x y - a
+	0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,   // u t h o r i z a
+	0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05,   // t i o n - - - -
+	0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00,   // r a n g e - - -
+	0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72,   // - r e f e r e r
+	0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72,   // - - - - r e t r
+	0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00,   // y - a f t e r -
+	0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65,   // - - - s e r v e
+	0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00,   // r - - - - t e -
+	0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c,   // - - - t r a i l
+	0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72,   // e r - - - - t r
+	0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65,   // a n s f e r - e
+	0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00,   // n c o d i n g -
+	0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61,   // - - - u p g r a
+	0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73,   // d e - - - - u s
+	0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74,   // e r - a g e n t
+	0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79,   // - - - - v a r y
+	0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00,   // - - - - v i a -
+	0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69,   // - - - w a r n i
+	0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77,   // n g - - - - w w
+	0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e,   // w - a u t h e n
+	0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00,   // t i c a t e - -
+	0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,   // - - m e t h o d
+	0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00,   // - - - - g e t -
+	0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,   // - - - s t a t u
+	0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30,   // s - - - - 2 0 0
+	0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76,   // - O K - - - - v
+	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00,   // e r s i o n - -
+	0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31,   // - - H T T P - 1
+	0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72,   // - 1 - - - - u r
+	0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62,   // l - - - - p u b
+	0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73,   // l i c - - - - s
+	0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69,   // e t - c o o k i
+	0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65,   // e - - - - k e e
+	0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00,   // p - a l i v e -
+	0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69,   // - - - o r i g i
+	0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32,   // n 1 0 0 1 0 1 2
+	0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35,   // 0 1 2 0 2 2 0 5
+	0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30,   // 2 0 6 3 0 0 3 0
+	0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33,   // 2 3 0 3 3 0 4 3
+	0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37,   // 0 5 3 0 6 3 0 7
+	0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30,   // 4 0 2 4 0 5 4 0
+	0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34,   // 6 4 0 7 4 0 8 4
+	0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31,   // 0 9 4 1 0 4 1 1
+	0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31,   // 4 1 2 4 1 3 4 1
+	0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34,   // 4 4 1 5 4 1 6 4
+	0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34,   // 1 7 5 0 2 5 0 4
+	0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e,   // 5 0 5 2 0 3 - N
+	0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f,   // o n - A u t h o
+	0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65,   // r i t a t i v e
+	0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61,   // - I n f o r m a
+	0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20,   // t i o n 2 0 4 -
+	0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65,   // N o - C o n t e
+	0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f,   // n t 3 0 1 - M o
+	0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d,   // v e d - P e r m
+	0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34,   // a n e n t l y 4
+	0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52,   // 0 0 - B a d - R
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30,   // e q u e s t 4 0
+	0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68,   // 1 - U n a u t h
+	0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30,   // o r i z e d 4 0
+	0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64,   // 3 - F o r b i d
+	0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e,   // d e n 4 0 4 - N
+	0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64,   // o t - F o u n d
+	0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65,   // 5 0 0 - I n t e
+	0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72,   // r n a l - S e r
+	0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f,   // v e r - E r r o
+	0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74,   // r 5 0 1 - N o t
+	0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65,   // - I m p l e m e
+	0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20,   // n t e d 5 0 3 -
+	0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20,   // S e r v i c e -
+	0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61,   // U n a v a i l a
+	0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46,   // b l e J a n - F
+	0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41,   // e b - M a r - A
+	0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a,   // p r - M a y - J
+	0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41,   // u n - J u l - A
+	0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20,   // u g - S e p t -
+	0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20,   // O c t - N o v -
+	0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30,   // D e c - 0 0 - 0
+	0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e,   // 0 - 0 0 - M o n
+	0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57,   // - - T u e - - W
+	0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c,   // e d - - T h u -
+	0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61,   // - F r i - - S a
+	0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20,   // t - - S u n - -
+	0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b,   // G M T c h u n k
+	0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f,   // e d - t e x t -
+	0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61,   // h t m l - i m a
+	0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69,   // g e - p n g - i
+	0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67,   // m a g e - j p g
+	0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67,   // - i m a g e - g
+	0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,   // i f - a p p l i
+	0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,   // c a t i o n - x
+	0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,   // m l - a p p l i
+	0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,   // c a t i o n - x
+	0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c,   // h t m l - x m l
+	0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c,   // - t e x t - p l
+	0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74,   // a i n - t e x t
+	0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72,   // - j a v a s c r
+	0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c,   // i p t - p u b l
+	0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,   // i c p r i v a t
+	0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65,   // e m a x - a g e
+	0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65,   // - g z i p - d e
+	0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64,   // f l a t e - s d
+	0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,   // c h c h a r s e
+	0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63,   // t - u t f - 8 c
+	0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69,   // h a r s e t - i
+	0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d,   // s o - 8 8 5 9 -
+	0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a,   // 1 - u t f - - -
+	0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e          // - e n q - 0 -
+};
+
+
+int
+SPDYF_zlib_deflate_init(z_stream *strm)
+{
+	int ret;
+	
+	strm->zalloc = Z_NULL;
+	strm->zfree = Z_NULL;
+	strm->opaque = Z_NULL;
+	//the second argument is "level of compression"
+	//use 0 for no compression; 9 for best compression
+	ret = deflateInit(strm, Z_DEFAULT_COMPRESSION);
+	if(ret != Z_OK)
+	{
+		SPDYF_DEBUG("deflate init");
+		return SPDY_NO;
+	}
+	ret = deflateSetDictionary(strm,
+				   spdyf_zlib_dictionary,
+				   sizeof(spdyf_zlib_dictionary));
+	if(ret != Z_OK)
+	{
+		SPDYF_DEBUG("deflate set dict");
+		deflateEnd(strm);
+		return SPDY_NO;
+	}
+	return SPDY_YES;
+}
+
+
+void
+SPDYF_zlib_deflate_end(z_stream *strm)
+{
+	deflateEnd(strm);
+}
+
+int
+SPDYF_zlib_deflate(z_stream *strm,
+					const void *src,
+					size_t src_size,
+					size_t *data_used,
+					void **dest,
+					size_t *dest_size)
+{
+	int ret;
+	int flush;
+	unsigned int have;
+	Bytef out[SPDYF_ZLIB_CHUNK];
+	
+	*dest = NULL;
+	*dest_size = 0;
+
+	do
+	{
+		/* check for big data bigger than the buffer used */
+		if(src_size > SPDYF_ZLIB_CHUNK)
+		{
+			strm->avail_in = SPDYF_ZLIB_CHUNK;
+			src_size -= SPDYF_ZLIB_CHUNK;
+			/* flush is used for the loop to detect if we still
+			 * need to supply additional
+			 * data to the stream via avail_in and next_in. */
+			flush = Z_NO_FLUSH;
+		}
+		else
+		{
+			strm->avail_in = src_size;
+			flush = Z_SYNC_FLUSH;
+		}
+		*data_used += strm->avail_in;
+
+		strm->next_in = (Bytef *)src;
+
+		/* Loop while output data is available */
+		do
+		{
+			strm->avail_out = SPDYF_ZLIB_CHUNK;
+			strm->next_out = out;
+
+			/* No need to check return value of deflate.
+			* (See zlib documentation at http://www.zlib.net/zlib_how.html */
+			ret = deflate(strm, flush);
+			have = SPDYF_ZLIB_CHUNK - strm->avail_out;
+
+			/* (Re)allocate memory for dest and keep track of it's size. */
+			*dest_size += have;
+			*dest = realloc(*dest, *dest_size);
+			if(!*dest)
+			{
+				SPDYF_DEBUG("realloc data for result");
+				deflateEnd(strm);
+				return SPDY_NO;
+			}
+			memcpy((*dest) + ((*dest_size) - have), out, have);
+		}
+		while(strm->avail_out == 0);
+		/* At this point, all of the input data should already
+		* have been used. */
+		SPDYF_ASSERT(strm->avail_in == 0,"compressing bug");
+	}
+	while(flush != Z_SYNC_FLUSH);
+	
+	return Z_OK == ret ? SPDY_YES : SPDY_NO;
+}
+
+
+int
+SPDYF_zlib_inflate_init(z_stream *strm)
+{
+	int ret;
+	
+	strm->zalloc = Z_NULL;
+	strm->zfree = Z_NULL;
+	strm->opaque = Z_NULL;
+	strm->avail_in = 0;
+	strm->next_in = Z_NULL;
+	//change 15 to lower value for performance and benchmark
+	//"The windowBits parameter is the base two logarithm of the
+	// maximum window size (the size of the history buffer)."
+	ret = inflateInit2(strm, 15);
+	if(ret != Z_OK)
+	{
+		SPDYF_DEBUG("Cannot inflateInit2 the stream");
+		return SPDY_NO;
+	}
+	return SPDY_YES;
+}
+
+
+void
+SPDYF_zlib_inflate_end(z_stream *strm)
+{
+	inflateEnd(strm);
+}
+
+
+int
+SPDYF_zlib_inflate(z_stream *strm,
+					const void *src,
+					size_t src_size,
+					void **dest,
+					size_t *dest_size)
+{
+	int ret = Z_OK;
+	uint32_t have;
+	Bytef out[SPDYF_ZLIB_CHUNK];
+	
+	*dest = NULL;
+	*dest_size = 0;
+
+	/* decompress until deflate stream ends or end of file */
+	do
+	{		
+		if(src_size > SPDYF_ZLIB_CHUNK)
+		{
+			strm->avail_in = SPDYF_ZLIB_CHUNK;
+			src_size -= SPDYF_ZLIB_CHUNK;
+		}
+		else
+		{
+			strm->avail_in = src_size;
+			src_size = 0;
+		}
+		
+		if(strm->avail_in == 0){
+			//the loop breaks always here as the stream never ends
+			break;
+		}
+			
+		strm->next_in = (Bytef *) src;
+		/* run inflate() on input until output buffer not full */
+		do {
+			strm->avail_out = SPDYF_ZLIB_CHUNK;
+			strm->next_out = out;
+			ret = inflate(strm, Z_SYNC_FLUSH);
+			
+			switch (ret)
+			{
+				case Z_STREAM_ERROR:
+					SPDYF_DEBUG("Error on inflate");
+					//no inflateEnd here, same in zlib example
+					return SPDY_NO;
+				
+				case Z_NEED_DICT:
+					ret = inflateSetDictionary(strm,
+										   spdyf_zlib_dictionary,
+										   sizeof(spdyf_zlib_dictionary));
+					if(ret != Z_OK)
+					{
+						SPDYF_DEBUG("Error on inflateSetDictionary");
+						inflateEnd(strm);
+						return SPDY_NO;
+					}
+					ret = inflate(strm, Z_SYNC_FLUSH);
+					if(Z_STREAM_ERROR == ret)
+					{
+						SPDYF_DEBUG("Error on inflate");
+						return SPDY_NO;
+					}
+					break;
+					
+				case Z_DATA_ERROR:
+					SPDYF_DEBUG("Z_DATA_ERROR");
+					inflateEnd(strm);
+					return SPDY_NO;
+					
+				case Z_MEM_ERROR:
+					SPDYF_DEBUG("Z_MEM_ERROR");
+					inflateEnd(strm);
+					return SPDY_NO;
+			}
+			have = SPDYF_ZLIB_CHUNK - strm->avail_out;
+			*dest_size += have;
+			/* (re)alloc memory for the output buffer */
+			*dest = realloc(*dest, *dest_size);
+			if(!*dest)
+			{
+				SPDYF_DEBUG("Cannot realloc memory");
+				inflateEnd(strm);
+				return SPDY_NO;
+			}
+			memcpy((*dest) + ((*dest_size) - have), out, have);
+		}
+		while (0 == strm->avail_out);
+	}
+	while (Z_STREAM_END != ret);
+	
+	return Z_OK == ret || Z_STREAM_END == ret ? SPDY_YES : SPDY_NO;
+}
diff --git a/src/microspdy/compression.h b/src/microspdy/compression.h
new file mode 100644
index 0000000..40746e7
--- /dev/null
+++ b/src/microspdy/compression.h
@@ -0,0 +1,117 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file compression.h
+ * @brief  zlib handling functions
+ * @author Andrey Uzunov
+ */
+
+#ifndef COMPRESSION_H
+#define COMPRESSION_H
+
+#include "platform.h"
+
+/* size of buffers used by zlib on (de)compressing */
+#define SPDYF_ZLIB_CHUNK 16384
+
+
+/**
+ * Initializes the zlib stream for compression. Must be called once
+ * for a session on initialization.
+ *
+ * @param strm Zlib stream on which we work
+ * @return SPDY_NO if zlib failed. SPDY_YES otherwise
+ */		
+int
+SPDYF_zlib_deflate_init(z_stream *strm);
+
+
+/**
+ * Deinitializes the zlib stream for compression. Should be called once
+ * for a session on cleaning up.
+ *
+ * @param strm Zlib stream on which we work
+ */	
+void
+SPDYF_zlib_deflate_end(z_stream *strm);
+
+
+/**
+ * Compressing stream with zlib.
+ *
+ * @param strm Zlib stream on which we work
+ * @param src stream of the data to be compressed
+ * @param src_size size of the data
+ * @param data_used the number of bytes from src_stream that were used
+ * 					TODO do we need
+ * @param dest the resulting compressed stream. Should be NULL. Must be
+ * 					freed later manually.
+ * @param dest_size size of the data after compression
+ * @return SPDY_NO if malloc or zlib failed. SPDY_YES otherwise
+ */
+int
+SPDYF_zlib_deflate(z_stream *strm,
+					const void *src,
+					size_t src_size,
+					size_t *data_used,
+					void **dest,
+					size_t *dest_size);
+     
+
+/**
+ * Initializes the zlib stream for decompression. Must be called once
+ * for a session.
+ *
+ * @param strm Zlib stream on which we work
+ * @return SPDY_NO if zlib failed. SPDY_YES otherwise
+ */	                 
+int
+SPDYF_zlib_inflate_init(z_stream *strm);
+
+
+/**
+ * Deinitializes the zlib stream for decompression. Should be called once
+ * for a session on cleaning up.
+ *
+ * @param strm Zlib stream on which we work
+ */	
+void
+SPDYF_zlib_inflate_end(z_stream *strm);
+
+
+/**
+ * Decompressing stream with zlib.
+ *
+ * @param strm Zlib stream on which we work
+ * @param src stream of the data to be decompressed
+ * @param src_size size of the data
+ * @param dest the resulting decompressed stream. Should be NULL. Must
+ * 				be freed manually.
+ * @param dest_size size of the data after decompression
+ * @return SPDY_NO if malloc or zlib failed. SPDY_YES otherwise. If the
+ * 			function fails, the SPDY session must be closed
+ */
+int
+SPDYF_zlib_inflate(z_stream *strm,
+					const void *src,
+					size_t src_size,
+					void **dest,
+					size_t *dest_size);
+
+#endif
diff --git a/src/microspdy/daemon.c b/src/microspdy/daemon.c
new file mode 100644
index 0000000..32285ae
--- /dev/null
+++ b/src/microspdy/daemon.c
@@ -0,0 +1,544 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file microspdy/daemon.c
+ * @brief  daemon functionality
+ * @author Andrey Uzunov
+ */
+ 
+#include "platform.h"
+#include "structures.h"
+#include "internal.h"
+#include "session.h"
+#include "io.h"
+
+
+/**
+ * Default implementation of the panic function,
+ * prints an error message and aborts.
+ *
+ * @param cls unused
+ * @param file name of the file with the problem
+ * @param line line number with the problem
+ * @param reason error message with details
+ */
+static void 
+spdyf_panic_std (void *cls,
+	       const char *file,
+	       unsigned int line,
+	       const char *reason)
+{
+	(void)cls;
+	fprintf (stdout, "Fatal error in libmicrospdy %s:%u: %s\n",
+		file, line, reason);
+	//raise(SIGINT); //used for gdb
+	abort ();
+}
+
+
+/**
+ * Global handler for fatal errors.
+ */
+SPDY_PanicCallback spdyf_panic = &spdyf_panic_std;
+
+
+/**
+ * Global closure argument for "spdyf_panic".
+ */
+void *spdyf_panic_cls;
+
+
+/**
+ * Free resources associated with all closed connections.
+ * (destroy responses, free buffers, etc.).
+ *
+ * @param daemon daemon to clean up
+ */
+static void
+spdyf_cleanup_sessions (struct SPDY_Daemon *daemon)
+{
+	struct SPDY_Session *session;
+	
+	while (NULL != (session = daemon->cleanup_head))
+	{
+		DLL_remove (daemon->cleanup_head,
+			daemon->cleanup_tail,
+			session);
+			
+		SPDYF_session_destroy(session);
+	}
+}
+
+
+/**
+ * Closing of all connections handled by the daemon.
+ *
+ * @param daemon SPDY daemon
+ */
+static void
+spdyf_close_all_sessions (struct SPDY_Daemon *daemon)
+{
+	struct SPDY_Session *session;
+	
+	while (NULL != (session = daemon->sessions_head))
+	{	
+		//prepare GOAWAY frame
+		SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_OK, true);
+		//try to send the frame (it is best effort, so it will maybe sent)
+		SPDYF_session_write(session,true);
+		SPDYF_session_close(session);
+	}
+	
+	spdyf_cleanup_sessions(daemon);
+}
+
+
+/**
+ * Parse a list of options given as varargs.
+ * 
+ * @param daemon the daemon to initialize
+ * @param valist the options
+ * @return SPDY_YES on success, SPDY_NO on error
+ */
+static int
+spdyf_parse_options_va (struct SPDY_Daemon *daemon,
+		  va_list valist)
+{
+	enum SPDY_DAEMON_OPTION opt;
+
+	while (SPDY_DAEMON_OPTION_END != (opt = (enum SPDY_DAEMON_OPTION) va_arg (valist, int)))
+	{
+		if(opt & daemon->options)
+		{
+			SPDYF_DEBUG("Daemon option %i used twice",opt);
+			return SPDY_NO;
+		}
+		daemon->options |= opt;
+		
+		switch (opt)
+		{
+			case SPDY_DAEMON_OPTION_SESSION_TIMEOUT:
+				daemon->session_timeout = va_arg (valist, unsigned int) * 1000;
+				break;
+			case SPDY_DAEMON_OPTION_SOCK_ADDR:
+				daemon->address = va_arg (valist, struct sockaddr *);
+				break;
+			case SPDY_DAEMON_OPTION_FLAGS:
+				daemon->flags = va_arg (valist, enum SPDY_DAEMON_FLAG);
+				break;
+			case SPDY_DAEMON_OPTION_IO_SUBSYSTEM:
+				daemon->io_subsystem = va_arg (valist, enum SPDY_IO_SUBSYSTEM);
+				break;
+			case SPDY_DAEMON_OPTION_MAX_NUM_FRAMES:
+				daemon->max_num_frames = va_arg (valist, uint32_t);
+				break;
+			default:
+				SPDYF_DEBUG("Wrong option for the daemon %i",opt);
+				return SPDY_NO;
+		}
+	}
+	return SPDY_YES;
+}
+
+
+void 
+SPDY_set_panic_func (SPDY_PanicCallback cb,
+					void *cls)
+{
+	spdyf_panic = cb;
+	spdyf_panic_cls = cls;
+}
+
+
+struct SPDY_Daemon *
+SPDYF_start_daemon_va (uint16_t port,
+					const char *certfile,
+					const char *keyfile,
+					SPDY_NewSessionCallback nscb,
+					SPDY_SessionClosedCallback sccb,
+					SPDY_NewRequestCallback nrcb,
+					SPDY_NewDataCallback npdcb,
+					SPDYF_NewStreamCallback fnscb,
+					SPDYF_NewDataCallback fndcb,
+					void * cls,
+					void * fcls,
+					va_list valist)
+{
+	struct SPDY_Daemon *daemon = NULL;
+	int afamily;
+	int option_on = 1;
+	int ret;
+	struct sockaddr_in* servaddr4 = NULL;
+#if HAVE_INET6
+	struct sockaddr_in6* servaddr6 = NULL;
+#endif
+	socklen_t addrlen;
+
+	if (NULL == (daemon = malloc (sizeof (struct SPDY_Daemon))))
+	{
+		SPDYF_DEBUG("malloc");
+		return NULL;
+	}
+	memset (daemon, 0, sizeof (struct SPDY_Daemon));
+	daemon->socket_fd = -1;
+	daemon->port = port;
+
+	if(SPDY_YES != spdyf_parse_options_va (daemon, valist))
+	{
+		SPDYF_DEBUG("parse");
+		goto free_and_fail;
+	}
+  
+  if(0 == daemon->max_num_frames)
+    daemon->max_num_frames = SPDYF_NUM_SENT_FRAMES_AT_ONCE;
+	
+	if(!port && NULL == daemon->address)
+	{
+		SPDYF_DEBUG("Port is 0");
+		goto free_and_fail;
+	}
+  if(0 == daemon->io_subsystem)
+    daemon->io_subsystem = SPDY_IO_SUBSYSTEM_OPENSSL;
+  
+  if(SPDY_YES != SPDYF_io_set_daemon(daemon, daemon->io_subsystem))
+		goto free_and_fail;
+  
+  if(SPDY_IO_SUBSYSTEM_RAW != daemon->io_subsystem)
+  {
+    if (NULL == certfile
+      || NULL == (daemon->certfile = strdup (certfile)))
+    {
+      SPDYF_DEBUG("strdup (certfile)");
+      goto free_and_fail;
+    }
+    if (NULL == keyfile
+      || NULL == (daemon->keyfile = strdup (keyfile)))
+    {
+      SPDYF_DEBUG("strdup (keyfile)");
+      goto free_and_fail;
+    }
+  }
+  
+	daemon->new_session_cb = nscb;
+	daemon->session_closed_cb = sccb;
+	daemon->new_request_cb = nrcb;
+	daemon->received_data_cb = npdcb;
+	daemon->cls = cls;
+	daemon->fcls = fcls;
+	daemon->fnew_stream_cb = fnscb;
+	daemon->freceived_data_cb = fndcb;
+
+#if HAVE_INET6
+	//handling IPv6
+	if((daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
+		&& NULL != daemon->address && AF_INET6 != daemon->address->sa_family)
+	{
+		SPDYF_DEBUG("SPDY_DAEMON_FLAG_ONLY_IPV6 set but IPv4 address provided");
+		goto free_and_fail;
+	}
+  
+  addrlen = sizeof (struct sockaddr_in6);
+    
+	if(NULL == daemon->address)
+	{		
+		if (NULL == (servaddr6 = malloc (addrlen)))
+		{
+			SPDYF_DEBUG("malloc");
+			goto free_and_fail;
+		}
+		memset (servaddr6, 0, addrlen);
+		servaddr6->sin6_family = AF_INET6;
+		servaddr6->sin6_addr = in6addr_any;
+		servaddr6->sin6_port = htons (port);
+		daemon->address = (struct sockaddr *) servaddr6;
+	}
+	
+  if(AF_INET6 == daemon->address->sa_family)
+  {
+    afamily = PF_INET6;
+  }
+  else
+  {
+    afamily = PF_INET;
+  }
+#else
+	//handling IPv4
+	if(daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
+	{
+		SPDYF_DEBUG("SPDY_DAEMON_FLAG_ONLY_IPV6 set but no support");
+		goto free_and_fail;
+	}
+	
+  addrlen = sizeof (struct sockaddr_in);
+    
+	if(NULL == daemon->address)
+	{		
+		if (NULL == (servaddr4 = malloc (addrlen)))
+		{
+			SPDYF_DEBUG("malloc");
+			goto free_and_fail;
+		}
+		memset (servaddr4, 0, addrlen);
+		servaddr4->sin_family = AF_INET;
+		servaddr4->sin_addr = INADDR_ANY;
+		servaddr4->sin_port = htons (port);
+		daemon->address = (struct sockaddr *) servaddr4;
+	}
+	
+	afamily = PF_INET;
+#endif	
+
+	daemon->socket_fd = socket (afamily, SOCK_STREAM, 0);
+	if (-1 == daemon->socket_fd)
+	{
+		SPDYF_DEBUG("sock");
+		goto free_and_fail;
+	}
+
+	//setting option for the socket to reuse address
+	ret = setsockopt(daemon->socket_fd, SOL_SOCKET, SO_REUSEADDR, &option_on, sizeof(option_on));
+	if(ret)
+	{
+		SPDYF_DEBUG("WARNING: SO_REUSEADDR was not set for the server");
+	}
+	
+#if HAVE_INET6
+	if(daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
+	{
+		ret = setsockopt(daemon->socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &option_on, sizeof(option_on));
+		if(ret)
+		{
+			SPDYF_DEBUG("setsockopt with IPPROTO_IPV6 failed");
+			goto free_and_fail;
+		}
+	}
+#endif
+	
+	if (-1 == bind (daemon->socket_fd, daemon->address, addrlen))
+	{
+		SPDYF_DEBUG("bind %i",errno);
+		goto free_and_fail;
+	}
+
+	if (listen (daemon->socket_fd, 20) < 0)
+	{
+		SPDYF_DEBUG("listen %i",errno);
+		goto free_and_fail;
+	}
+
+	if(SPDY_YES != daemon->fio_init(daemon))
+	{
+		SPDYF_DEBUG("tls");
+		goto free_and_fail;
+	}
+
+	return daemon;
+
+	//for GOTO
+	free_and_fail:
+	if(daemon->socket_fd > 0)
+		(void)close (daemon->socket_fd);
+		
+	free(servaddr4);
+#if HAVE_INET6
+	free(servaddr6);
+#endif
+	if(NULL != daemon->certfile)
+		free(daemon->certfile);
+	if(NULL != daemon->keyfile)
+		free(daemon->keyfile);
+	free (daemon);
+	
+	return NULL;
+}
+
+
+void 
+SPDYF_stop_daemon (struct SPDY_Daemon *daemon)
+{
+	daemon->fio_deinit(daemon);
+	
+	shutdown (daemon->socket_fd, SHUT_RDWR);
+	spdyf_close_all_sessions (daemon);
+	(void)close (daemon->socket_fd);
+	
+	if(!(SPDY_DAEMON_OPTION_SOCK_ADDR & daemon->options))
+		free(daemon->address);
+	
+	free(daemon->certfile);
+	free(daemon->keyfile);
+	
+	free(daemon);
+}
+
+
+int
+SPDYF_get_timeout (struct SPDY_Daemon *daemon, 
+		     unsigned long long *timeout)
+{
+	unsigned long long earliest_deadline = 0;
+	unsigned long long now;
+	struct SPDY_Session *pos;
+	bool have_timeout;
+	
+	if(0 == daemon->session_timeout)
+		return SPDY_NO;
+
+	now = SPDYF_monotonic_time();
+	have_timeout = false;
+	for (pos = daemon->sessions_head; NULL != pos; pos = pos->next)
+	{
+		if ( (! have_timeout) ||
+			(earliest_deadline > pos->last_activity + daemon->session_timeout) )
+			earliest_deadline = pos->last_activity + daemon->session_timeout;
+
+		have_timeout = true;
+		
+		if (SPDY_YES == pos->fio_is_pending(pos))
+		{
+			earliest_deadline = 0;
+			break;
+		}
+	}
+	
+	if (!have_timeout)
+		return SPDY_NO;
+	if (earliest_deadline <= now)
+		*timeout = 0;
+	else
+		*timeout = earliest_deadline - now;
+		
+	return SPDY_YES;
+}
+
+
+int
+SPDYF_get_fdset (struct SPDY_Daemon *daemon,
+				fd_set *read_fd_set,
+				fd_set *write_fd_set, 
+				fd_set *except_fd_set,
+				bool all)
+{
+	(void)except_fd_set;
+	struct SPDY_Session *pos;
+	int fd;
+	int max_fd = -1;
+
+	fd = daemon->socket_fd;
+	if (-1 != fd)
+	{
+		FD_SET (fd, read_fd_set);
+		/* update max file descriptor */
+		max_fd = fd;
+	}
+
+	for (pos = daemon->sessions_head; NULL != pos; pos = pos->next)
+	{
+		fd = pos->socket_fd;
+		FD_SET(fd, read_fd_set);
+		if (all
+		    || (NULL != pos->response_queue_head) //frames pending
+		    || (NULL != pos->write_buffer) //part of last frame pending
+		    || (SPDY_SESSION_STATUS_CLOSING == pos->status) //the session is about to be closed
+		    || (daemon->session_timeout //timeout passed for the session
+			&& (pos->last_activity + daemon->session_timeout < SPDYF_monotonic_time()))
+		    || (SPDY_YES == pos->fio_is_pending(pos)) //data in TLS' read buffer pending
+		    || ((pos->read_buffer_offset - pos->read_buffer_beginning) > 0) // data in lib's read buffer pending
+		    )
+			FD_SET(fd, write_fd_set);
+		if(fd > max_fd)
+			max_fd = fd;
+	}
+
+	return max_fd;
+}
+
+
+void 
+SPDYF_run (struct SPDY_Daemon *daemon)
+{
+	struct SPDY_Session *pos;
+	struct SPDY_Session *next;
+	int num_ready;
+	fd_set rs;
+	fd_set ws;
+	fd_set es;
+	int max;
+	struct timeval timeout;
+	int ds;
+
+	timeout.tv_sec = 0;
+	timeout.tv_usec = 0;
+	FD_ZERO (&rs);
+	FD_ZERO (&ws);
+	FD_ZERO (&es);
+	//here we need really all descriptors to see later which are ready
+	max = SPDYF_get_fdset(daemon,&rs,&ws,&es, true);
+
+	num_ready = select (max + 1, &rs, &ws, &es, &timeout);
+
+	if(num_ready < 1)
+		return;
+
+	if ( (-1 != (ds = daemon->socket_fd)) &&
+		(FD_ISSET (ds, &rs)) ){
+		SPDYF_session_accept(daemon);
+	}
+
+	next = daemon->sessions_head;
+	while (NULL != (pos = next))
+	{
+		next = pos->next;
+		ds = pos->socket_fd;
+		if (ds != -1)
+		{
+			//fill the read buffer
+			if (FD_ISSET (ds, &rs) || pos->fio_is_pending(pos)){
+				SPDYF_session_read(pos);
+			}
+			
+			//do something with the data in read buffer
+			if(SPDY_NO == SPDYF_session_idle(pos))
+			{
+				//the session was closed, cannot write anymore
+				//continue;
+			}
+			
+			//write whatever has been put to the response queue
+			//during read or idle operation, something might be put
+			//on the response queue, thus call write operation
+			if (FD_ISSET (ds, &ws)){
+				if(SPDY_NO == SPDYF_session_write(pos, false))
+				{
+					//SPDYF_session_close(pos);
+					//continue;
+				}
+			}
+			
+			/* the response queue has been flushed for half closed
+			 * connections, so let close them */
+			/*if(pos->read_closed)
+			{
+				SPDYF_session_close(pos);
+			}*/
+		}
+	}
+	
+	spdyf_cleanup_sessions(daemon);
+}
diff --git a/src/microspdy/daemon.h b/src/microspdy/daemon.h
new file mode 100644
index 0000000..cb3ed5f
--- /dev/null
+++ b/src/microspdy/daemon.h
@@ -0,0 +1,130 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file daemon.h
+ * @brief  daemon functionality
+ * @author Andrey Uzunov
+ */
+
+#ifndef DAEMON_H
+#define DAEMON_H
+
+#include "platform.h"
+
+
+/**
+ * Global flags containing the initialized IO subsystems.
+ */
+enum SPDY_IO_SUBSYSTEM spdyf_io_initialized;
+
+
+/**
+ * Start a SPDDY webserver on the given port.
+ *
+ * @param port port to bind to
+ * @param certfile path to the certificate that will be used by server
+ * @param keyfile path to the keyfile for the certificate
+ * @param nscb callback called when a new SPDY session is
+ * 			established	by a client
+ * @param sccb callback called when a client closes the session
+ * @param nrcb callback called when a client sends request
+ * @param npdcb callback called when HTTP POST params are received
+ * 			after request
+ * @param fnscb callback called when new stream is opened by a client
+ * @param fndcb callback called when new data -- within a data frame --
+ *        is received by the server
+ * @param cls extra argument to all of the callbacks without those
+ * 				 specific only for the framing layer
+ * @param fcls extra argument to all of the callbacks, specific only for
+ * 				the framing layer (those vars starting with 'f').
+ * @param valist va_list of options (type-value pairs,
+ *        terminated with SPDY_DAEMON_OPTION_END).
+ * @return NULL on error, handle to daemon on success
+ */
+struct SPDY_Daemon *
+SPDYF_start_daemon_va (uint16_t port,
+					const char *certfile,
+					const char *keyfile,
+					SPDY_NewSessionCallback nscb,
+					SPDY_SessionClosedCallback sccb,
+					SPDY_NewRequestCallback nrcb,
+					SPDY_NewDataCallback npdcb,
+					SPDYF_NewStreamCallback fnscb,
+					SPDYF_NewDataCallback fndcb,
+					void * cls,
+					void * fcls,
+					va_list valist);
+
+
+/**
+ * Run webserver operations (without blocking unless
+ * in client callbacks). This method must be called in the client event
+ * loop.
+ *
+ * @param daemon daemon to run
+ */
+void 
+SPDYF_run (struct SPDY_Daemon *daemon);
+
+
+/**
+ * Obtain timeout value for select for this daemon. The returned value
+ * is how long select
+ * should at most block, not the timeout value set for connections.
+ *
+ * @param daemon daemon to query for timeout
+ * @param timeout set to the timeout (in milliseconds)
+ * @return SPDY_YES on success, SPDY_NO if no connections exist that
+ * 			would necessiate the use of a timeout right now
+ */
+int
+SPDYF_get_timeout (struct SPDY_Daemon *daemon, 
+		     unsigned long long *timeout);
+
+
+/**
+ * Obtain the select sets for this daemon. The idea of SPDYF_get_fdset
+ * is to return such descriptors that the select in the application can
+ * return and SPDY_run can be called only when this is really needed.
+ * That means not all sockets will be added to write_fd_set.
+ *
+ * @param daemon daemon to get sets from
+ * @param read_fd_set read set
+ * @param write_fd_set write set
+ * @param except_fd_set except set
+ * @param all add all session's descriptors to write_fd_set or not
+ * @return largest FD added
+ */
+int
+SPDYF_get_fdset (struct SPDY_Daemon *daemon,
+				fd_set *read_fd_set,
+				fd_set *write_fd_set, 
+				fd_set *except_fd_set,
+				bool all);
+
+
+/**
+ * Shutdown the daemon.
+ *
+ * @param daemon daemon to stop
+ */				
+void 
+SPDYF_stop_daemon (struct SPDY_Daemon *daemon);
+
+#endif
diff --git a/src/microspdy/internal.c b/src/microspdy/internal.c
new file mode 100644
index 0000000..f0d2fc1
--- /dev/null
+++ b/src/microspdy/internal.c
@@ -0,0 +1,40 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file microspdy/internal.c
+ * @brief  internal functions and macros for the framing layer
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include "structures.h"
+
+
+unsigned long long
+SPDYF_monotonic_time (void)
+{
+#ifdef HAVE_CLOCK_GETTIME
+#ifdef CLOCK_MONOTONIC
+  struct timespec ts;
+  if (0 == clock_gettime (CLOCK_MONOTONIC, &ts))
+    return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+#endif
+#endif
+  return time (NULL) * 1000;
+}
diff --git a/src/microspdy/internal.h b/src/microspdy/internal.h
new file mode 100644
index 0000000..e618a5a
--- /dev/null
+++ b/src/microspdy/internal.h
@@ -0,0 +1,198 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file microspdy/internal.h
+ * @brief  internal functions and macros for the framing layer
+ * @author Andrey Uzunov
+ */
+
+#ifndef INTERNAL_H_H
+#define INTERNAL_H_H
+
+#include "platform.h"
+#include "microspdy.h"
+
+/**
+ * size of read buffers for each connection
+ * must be at least the size of SPDY_MAX_SUPPORTED_FRAME_SIZE
+ */
+#define SPDYF_BUFFER_SIZE 8192
+
+/**
+ * initial size of window for each stream (this is for the data
+ * within data frames that can be handled)
+ */
+#define SPDYF_INITIAL_WINDOW_SIZE 65536
+
+/**
+ * number of frames written to the socket at once. After X frames
+ * everything should be run again. In this way the application can
+ * response to more important requests while a big file is still
+ * being transmitted to the client
+ */
+#define SPDYF_NUM_SENT_FRAMES_AT_ONCE 10
+
+
+/**
+ * Handler for fatal errors.
+ */
+extern SPDY_PanicCallback spdyf_panic;
+
+
+/**
+ * Closure argument for "mhd_panic".
+ */
+extern void *spdyf_panic_cls;
+
+
+/**
+ * Trigger 'panic' action based on fatal errors.
+ * 
+ * @param msg error message (const char *)
+ */
+#define SPDYF_PANIC(msg) \
+	spdyf_panic (spdyf_panic_cls, __FILE__, __LINE__, msg)
+
+
+/**
+ * Asserts the validity of an expression.
+ *
+ * @param expr (bool)
+ * @param msg message to print on error (const char *)
+ */
+#define SPDYF_ASSERT(expr, msg) \
+	if(!(expr)){\
+		SPDYF_PANIC(msg);\
+		abort();\
+	}
+
+
+/**
+ * Convert 24 bit integer from host byte order to network byte order.
+ *
+ * @param n input value (int32_t)
+ * @return converted value (uint32_t)
+ */
+#if HAVE_BIG_ENDIAN
+#define HTON24(n) n
+#else
+#define HTON24(n) (((((uint32_t)(n) & 0xFF)) << 16)\
+	| (((uint32_t)(n) & 0xFF00))\
+	| ((((uint32_t)(n) & 0xFF0000)) >> 16))
+#endif
+
+
+/**
+ * Convert 24 bit integer from network byte order to host byte order.
+ *
+ * @param n input value (int32_t)
+ * @return converted value (uint32_t)
+ */	
+#if HAVE_BIG_ENDIAN
+#define NTOH24(n) n
+#else
+#define NTOH24(n) (((((uint32_t)(n) & 0xFF)) << 16)\
+	| (((uint32_t)(n) & 0xFF00))\
+	| ((((uint32_t)(n) & 0xFF0000)) >> 16))
+#endif
+
+
+/**
+ * Convert 31 bit integer from network byte order to host byte order.
+ *
+ * @param n input value (int32_t)
+ * @return converted value (uint32_t)
+ */
+#if HAVE_BIG_ENDIAN
+#define NTOH31(n) n
+#else
+#define NTOH31(n) (((((uint32_t)(n) & 0x7F)) << 24) | \
+                  ((((uint32_t)(n) & 0xFF00)) << 8) | \
+                  ((((uint32_t)(n) & 0xFF0000)) >> 8) | \
+                  ((((uint32_t)(n) & 0xFF000000)) >> 24))
+#endif
+
+
+/**
+ * Convert 31 bit integer from host byte order to network byte order.
+ *
+ * @param n input value (int32_t)
+ * @return converted value (uint32_t)
+ */
+#if HAVE_BIG_ENDIAN
+#define HTON31(n) n
+#else
+#define HTON31(n) (((((uint32_t)(n) & 0xFF)) << 24) | \
+                  ((((uint32_t)(n) & 0xFF00)) << 8) | \
+                  ((((uint32_t)(n) & 0xFF0000)) >> 8) | \
+                  ((((uint32_t)(n) & 0x7F000000)) >> 24))
+#endif
+
+
+/**
+ * Print formatted debug value.
+ *
+ * @param fmt format (const char *)
+ * @param ... args for format
+ */
+#define SPDYF_DEBUG(fmt, ...) do { \
+	fprintf (stdout, "%s\n%u: ",__FILE__, __LINE__);\
+	fprintf(stdout,fmt,##__VA_ARGS__);\
+	fprintf(stdout,"\n");\
+	fflush(stdout); } while (0)
+
+
+/**
+ * Print stream for debuging.
+ *
+ * @param strm (void *)
+ * @param size (int)
+ */
+#define SPDYF_PRINT_STREAM(strm, size) do { \
+	int ___i;\
+	for(___i=0;___i<size;___i++){\
+		fprintf(stdout,"%x ",*((uint8_t *) strm + ___i));\
+		fflush(stdout);\
+	}\
+	fprintf(stdout,"\n");\
+	} while (0)
+
+
+/**
+ * Print message and raise SIGINT for debug purposes.
+ *
+ * @param msg message (const char *)
+ */
+#define SPDYF_SIGINT(msg) do { \
+	fprintf(stdout,"%i : %s\n", __LINE__,__FILE__);\
+	fprintf(stdout,msg);\
+	fprintf(stdout,"\n");\
+	fflush(stdout);\
+	raise(SIGINT); } while (0)
+
+
+/**
+ * Returns monotonic time, to be used for session timeouts.
+ *
+ * @return time in milliseconds
+ */
+unsigned long long
+SPDYF_monotonic_time(void);
+
+#endif
diff --git a/src/microspdy/io.c b/src/microspdy/io.c
new file mode 100644
index 0000000..c333c89
--- /dev/null
+++ b/src/microspdy/io.c
@@ -0,0 +1,90 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file io.c
+ * @brief Generic functions for IO.
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include "structures.h"
+#include "internal.h"
+#include "io.h"
+
+
+int
+SPDYF_io_set_daemon(struct SPDY_Daemon *daemon,
+                    enum SPDY_IO_SUBSYSTEM io_subsystem)
+{
+  switch(io_subsystem)
+  {
+    case SPDY_IO_SUBSYSTEM_OPENSSL:
+      daemon->fio_init = &SPDYF_openssl_init;
+      daemon->fio_deinit = &SPDYF_openssl_deinit;
+      break;
+      
+    case SPDY_IO_SUBSYSTEM_RAW:
+      daemon->fio_init = &SPDYF_raw_init;
+      daemon->fio_deinit = &SPDYF_raw_deinit;
+      break;
+      
+    case SPDY_IO_SUBSYSTEM_NONE:
+    default:
+      SPDYF_DEBUG("Unsupported subsystem");
+      return SPDY_NO;
+  }
+  
+  return SPDY_YES;
+}
+
+
+int
+SPDYF_io_set_session(struct SPDY_Session *session,
+                     enum SPDY_IO_SUBSYSTEM io_subsystem)
+{
+  switch(io_subsystem)
+  {
+    case SPDY_IO_SUBSYSTEM_OPENSSL:
+      session->fio_new_session = &SPDYF_openssl_new_session;
+      session->fio_close_session = &SPDYF_openssl_close_session;
+      session->fio_is_pending = &SPDYF_openssl_is_pending;
+      session->fio_recv = &SPDYF_openssl_recv;
+      session->fio_send = &SPDYF_openssl_send;
+      session->fio_before_write = &SPDYF_openssl_before_write;
+      session->fio_after_write = &SPDYF_openssl_after_write;
+      break;
+      
+    case SPDY_IO_SUBSYSTEM_RAW:
+      session->fio_new_session = &SPDYF_raw_new_session;
+      session->fio_close_session = &SPDYF_raw_close_session;
+      session->fio_is_pending = &SPDYF_raw_is_pending;
+      session->fio_recv = &SPDYF_raw_recv;
+      session->fio_send = &SPDYF_raw_send;
+      session->fio_before_write = &SPDYF_raw_before_write;
+      session->fio_after_write = &SPDYF_raw_after_write;
+      break;
+      
+    case SPDY_IO_SUBSYSTEM_NONE:
+    default:
+      SPDYF_DEBUG("Unsupported subsystem");
+      return SPDY_NO;
+  }
+  
+  return SPDY_YES;
+}
diff --git a/src/microspdy/io.h b/src/microspdy/io.h
new file mode 100644
index 0000000..c28ba21
--- /dev/null
+++ b/src/microspdy/io.h
@@ -0,0 +1,216 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file io.h
+ * @brief  Signatures for IO functions.
+ * @author Andrey Uzunov
+ */
+
+#ifndef IO_H
+#define IO_H
+
+#include "platform.h"
+#include "io_openssl.h"
+#include "io_raw.h"
+
+
+/**
+ * Used for return code when reading and writing to the TLS socket.
+ */
+enum SPDY_IO_ERROR
+{
+	/**
+	 * The connection was closed by the other party.
+	 */
+	SPDY_IO_ERROR_CLOSED = 0,
+	
+	/**
+	 * Any kind of error ocurred. The session has to be closed.
+	 */
+	SPDY_IO_ERROR_ERROR = -2,
+	
+	/**
+	 * The function had to return without processing any data. The whole
+	 * cycle of events has to be called again (SPDY_run) as something
+	 * either has to be written or read or the the syscall was
+	 * interrupted by a signal.
+	 */
+	SPDY_IO_ERROR_AGAIN = -3,
+};
+
+
+/**
+ * Global initializing. Must be called only once in the program.
+ *
+ */
+typedef void
+(*SPDYF_IOGlobalInit) ();
+
+
+/**
+ * Global deinitializing for the whole program. Should be called
+ * at the end of the program.
+ *
+ */
+typedef void
+(*SPDYF_IOGlobalDeinit) ();
+
+
+/**
+ * Initializing of io context for a specific daemon.
+ * Must be called when the daemon starts.
+ *
+ * @param daemon SPDY_Daemon for which io will be used. Daemon's
+ * 				certificate and key file are used for tls.
+ * @return SPDY_YES on success or SPDY_NO on error
+ */
+typedef int
+(*SPDYF_IOInit) (struct SPDY_Daemon *daemon);
+
+
+/**
+ * Deinitializing io context for a daemon. Should be called
+ * when the deamon is stopped.
+ *
+ * @param daemon SPDY_Daemon which is being stopped
+ */
+typedef void
+(*SPDYF_IODeinit) (struct SPDY_Daemon *daemon);
+
+
+/**
+ * Initializing io for a specific connection. Must be called
+ * after the connection has been accepted.
+ *
+ * @param session SPDY_Session whose socket will be used
+ * @return SPDY_NO if some funcs inside fail. SPDY_YES otherwise
+ */
+typedef int
+(*SPDYF_IONewSession) (struct SPDY_Session *session);
+
+
+/**
+ * Deinitializing io for a specific connection. Should be called
+ * closing session's socket.
+ *
+ * @param session SPDY_Session whose socket is used
+ */
+typedef void
+(*SPDYF_IOCloseSession) (struct SPDY_Session *session);
+
+
+/**
+ * Reading from session's socket. Reads available data and put it to the
+ * buffer.
+ *
+ * @param session for which data is received
+ * @param buffer where data from the socket will be written to
+ * @param size of the buffer
+ * @return number of bytes (at most size) read from the connection
+ *         0 if the other party has closed the connection
+ *         SPDY_IO_ERROR code on error
+ */
+typedef int
+(*SPDYF_IORecv) (struct SPDY_Session *session,
+				void * buffer,
+				size_t size);
+
+
+/**
+ * Writing to session's socket. Writes the data given into the buffer to the
+ *  socket.
+ *
+ * @param session whose context is used
+ * @param buffer from where data will be written to the socket
+ * @param size number of bytes to be taken from the buffer
+ * @return number of bytes (at most size) from the buffer that has been
+ * 			written to the connection
+ *         0 if the other party has closed the connection
+ *         SPDY_IO_ERROR code on error
+ */
+typedef int
+(*SPDYF_IOSend) (struct SPDY_Session *session,
+				const void * buffer,
+				size_t size);
+
+
+/**
+ * Checks if there is data staying in the buffers of the underlying
+ * system that waits to be read. In case of TLS, this will call
+ * something like SSL_pending().
+ *
+ * @param session which is checked
+ * @return SPDY_YES if data is pending or SPDY_NO otherwise
+ */
+typedef int
+(*SPDYF_IOIsPending) (struct SPDY_Session *session);
+
+
+/**
+ * Called just before frames are about to be processed and written
+ * to the socket.
+ *
+ * @param session
+ * @return SPDY_NO if writing must not happen in the call;
+ *         SPDY_YES otherwise
+ */
+typedef int
+(*SPDYF_IOBeforeWrite) (struct SPDY_Session *session);
+
+
+/**
+ * Called just after frames have been processed and written
+ * to the socket.
+ *
+ * @param session
+ * @param was_written has the same value as the write function for the
+ *        session will return 
+ * @return returned value will be used by the write function to return
+ */
+typedef int
+(*SPDYF_IOAfterWrite) (struct SPDY_Session *session,
+                       int was_written);
+
+
+/**
+ * Sets callbacks for the daemon with regard to the IO subsystem.
+ *
+ * @param daemon
+ * @param io_subsystem the IO subsystem that will
+ *        be initialized and used by daemon.
+ * @return SPDY_YES on success or SPDY_NO otherwise
+ */
+int
+SPDYF_io_set_daemon(struct SPDY_Daemon *daemon,
+                    enum SPDY_IO_SUBSYSTEM io_subsystem);
+
+
+/**
+ * Sets callbacks for the session with regard to the IO subsystem.
+ *
+ * @param session
+ * @param io_subsystem the IO subsystem that will
+ *        be initialized and used by session.
+ * @return SPDY_YES on success or SPDY_NO otherwise
+ */
+int
+SPDYF_io_set_session(struct SPDY_Session *session,
+                     enum SPDY_IO_SUBSYSTEM io_subsystem);
+
+#endif
diff --git a/src/microspdy/io_openssl.c b/src/microspdy/io_openssl.c
new file mode 100644
index 0000000..f71a923
--- /dev/null
+++ b/src/microspdy/io_openssl.c
@@ -0,0 +1,280 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file io_openssl.c
+ * @brief  TLS handling using libssl. The current code assumes that
+ * 			blocking I/O is in use.
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include "internal.h"
+#include "session.h"
+#include "io_openssl.h"
+
+
+/**
+ * Callback to advertise spdy ver. 3 in Next Protocol Negotiation
+ *
+ * @param ssl openssl context for a connection
+ * @param out must be set to the raw data that is advertised in NPN
+ * @param outlen must be set to size of out
+ * @param arg
+ * @return SSL_TLSEXT_ERR_OK to do advertising
+ */
+static int
+spdyf_next_protos_advertised_cb (SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg)
+{
+	(void)ssl;
+	(void)arg;
+	static unsigned char npn_spdy3[] = {0x06, // length of "spdy/3"
+		0x73,0x70,0x64,0x79,0x2f,0x33};// spdy/3
+
+	*out = npn_spdy3;
+	*outlen = 7; // total length of npn_spdy3
+	return SSL_TLSEXT_ERR_OK;
+}
+
+
+void
+SPDYF_openssl_global_init()
+{
+	//error strings are now not used by the lib
+    //SSL_load_error_strings();
+    //init libssl
+    SSL_library_init(); //always returns 1
+    //the table for looking up algos is not used now by the lib
+    //OpenSSL_add_all_algorithms();
+}
+
+
+void
+SPDYF_openssl_global_deinit()
+{
+	//if SSL_load_error_strings was called
+    //ERR_free_strings();
+    //if OpenSSL_add_all_algorithms was called
+    //EVP_cleanup();
+}
+
+
+int
+SPDYF_openssl_init(struct SPDY_Daemon *daemon)
+{
+    int options;
+    //create ssl context. TLSv1 used
+    if(NULL == (daemon->io_context = SSL_CTX_new(TLSv1_server_method())))
+    {
+		SPDYF_DEBUG("Couldn't create ssl context");
+		return SPDY_NO;
+        }
+	//set options for tls
+	//TODO DH is not enabled for easier debugging
+    //SSL_CTX_set_options(daemon->io_context, SSL_OP_SINGLE_DH_USE);
+
+    //TODO here session tickets are disabled for easier debuging with
+    //wireshark when using Chrome
+    // SSL_OP_NO_COMPRESSION disables TLS compression to avoid CRIME attack
+    options = SSL_OP_NO_TICKET;
+#ifdef SSL_OP_NO_COMPRESSION
+    options |= SSL_OP_NO_COMPRESSION;
+#elif OPENSSL_VERSION_NUMBER >= 0x00908000L /* workaround for OpenSSL 0.9.8 */
+    sk_SSL_COMP_zero(SSL_COMP_get_compression_methods());
+#endif
+
+    SSL_CTX_set_options(daemon->io_context, options);
+    if(1 != SSL_CTX_use_certificate_file(daemon->io_context, daemon->certfile , SSL_FILETYPE_PEM))
+    {
+		SPDYF_DEBUG("Couldn't load the cert file");
+		SSL_CTX_free(daemon->io_context);
+		return SPDY_NO;
+	}
+    if(1 != SSL_CTX_use_PrivateKey_file(daemon->io_context, daemon->keyfile, SSL_FILETYPE_PEM))
+    {
+		SPDYF_DEBUG("Couldn't load the name file");
+		SSL_CTX_free(daemon->io_context);
+		return SPDY_NO;
+	}
+    SSL_CTX_set_next_protos_advertised_cb(daemon->io_context, &spdyf_next_protos_advertised_cb, NULL);
+    if (1 != SSL_CTX_set_cipher_list(daemon->io_context, "HIGH"))
+    {
+		SPDYF_DEBUG("Couldn't set the desired cipher list");
+		SSL_CTX_free(daemon->io_context);
+		return SPDY_NO;
+	}
+
+	return SPDY_YES;
+}
+
+
+void
+SPDYF_openssl_deinit(struct SPDY_Daemon *daemon)
+{
+    SSL_CTX_free(daemon->io_context);
+}
+
+
+int
+SPDYF_openssl_new_session(struct SPDY_Session *session)
+{
+	int ret;
+
+	if(NULL == (session->io_context = SSL_new(session->daemon->io_context)))
+    {
+		SPDYF_DEBUG("Couldn't create ssl structure");
+		return SPDY_NO;
+	}
+	if(1 != (ret = SSL_set_fd(session->io_context, session->socket_fd)))
+    {
+		SPDYF_DEBUG("SSL_set_fd %i",ret);
+		SSL_free(session->io_context);
+		session->io_context = NULL;
+		return SPDY_NO;
+	}
+
+	//for non-blocking I/O SSL_accept may return -1
+	//and this function won't work
+	if(1 != (ret = SSL_accept(session->io_context)))
+    {
+		SPDYF_DEBUG("SSL_accept %i",ret);
+		SSL_free(session->io_context);
+		session->io_context = NULL;
+		return SPDY_NO;
+	}
+	/* alternatively
+	SSL_set_accept_state(session->io_context);
+	* may be called and then the negotiation will be done on reading
+	*/
+
+	return SPDY_YES;
+}
+
+
+void
+SPDYF_openssl_close_session(struct SPDY_Session *session)
+{
+	//SSL_shutdown sends TLS "close notify" as in TLS standard.
+	//The function may fail as it waits for the other party to also close
+	//the TLS session. The lib just sends it and will close the socket
+	//after that because the browsers don't seem to care much about
+	//"close notify"
+	SSL_shutdown(session->io_context);
+
+	SSL_free(session->io_context);
+}
+
+
+int
+SPDYF_openssl_recv(struct SPDY_Session *session,
+				void * buffer,
+				size_t size)
+{
+	int ret;
+	int n = SSL_read(session->io_context,
+					buffer,
+					size);
+	//if(n > 0) SPDYF_DEBUG("recvd: %i",n);
+	if (n <= 0)
+	{
+		ret = SSL_get_error(session->io_context, n);
+		switch(ret)
+		{
+			case SSL_ERROR_ZERO_RETURN:
+				return 0;
+
+			case SSL_ERROR_WANT_READ:
+			case SSL_ERROR_WANT_WRITE:
+				return SPDY_IO_ERROR_AGAIN;
+
+			case SSL_ERROR_SYSCALL:
+				if(EINTR == errno)
+					return SPDY_IO_ERROR_AGAIN;
+				return SPDY_IO_ERROR_ERROR;
+			default:
+				return SPDY_IO_ERROR_ERROR;
+		}
+	}
+
+	return n;
+}
+
+
+int
+SPDYF_openssl_send(struct SPDY_Session *session,
+				const void * buffer,
+				size_t size)
+{
+	int ret;
+
+	int n = SSL_write(session->io_context,
+					buffer,
+					size);
+	//if(n > 0) SPDYF_DEBUG("sent: %i",n);
+	if (n <= 0)
+	{
+		ret = SSL_get_error(session->io_context, n);
+		switch(ret)
+		{
+			case SSL_ERROR_ZERO_RETURN:
+				return 0;
+
+			case SSL_ERROR_WANT_READ:
+			case SSL_ERROR_WANT_WRITE:
+				return SPDY_IO_ERROR_AGAIN;
+
+			case SSL_ERROR_SYSCALL:
+				if(EINTR == errno)
+					return SPDY_IO_ERROR_AGAIN;
+				return SPDY_IO_ERROR_ERROR;
+			default:
+				return SPDY_IO_ERROR_ERROR;
+		}
+	}
+
+	return n;
+}
+
+
+int
+SPDYF_openssl_is_pending(struct SPDY_Session *session)
+{
+	/* From openssl docs:
+	 * BUGS
+SSL_pending() takes into account only bytes from the TLS/SSL record that is currently being processed (if any). If the SSL object's read_ahead flag is set, additional protocol bytes may have been read containing more TLS/SSL records; these are ignored by SSL_pending().
+	 */
+	return SSL_pending(session->io_context) > 0 ? SPDY_YES : SPDY_NO;
+}
+
+
+int
+SPDYF_openssl_before_write(struct SPDY_Session *session)
+{
+  (void)session;
+
+  return SPDY_YES;
+}
+
+
+int
+SPDYF_openssl_after_write(struct SPDY_Session *session, int was_written)
+{
+  (void)session;
+
+  return was_written;
+}
diff --git a/src/microspdy/io_openssl.h b/src/microspdy/io_openssl.h
new file mode 100644
index 0000000..a4e9429
--- /dev/null
+++ b/src/microspdy/io_openssl.h
@@ -0,0 +1,166 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file io_openssl.h
+ * @brief  TLS handling. openssl with NPN is used, but as long as the
+ * 			functions conform to this interface file, other libraries
+ * 			can be used.
+ * @author Andrey Uzunov
+ */
+
+#ifndef IO_OPENSSL_H
+#define IO_OPENSSL_H
+
+#include "platform.h"
+#include "io.h"
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include <openssl/rand.h>
+
+
+/**
+ * Global initializing of openssl. Must be called only once in the program.
+ *
+ */
+void
+SPDYF_openssl_global_init();
+
+
+/**
+ * Global deinitializing of openssl for the whole program. Should be called
+ * at the end of the program.
+ *
+ */
+void
+SPDYF_openssl_global_deinit();
+
+
+/**
+ * Initializing of openssl for a specific daemon.
+ * Must be called when the daemon starts.
+ *
+ * @param daemon SPDY_Daemon for which openssl will be used. Daemon's
+ * 				certificate and key file are used.
+ * @return SPDY_YES on success or SPDY_NO on error
+ */
+int
+SPDYF_openssl_init(struct SPDY_Daemon *daemon);
+
+
+/**
+ * Deinitializing openssl for a daemon. Should be called
+ * when the deamon is stopped.
+ *
+ * @param daemon SPDY_Daemon which is being stopped
+ */
+void
+SPDYF_openssl_deinit(struct SPDY_Daemon *daemon);
+
+
+/**
+ * Initializing openssl for a specific connection. Must be called
+ * after the connection has been accepted.
+ *
+ * @param session SPDY_Session whose socket will be used by openssl
+ * @return SPDY_NO if some openssl funcs fail. SPDY_YES otherwise
+ */
+int
+SPDYF_openssl_new_session(struct SPDY_Session *session);
+
+
+/**
+ * Deinitializing openssl for a specific connection. Should be called
+ * closing session's socket.
+ *
+ * @param session SPDY_Session whose socket is used by openssl
+ */
+void
+SPDYF_openssl_close_session(struct SPDY_Session *session);
+
+
+/**
+ * Reading from a TLS socket. Reads available data and put it to the
+ * buffer.
+ *
+ * @param session for which data is received
+ * @param buffer where data from the socket will be written to
+ * @param size of the buffer
+ * @return number of bytes (at most size) read from the TLS connection
+ *         0 if the other party has closed the connection
+ *         SPDY_IO_ERROR code on error
+ */
+int
+SPDYF_openssl_recv(struct SPDY_Session *session,
+				void * buffer,
+				size_t size);
+
+
+/**
+ * Writing to a TLS socket. Writes the data given into the buffer to the
+ * TLS socket.
+ *
+ * @param session whose context is used
+ * @param buffer from where data will be written to the socket
+ * @param size number of bytes to be taken from the buffer
+ * @return number of bytes (at most size) from the buffer that has been
+ * 			written to the TLS connection
+ *         0 if the other party has closed the connection
+ *         SPDY_IO_ERROR code on error
+ */
+int
+SPDYF_openssl_send(struct SPDY_Session *session,
+				const void * buffer,
+				size_t size);
+
+
+/**
+ * Checks if there is data staying in the buffers of the underlying
+ * system that waits to be read.
+ *
+ * @param session which is checked
+ * @return SPDY_YES if data is pending or SPDY_NO otherwise
+ */
+int
+SPDYF_openssl_is_pending(struct SPDY_Session *session);
+
+
+/**
+ * Nothing.
+ *
+ * @param session
+ * @return SPDY_NO if writing must not happen in the call;
+ *         SPDY_YES otherwise
+ */
+int
+SPDYF_openssl_before_write(struct SPDY_Session *session);
+
+
+/**
+ * Nothing.
+ *
+ * @param session
+ * @param was_written has the same value as the write function for the
+ *        session will return 
+ * @return returned value will be used by the write function to return
+ */
+int
+SPDYF_openssl_after_write(struct SPDY_Session *session, int was_written);
+
+
+#endif
diff --git a/src/microspdy/io_raw.c b/src/microspdy/io_raw.c
new file mode 100644
index 0000000..722f347
--- /dev/null
+++ b/src/microspdy/io_raw.c
@@ -0,0 +1,194 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file io_raw.c
+ * @brief  IO for SPDY without TLS.
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include "internal.h"
+#include "session.h"
+#include "io_raw.h"
+//TODO put in in the right place
+#include <netinet/tcp.h>
+
+
+void
+SPDYF_raw_global_init()
+{
+}
+
+
+void
+SPDYF_raw_global_deinit()
+{
+}
+
+
+int
+SPDYF_raw_init(struct SPDY_Daemon *daemon)
+{
+  (void)daemon;
+  
+	return SPDY_YES;
+}
+
+
+void
+SPDYF_raw_deinit(struct SPDY_Daemon *daemon)
+{
+  (void)daemon;
+}
+
+
+int
+SPDYF_raw_new_session(struct SPDY_Session *session)
+{	
+  int fd_flags;
+  int val = 1;
+  int ret;
+  
+	//setting the socket to be non-blocking
+	fd_flags = fcntl (session->socket_fd, F_GETFL);
+	if ( -1 == fd_flags
+		|| 0 != fcntl (session->socket_fd, F_SETFL, fd_flags | O_NONBLOCK))
+		SPDYF_DEBUG("WARNING: Couldn't set the new connection to be non-blocking");
+  
+  if(SPDY_DAEMON_FLAG_NO_DELAY & session->daemon->flags)
+  {
+    ret = setsockopt(session->socket_fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
+    if(-1 == ret)
+      SPDYF_DEBUG("WARNING: Couldn't set the new connection to TCP_NODELAY");
+  }
+  
+	return SPDY_YES;
+}
+
+
+void
+SPDYF_raw_close_session(struct SPDY_Session *session)
+{
+  (void)session;
+}
+
+
+int
+SPDYF_raw_recv(struct SPDY_Session *session,
+				void * buffer,
+				size_t size)
+{
+	int n = read(session->socket_fd, 
+					buffer,
+					size);
+	//if(n > 0) SPDYF_DEBUG("recvd: %i",n);
+	if (n < 0)
+	{
+		switch(errno)
+		{				
+			case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+      case EWOULDBLOCK:
+#endif
+			case EINTR:
+        return SPDY_IO_ERROR_AGAIN;
+				
+			default:
+				return SPDY_IO_ERROR_ERROR;
+		}
+	}
+
+	return n;
+}
+
+
+int
+SPDYF_raw_send(struct SPDY_Session *session,
+				const void * buffer,
+				size_t size)
+{
+	int n = write(session->socket_fd, 
+					buffer,
+					size);
+	//if(n > 0) SPDYF_DEBUG("sent: %i",n);
+	if (n < 0)
+	{
+		switch(errno)
+		{				
+			case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+      case EWOULDBLOCK:
+#endif
+			case EINTR:
+        return SPDY_IO_ERROR_AGAIN;
+				
+			default:
+				return SPDY_IO_ERROR_ERROR;
+		}
+	}
+	
+	return n;
+}
+
+
+int
+SPDYF_raw_is_pending(struct SPDY_Session *session)
+{
+  (void)session;
+  
+	return SPDY_NO;
+}
+
+
+int
+SPDYF_raw_before_write(struct SPDY_Session *session)
+{
+#if HAVE_DECL_TCP_CORK
+  if(0 == (SPDY_DAEMON_FLAG_NO_DELAY & session->daemon->flags))
+  {
+    int val = 1;
+    int ret;
+    
+    ret = setsockopt(session->socket_fd, IPPROTO_TCP, TCP_CORK, &val, (socklen_t)sizeof(val));
+    if(-1 == ret)
+      SPDYF_DEBUG("WARNING: Couldn't set the new connection to TCP_CORK");
+  }
+#endif
+  
+	return SPDY_YES;
+}
+
+
+int
+SPDYF_raw_after_write(struct SPDY_Session *session, int was_written)
+{
+#if HAVE_DECL_TCP_CORK
+  if(SPDY_YES == was_written && 0 == (SPDY_DAEMON_FLAG_NO_DELAY & session->daemon->flags))
+  {
+    int val = 0;
+    int ret;
+    
+    ret = setsockopt(session->socket_fd, IPPROTO_TCP, TCP_CORK, &val, (socklen_t)sizeof(val));
+    if(-1 == ret)
+      SPDYF_DEBUG("WARNING: Couldn't unset the new connection to TCP_CORK");
+  }
+  
+#endif
+	return was_written;
+}
diff --git a/src/microspdy/io_raw.h b/src/microspdy/io_raw.h
new file mode 100644
index 0000000..8ca830b
--- /dev/null
+++ b/src/microspdy/io_raw.h
@@ -0,0 +1,158 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file io_raw.h
+ * @brief  IO for SPDY without TLS.
+ * @author Andrey Uzunov
+ */
+
+#ifndef IO_RAW_H
+#define IO_RAW_H
+
+#include "platform.h"
+
+
+/**
+ * Must be called only once in the program.
+ *
+ */
+void
+SPDYF_raw_global_init();
+
+
+/**
+ * Should be called
+ * at the end of the program.
+ *
+ */
+void
+SPDYF_raw_global_deinit();
+
+
+/**
+ * Must be called when the daemon starts.
+ *
+ * @param daemon SPDY_Daemon 
+ * @return SPDY_YES on success or SPDY_NO on error
+ */
+int
+SPDYF_raw_init(struct SPDY_Daemon *daemon);
+
+
+/**
+ * Should be called
+ * when the deamon is stopped.
+ *
+ * @param daemon SPDY_Daemon which is being stopped
+ */
+void
+SPDYF_raw_deinit(struct SPDY_Daemon *daemon);
+
+
+/**
+ * Must be called
+ * after the connection has been accepted.
+ *
+ * @param session SPDY_Session whose socket will be used
+ * @return SPDY_NO if some funcs fail. SPDY_YES otherwise
+ */
+int
+SPDYF_raw_new_session(struct SPDY_Session *session);
+
+
+/**
+ * Should be called
+ * closing session's socket.
+ *
+ * @param session SPDY_Session whose socket is used
+ */
+void
+SPDYF_raw_close_session(struct SPDY_Session *session);
+
+
+/**
+ * Reading from socket. Reads available data and put it to the
+ * buffer.
+ *
+ * @param session for which data is received
+ * @param buffer where data from the socket will be written to
+ * @param size of the buffer
+ * @return number of bytes (at most size) read from the connection
+ *         0 if the other party has closed the connection
+ *         SPDY_IO_ERROR code on error
+ */
+int
+SPDYF_raw_recv(struct SPDY_Session *session,
+				void * buffer,
+				size_t size);
+
+
+/**
+ * Writing to socket. Writes the data given into the buffer to the
+ * socket.
+ *
+ * @param session whose context is used
+ * @param buffer from where data will be written to the socket
+ * @param size number of bytes to be taken from the buffer
+ * @return number of bytes (at most size) from the buffer that has been
+ * 			written to the connection
+ *         0 if the other party has closed the connection
+ *         SPDY_IO_ERROR code on error
+ */
+int
+SPDYF_raw_send(struct SPDY_Session *session,
+				const void * buffer,
+				size_t size);
+
+
+/**
+ * Checks if there is data staying in the buffers of the underlying
+ * system that waits to be read. Always returns SPDY_NO, as we do not
+ * use a subsystem here.
+ *
+ * @param session which is checked
+ * @return SPDY_YES if data is pending or SPDY_NO otherwise
+ */
+int
+SPDYF_raw_is_pending(struct SPDY_Session *session);
+
+
+/**
+ * Sets TCP_CORK.
+ *
+ * @param session
+ * @return SPDY_NO if writing must not happen in the call;
+ *         SPDY_YES otherwise
+ */
+int
+SPDYF_raw_before_write(struct SPDY_Session *session);
+
+
+/**
+ * Unsets TCP_CORK.
+ *
+ * @param session
+ * @param was_written has the same value as the write function for the
+ *        session will return 
+ * @return returned value will be used by the write function to return
+ */
+int
+SPDYF_raw_after_write(struct SPDY_Session *session, int was_written);
+
+#endif
diff --git a/src/microspdy/session.c b/src/microspdy/session.c
new file mode 100644
index 0000000..3714c25
--- /dev/null
+++ b/src/microspdy/session.c
@@ -0,0 +1,1769 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file session.c
+ * @brief  TCP connection/SPDY session handling. So far most of the
+ * 			functions for handling SPDY framing layer are here.
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include "structures.h"
+#include "internal.h"
+#include "session.h"
+#include "compression.h"
+#include "stream.h"
+#include "io.h"
+
+
+/**
+ * Handler for reading the full SYN_STREAM frame after we know that
+ * the frame is such.
+ * The function waits for the full frame and then changes status
+ * of the session. New stream is created.
+ *
+ * @param session SPDY_Session whose read buffer is used.
+ */
+static void
+spdyf_handler_read_syn_stream (struct SPDY_Session *session)
+{
+  size_t name_value_strm_size = 0;
+  unsigned int compressed_data_size;
+  int ret;
+  void *name_value_strm = NULL;
+  struct SPDYF_Control_Frame *frame;
+  struct SPDY_NameValue *headers;
+
+  SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status
+               || SPDY_SESSION_STATUS_WAIT_FOR_BODY == session->status,
+               "the function is called wrong");
+
+  frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls;
+
+  //handle subheaders
+  if(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status)
+    {
+      if(0 == frame->length)
+        {
+          //protocol error: incomplete frame
+          //we just ignore it since there is no stream id for which to
+          //send RST_STREAM
+          //TODO maybe GOAWAY and closing session is appropriate
+          SPDYF_DEBUG("zero long SYN_STREAM received");
+          session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
+          free(frame);
+          return;
+        }
+
+      if(SPDY_YES != SPDYF_stream_new(session))
+        {
+          /* waiting for some more fields to create new stream
+             or something went wrong, SPDYF_stream_new has handled the
+             situation */
+          return;
+        }
+
+      session->current_stream_id = session->streams_head->stream_id;
+      if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE)
+        {
+          //TODO no need to create stream if this happens
+          session->status = SPDY_SESSION_STATUS_IGNORE_BYTES;
+          return;
+        }
+      else
+        session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY;
+    }
+
+  //handle body
+
+  //start reading the compressed name/value pairs (http headers)
+  compressed_data_size = frame->length //everything after length field
+    - 10;//4B stream id, 4B assoc strem id, 2B priority, unused and slot
+
+  if(session->read_buffer_offset - session->read_buffer_beginning < compressed_data_size)
+    {
+      // the full frame is not yet here, try later
+      return;
+    }
+
+  if ( (compressed_data_size > 0) &&
+       (SPDY_YES !=
+        SPDYF_zlib_inflate(&session->zlib_recv_stream,
+                           session->read_buffer + session->read_buffer_beginning,
+                           compressed_data_size,
+                           &name_value_strm,
+                           &name_value_strm_size)) )
+    {
+      /* something went wrong on inflating,
+       * the state of the stream for decompression is unknown
+       * and we may not be able to read anything more received on
+       * this session,
+       * so it is better to close the session */
+      free(name_value_strm);
+      free(frame);
+
+      /* mark the session for closing and close it, when
+       * everything on the output queue is already written */
+      session->status = SPDY_SESSION_STATUS_FLUSHING;
+
+      SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_INTERNAL_ERROR, false);
+
+      return;
+    }
+
+  if(0 == name_value_strm_size || 0 == compressed_data_size)
+    {
+      //Protocol error: send RST_STREAM
+      if(SPDY_YES != SPDYF_prepare_rst_stream(session, session->streams_head,
+                                              SPDY_RST_STREAM_STATUS_PROTOCOL_ERROR))
+        {
+          //no memory, try later to send RST
+          free(name_value_strm);
+          return;
+        }
+    }
+  else
+    {
+      ret = SPDYF_name_value_from_stream(name_value_strm, name_value_strm_size, &headers);
+      if(SPDY_NO == ret)
+        {
+          //memory error, try later
+          free(name_value_strm);
+          return;
+        }
+
+      session->streams_head->headers = headers;
+      //inform the application layer for the new stream received
+      if(SPDY_YES != session->daemon->fnew_stream_cb(session->daemon->fcls, session->streams_head))
+        {
+          //memory error, try later
+          free(name_value_strm);
+          return;
+        }
+
+      session->read_buffer_beginning += compressed_data_size;
+    }
+
+  //SPDYF_DEBUG("syn_stream received: id %i", session->current_stream_id);
+
+  //change state to wait for new frame
+  free(name_value_strm);
+  session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
+  free(frame);
+}
+
+
+/**
+ * Handler for reading the GOAWAY frame after we know that
+ * the frame is such.
+ * The function waits for the full frame and then changes status
+ * of the session.
+ *
+ * @param session SPDY_Session whose read buffer is used.
+ */
+static void
+spdyf_handler_read_goaway (struct SPDY_Session *session)
+{
+	struct SPDYF_Control_Frame *frame;
+	uint32_t last_good_stream_id;
+	uint32_t status_int;
+	enum SPDY_GOAWAY_STATUS status;
+
+	SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status,
+		"the function is called wrong");
+
+	frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls;
+
+	if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE)
+	{
+		//this is a protocol error/attack
+		session->status = SPDY_SESSION_STATUS_IGNORE_BYTES;
+		return;
+	}
+
+	if(0 != frame->flags || 8 != frame->length)
+	{
+		//this is a protocol error
+		SPDYF_DEBUG("wrong GOAWAY received");
+		//anyway, it will be handled
+	}
+
+	if((session->read_buffer_offset - session->read_buffer_beginning) < frame->length)
+	{
+		//not all fields are received
+		//try later
+		return;
+	}
+
+	//mark that the session is almost closed
+	session->is_goaway_received = true;
+
+	if(8 == frame->length)
+	{
+		memcpy(&last_good_stream_id, session->read_buffer + session->read_buffer_beginning, 4);
+		last_good_stream_id = NTOH31(last_good_stream_id);
+		session->read_buffer_beginning += 4;
+
+		memcpy(&status_int, session->read_buffer + session->read_buffer_beginning, 4);
+		status = ntohl(status_int);
+		session->read_buffer_beginning += 4;
+
+		//TODO do something with last_good
+
+		//SPDYF_DEBUG("Received GOAWAY; status=%i; lastgood=%i",status,last_good_stream_id);
+
+		//do something according to the status
+		//TODO
+		switch(status)
+		{
+			case SPDY_GOAWAY_STATUS_OK:
+				break;
+			case SPDY_GOAWAY_STATUS_PROTOCOL_ERROR:
+				break;
+			case SPDY_GOAWAY_STATUS_INTERNAL_ERROR:
+				break;
+		}
+
+    //SPDYF_DEBUG("goaway received: status %i", status);
+	}
+
+	session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
+	free(frame);
+}
+
+
+/**
+ * Handler for reading RST_STREAM frames. After receiving the frame
+ * the stream moves into closed state and status
+ * of the session is changed. Frames, belonging to this stream, which
+ * are still at the output queue, will be ignored later.
+ *
+ * @param session SPDY_Session whose read buffer is used.
+ */
+static void
+spdyf_handler_read_rst_stream (struct SPDY_Session *session)
+{
+	struct SPDYF_Control_Frame *frame;
+	uint32_t stream_id;
+	int32_t status_int;
+	//enum SPDY_RST_STREAM_STATUS status; //for debug
+	struct SPDYF_Stream *stream;
+
+	SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status,
+		"the function is called wrong");
+
+	frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls;
+
+	if(0 != frame->flags || 8 != frame->length)
+	{
+		//this is a protocol error
+		SPDYF_DEBUG("wrong RST_STREAM received");
+		//ignore as a large frame
+		session->status = SPDY_SESSION_STATUS_IGNORE_BYTES;
+		return;
+	}
+
+	if((session->read_buffer_offset - session->read_buffer_beginning) < frame->length)
+	{
+		//not all fields are received
+		//try later
+		return;
+	}
+
+    memcpy(&stream_id, session->read_buffer + session->read_buffer_beginning, 4);
+	stream_id = NTOH31(stream_id);
+	session->read_buffer_beginning += 4;
+
+    memcpy(&status_int, session->read_buffer + session->read_buffer_beginning, 4);
+	//status = ntohl(status_int); //for debug
+	session->read_buffer_beginning += 4;
+
+	session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
+	free(frame);
+
+	//mark the stream as closed
+	stream = session->streams_head;
+	while(NULL != stream)
+	{
+		if(stream_id == stream->stream_id)
+		{
+			stream->is_in_closed = true;
+			stream->is_out_closed = true;
+			break;
+		}
+		stream = stream->next;
+	}
+
+	//SPDYF_DEBUG("Received RST_STREAM; status=%i; id=%i",status,stream_id);
+
+	//do something according to the status
+	//TODO
+	/*switch(status)
+	{
+		case SPDY_RST_STREAM_STATUS_PROTOCOL_ERROR:
+			break;
+	}*/
+}
+
+
+/**
+ * Handler for reading DATA frames. In requests they are used for POST
+ * arguments.
+ *
+ * @param session SPDY_Session whose read buffer is used.
+ */
+static void
+spdyf_handler_read_data (struct SPDY_Session *session)
+{
+  int ret;
+  struct SPDYF_Data_Frame * frame;
+  struct SPDYF_Stream * stream;
+
+	SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status
+		|| SPDY_SESSION_STATUS_WAIT_FOR_BODY == session->status,
+		"the function is called wrong");
+
+  //SPDYF_DEBUG("DATA frame received (POST?). Ignoring");
+
+  //SPDYF_SIGINT("");
+
+	frame = (struct SPDYF_Data_Frame *)session->frame_handler_cls;
+
+	//handle subheaders
+	if(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status)
+	{
+		if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE)
+		{
+			session->status = SPDY_SESSION_STATUS_IGNORE_BYTES;
+			return;
+		}
+		else
+			session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY;
+	}
+
+	//handle body
+
+	if(session->read_buffer_offset - session->read_buffer_beginning
+		>= frame->length)
+	{
+    stream = SPDYF_stream_find(frame->stream_id, session);
+
+    if(NULL == stream || stream->is_in_closed || NULL == session->daemon->received_data_cb)
+    {
+      if(NULL == session->daemon->received_data_cb)
+      SPDYF_DEBUG("No callback for DATA frame set; Ignoring DATA frame!");
+
+      //TODO send error?
+
+      //TODO for now ignore frame
+      session->read_buffer_beginning += frame->length;
+      session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
+      free(frame);
+      return;
+    }
+
+    ret = session->daemon->freceived_data_cb(session->daemon->cls,
+                                      stream,
+                                      session->read_buffer + session->read_buffer_beginning,
+                                      frame->length,
+                                      0 == (SPDY_DATA_FLAG_FIN & frame->flags));
+
+    session->read_buffer_beginning += frame->length;
+
+    stream->window_size -= frame->length;
+
+    //TODO close in and send rst maybe
+    SPDYF_ASSERT(SPDY_YES == ret, "Cancel POST data is not yet implemented");
+
+    if(SPDY_DATA_FLAG_FIN & frame->flags)
+    {
+      stream->is_in_closed = true;
+    }
+    else if(stream->window_size < SPDYF_INITIAL_WINDOW_SIZE / 2)
+    {
+      //very simple implementation of flow control
+      //when the window's size is under the half of the initial value,
+      //increase it again up to the initial value
+
+      //prepare WINDOW_UPDATE
+      if(SPDY_YES == SPDYF_prepare_window_update(session, stream,
+            SPDYF_INITIAL_WINDOW_SIZE - stream->window_size))
+      {
+        stream->window_size = SPDYF_INITIAL_WINDOW_SIZE;
+      }
+      //else: do it later
+    }
+
+    //SPDYF_DEBUG("data received: id %i", frame->stream_id);
+
+    session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
+    free(frame);
+	}
+}
+
+
+int
+SPDYF_handler_write_syn_reply (struct SPDY_Session *session)
+{
+	struct SPDYF_Response_Queue *response_queue = session->response_queue_head;
+	struct SPDYF_Stream *stream = response_queue->stream;
+	struct SPDYF_Control_Frame control_frame;
+	void *compressed_headers = NULL;
+	size_t compressed_headers_size=0;
+	size_t used_data=0;
+	size_t total_size;
+	uint32_t stream_id_nbo;
+
+	SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment");
+
+	memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame));
+
+	if(SPDY_YES != SPDYF_zlib_deflate(&session->zlib_send_stream,
+		response_queue->data,
+		response_queue->data_size,
+		&used_data,
+		&compressed_headers,
+		&compressed_headers_size))
+	{
+		/* something went wrong on compressing,
+		* the state of the stream for compression is unknown
+		* and we may not be able to send anything more on
+		* this session,
+		* so it is better to close the session right now */
+		session->status = SPDY_SESSION_STATUS_CLOSING;
+
+		free(compressed_headers);
+
+		return SPDY_NO;
+	}
+
+	//TODO do we need this used_Data
+	SPDYF_ASSERT(used_data == response_queue->data_size, "not everything was used by zlib");
+
+	total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header
+		+ 4 // stream id as "subheader"
+		+ compressed_headers_size;
+
+	if(NULL == (session->write_buffer = malloc(total_size)))
+	{
+		/* no memory
+		 * since we do not save the compressed data anywhere and
+		 * the sending zlib stream is already in new state, we must
+		 * close the session */
+		session->status = SPDY_SESSION_STATUS_CLOSING;
+
+		free(compressed_headers);
+
+		return SPDY_NO;
+	}
+	session->write_buffer_beginning = 0;
+	session->write_buffer_offset = 0;
+	session->write_buffer_size = total_size;
+
+	control_frame.length = compressed_headers_size + 4; // compressed data + stream_id
+	SPDYF_CONTROL_FRAME_HTON(&control_frame);
+
+	//put frame headers to write buffer
+	memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame));
+	session->write_buffer_offset +=  sizeof(struct SPDYF_Control_Frame);
+
+	//put stream id to write buffer
+	stream_id_nbo = HTON31(stream->stream_id);
+	memcpy(session->write_buffer + session->write_buffer_offset, &stream_id_nbo, 4);
+	session->write_buffer_offset += 4;
+
+	//put compressed name/value pairs to write buffer
+	memcpy(session->write_buffer + session->write_buffer_offset, compressed_headers, compressed_headers_size);
+	session->write_buffer_offset +=  compressed_headers_size;
+
+	SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1");
+	SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2");
+
+	//DEBUG CODE, break compression state to see what happens
+/*	SPDYF_zlib_deflate(&session->zlib_send_stream,
+		"1234567890",
+		10,
+		&used_data,
+		&compressed_headers,
+		&compressed_headers_size);
+*/
+	free(compressed_headers);
+
+	session->last_replied_to_stream_id = stream->stream_id;
+
+  //SPDYF_DEBUG("syn_reply sent: id %i", stream->stream_id);
+
+	return SPDY_YES;
+}
+
+
+int
+SPDYF_handler_write_goaway (struct SPDY_Session *session)
+{
+	struct SPDYF_Response_Queue *response_queue = session->response_queue_head;
+	struct SPDYF_Control_Frame control_frame;
+	size_t total_size;
+	int last_good_stream_id;
+
+	SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment");
+
+	memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame));
+
+	session->is_goaway_sent = true;
+
+	total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header
+		+ 4 // last good stream id as "subheader"
+		+ 4; // status code as "subheader"
+
+	if(NULL == (session->write_buffer = malloc(total_size)))
+	{
+		return SPDY_NO;
+	}
+	session->write_buffer_beginning = 0;
+	session->write_buffer_offset = 0;
+	session->write_buffer_size = total_size;
+
+	control_frame.length = 8; // always for GOAWAY
+	SPDYF_CONTROL_FRAME_HTON(&control_frame);
+
+	//put frame headers to write buffer
+	memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame));
+	session->write_buffer_offset +=  sizeof(struct SPDYF_Control_Frame);
+
+	//put last good stream id to write buffer
+	last_good_stream_id = HTON31(session->last_replied_to_stream_id);
+	memcpy(session->write_buffer + session->write_buffer_offset, &last_good_stream_id, 4);
+	session->write_buffer_offset +=  4;
+
+	//put "data" to write buffer. This is the status
+	memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, 4);
+	session->write_buffer_offset +=  4;
+	//data is not freed by the destroy function so:
+	//free(response_queue->data);
+
+  //SPDYF_DEBUG("goaway sent: status %i", NTOH31(*(uint32_t*)(response_queue->data)));
+
+	SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1");
+	SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2");
+
+	return SPDY_YES;
+}
+
+
+int
+SPDYF_handler_write_data (struct SPDY_Session *session)
+{
+	struct SPDYF_Response_Queue *response_queue = session->response_queue_head;
+	struct SPDYF_Response_Queue *new_response_queue;
+	size_t total_size;
+	struct SPDYF_Data_Frame data_frame;
+	ssize_t ret;
+	bool more;
+
+	SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment");
+
+	memcpy(&data_frame, response_queue->data_frame, sizeof(data_frame));
+
+	if(NULL == response_queue->response->rcb)
+	{
+		//standard response with data into the struct
+		SPDYF_ASSERT(NULL != response_queue->data, "no data for the response");
+
+		total_size = sizeof(struct SPDYF_Data_Frame) //SPDY header
+			+ response_queue->data_size;
+
+		if(NULL == (session->write_buffer = malloc(total_size)))
+		{
+			return SPDY_NO;
+		}
+		session->write_buffer_beginning = 0;
+		session->write_buffer_offset = 0;
+		session->write_buffer_size = total_size;
+
+		data_frame.length = response_queue->data_size;
+		SPDYF_DATA_FRAME_HTON(&data_frame);
+
+		//put SPDY headers to the writing buffer
+		memcpy(session->write_buffer + session->write_buffer_offset,&data_frame,sizeof(struct SPDYF_Data_Frame));
+		session->write_buffer_offset +=  sizeof(struct SPDYF_Data_Frame);
+
+		//put data to the writing buffer
+		memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, response_queue->data_size);
+		session->write_buffer_offset +=  response_queue->data_size;
+	}
+	else
+	{
+		/* response with callbacks. The lib will produce more than 1
+		 * data frames
+		 */
+
+		total_size = sizeof(struct SPDYF_Data_Frame) //SPDY header
+			+ SPDY_MAX_SUPPORTED_FRAME_SIZE; //max possible size
+
+		if(NULL == (session->write_buffer = malloc(total_size)))
+		{
+			return SPDY_NO;
+		}
+		session->write_buffer_beginning = 0;
+		session->write_buffer_offset = 0;
+		session->write_buffer_size = total_size;
+
+		ret = response_queue->response->rcb(response_queue->response->rcb_cls,
+			session->write_buffer + sizeof(struct SPDYF_Data_Frame),
+			response_queue->response->rcb_block_size,
+			&more);
+
+		if(ret < 0 || ret > response_queue->response->rcb_block_size)
+		{
+			free(session->write_buffer);
+      session->write_buffer = NULL;
+
+      //send RST_STREAM
+      if(SPDY_YES == (ret = SPDYF_prepare_rst_stream(session,
+        response_queue->stream,
+        SPDY_RST_STREAM_STATUS_INTERNAL_ERROR)))
+      {
+        return SPDY_NO;
+      }
+
+      //else no memory
+			//for now close session
+			//TODO what?
+			session->status = SPDY_SESSION_STATUS_CLOSING;
+
+			return SPDY_NO;
+		}
+		if(0 == ret && more)
+		{
+			//the app couldn't write anything to buf but later will
+			free(session->write_buffer);
+			session->write_buffer = NULL;
+			session->write_buffer_size = 0;
+
+			if(NULL != response_queue->next)
+			{
+				//put the frame at the end of the queue
+				//otherwise - head of line blocking
+				session->response_queue_head = response_queue->next;
+				session->response_queue_head->prev = NULL;
+				session->response_queue_tail->next = response_queue;
+				response_queue->prev = session->response_queue_tail;
+				response_queue->next = NULL;
+				session->response_queue_tail = response_queue;
+			}
+
+			return SPDY_YES;
+		}
+
+		if(more)
+		{
+			//create another response queue object to call the user cb again
+			if(NULL == (new_response_queue = SPDYF_response_queue_create(true,
+							NULL,
+							0,
+							response_queue->response,
+							response_queue->stream,
+							false,
+							response_queue->frqcb,
+							response_queue->frqcb_cls,
+							response_queue->rrcb,
+							response_queue->rrcb_cls)))
+			{
+				//TODO send RST_STREAM
+				//for now close session
+				session->status = SPDY_SESSION_STATUS_CLOSING;
+
+				free(session->write_buffer);
+        session->write_buffer = NULL;
+				return SPDY_NO;
+			}
+
+			//put it at second position on the queue
+			new_response_queue->prev = response_queue;
+			new_response_queue->next = response_queue->next;
+			if(NULL == response_queue->next)
+			{
+				session->response_queue_tail = new_response_queue;
+			}
+			else
+			{
+				response_queue->next->prev = new_response_queue;
+			}
+			response_queue->next = new_response_queue;
+
+			response_queue->frqcb = NULL;
+			response_queue->frqcb_cls = NULL;
+			response_queue->rrcb = NULL;
+			response_queue->rrcb_cls = NULL;
+		}
+		else
+		{
+			data_frame.flags |= SPDY_DATA_FLAG_FIN;
+		}
+
+		data_frame.length = ret;
+		SPDYF_DATA_FRAME_HTON(&data_frame);
+
+		//put SPDY headers to the writing buffer
+		memcpy(session->write_buffer + session->write_buffer_offset,
+			&data_frame,
+			sizeof(struct SPDYF_Data_Frame));
+		session->write_buffer_offset +=  sizeof(struct SPDYF_Data_Frame);
+		session->write_buffer_offset +=  ret;
+		session->write_buffer_size = session->write_buffer_offset;
+	}
+
+  //SPDYF_DEBUG("data sent: id %i", NTOH31(data_frame.stream_id));
+
+	SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1");
+	SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2");
+
+	return SPDY_YES;
+}
+
+
+int
+SPDYF_handler_write_rst_stream (struct SPDY_Session *session)
+{
+	struct SPDYF_Response_Queue *response_queue = session->response_queue_head;
+	struct SPDYF_Control_Frame control_frame;
+	size_t total_size;
+
+	SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment");
+
+	memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame));
+
+	total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header
+		+ 4 // stream id as "subheader"
+		+ 4; // status code as "subheader"
+
+	if(NULL == (session->write_buffer = malloc(total_size)))
+	{
+		return SPDY_NO;
+	}
+	session->write_buffer_beginning = 0;
+	session->write_buffer_offset = 0;
+	session->write_buffer_size = total_size;
+
+	control_frame.length = 8; // always for RST_STREAM
+	SPDYF_CONTROL_FRAME_HTON(&control_frame);
+
+	//put frame headers to write buffer
+	memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame));
+	session->write_buffer_offset +=  sizeof(struct SPDYF_Control_Frame);
+
+	//put stream id to write buffer. This is the status
+	memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, 8);
+	session->write_buffer_offset +=  8;
+	//data is not freed by the destroy function so:
+	//free(response_queue->data);
+
+  //SPDYF_DEBUG("rst_stream sent: id %i", NTOH31((((uint64_t)response_queue->data) & 0xFFFF0000) >> 32));
+
+	SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1");
+	SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2");
+
+	return SPDY_YES;
+}
+
+
+int
+SPDYF_handler_write_window_update (struct SPDY_Session *session)
+{
+	struct SPDYF_Response_Queue *response_queue = session->response_queue_head;
+	struct SPDYF_Control_Frame control_frame;
+	size_t total_size;
+
+	SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment");
+
+	memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame));
+
+	total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header
+		+ 4 // stream id as "subheader"
+		+ 4; // delta-window-size as "subheader"
+
+	if(NULL == (session->write_buffer = malloc(total_size)))
+	{
+		return SPDY_NO;
+	}
+	session->write_buffer_beginning = 0;
+	session->write_buffer_offset = 0;
+	session->write_buffer_size = total_size;
+
+	control_frame.length = 8; // always for WINDOW_UPDATE
+	SPDYF_CONTROL_FRAME_HTON(&control_frame);
+
+	//put frame headers to write buffer
+	memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame));
+	session->write_buffer_offset +=  sizeof(struct SPDYF_Control_Frame);
+
+	//put stream id and delta-window-size to write buffer
+	memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, 8);
+	session->write_buffer_offset +=  8;
+
+  //SPDYF_DEBUG("window_update sent: id %i", NTOH31((((uint64_t)response_queue->data) & 0xFFFF0000) >> 32));
+
+	SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1");
+	SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2");
+
+	return SPDY_YES;
+}
+
+
+void
+SPDYF_handler_ignore_frame (struct SPDY_Session *session)
+{
+	struct SPDYF_Control_Frame *frame;
+
+	SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status
+		|| SPDY_SESSION_STATUS_WAIT_FOR_BODY == session->status,
+		"the function is called wrong");
+
+
+	frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls;
+
+	//handle subheaders
+	if(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status)
+	{
+		if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE)
+		{
+			session->status = SPDY_SESSION_STATUS_IGNORE_BYTES;
+			return;
+		}
+		else
+			session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY;
+	}
+
+	//handle body
+
+	if(session->read_buffer_offset - session->read_buffer_beginning
+		>= frame->length)
+	{
+		session->read_buffer_beginning += frame->length;
+		session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
+		free(frame);
+	}
+}
+
+
+int
+SPDYF_session_read (struct SPDY_Session *session)
+{
+	int bytes_read;
+	bool reallocate;
+	size_t actual_buf_size;
+
+	if(SPDY_SESSION_STATUS_CLOSING == session->status
+		|| SPDY_SESSION_STATUS_FLUSHING == session->status)
+		return SPDY_NO;
+
+	//if the read buffer is full to the end, we need to reallocate space
+	if (session->read_buffer_size == session->read_buffer_offset)
+	{
+		//but only if the state of the session requires it
+		//i.e. no further proceeding is possible without reallocation
+		reallocate = false;
+		actual_buf_size = session->read_buffer_offset
+			- session->read_buffer_beginning;
+		switch(session->status)
+		{
+			case SPDY_SESSION_STATUS_WAIT_FOR_HEADER:
+
+			case SPDY_SESSION_STATUS_IGNORE_BYTES:
+				//we need space for a whole control frame header
+				if(actual_buf_size < sizeof(struct SPDYF_Control_Frame))
+					reallocate = true;
+				break;
+
+			case SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER:
+
+			case SPDY_SESSION_STATUS_WAIT_FOR_BODY:
+				//we need as many bytes as set in length field of the
+				//header
+				SPDYF_ASSERT(NULL != session->frame_handler_cls,
+					"no frame for session");
+				if(session->frame_handler != &spdyf_handler_read_data)
+				{
+					if(actual_buf_size
+						< ((struct SPDYF_Control_Frame *)session->frame_handler_cls)->length)
+						reallocate = true;
+				}
+				else
+				{
+					if(actual_buf_size
+						< ((struct SPDYF_Data_Frame *)session->frame_handler_cls)->length)
+						reallocate = true;
+				}
+				break;
+
+			case SPDY_SESSION_STATUS_CLOSING:
+			case SPDY_SESSION_STATUS_FLUSHING:
+				//nothing needed
+				break;
+		}
+
+		if(reallocate)
+		{
+			//reuse the space in the buffer that was already read by the lib
+			memmove(session->read_buffer,
+				session->read_buffer + session->read_buffer_beginning,
+				session->read_buffer_offset - session->read_buffer_beginning);
+
+			session->read_buffer_offset -= session->read_buffer_beginning;
+			session->read_buffer_beginning = 0;
+		}
+		else
+		{
+			//will read next time
+			//TODO optimize it, memmove more often?
+			return SPDY_NO;
+		}
+	}
+
+	session->last_activity = SPDYF_monotonic_time();
+
+	//actual read from the TLS socket
+	bytes_read = session->fio_recv(session,
+					session->read_buffer + session->read_buffer_offset,
+					session->read_buffer_size - session->read_buffer_offset);
+
+	switch(bytes_read)
+	{
+		case SPDY_IO_ERROR_CLOSED:
+			//The TLS connection was closed by the other party, clean
+			//or not
+			shutdown (session->socket_fd, SHUT_RD);
+			session->read_closed = true;
+			session->status = SPDY_SESSION_STATUS_CLOSING;
+			return SPDY_YES;
+
+		case SPDY_IO_ERROR_ERROR:
+			//any kind of error in the TLS subsystem
+			//try to prepare GOAWAY frame
+			SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_INTERNAL_ERROR, false);
+			//try to flush the queue when write is called
+			session->status = SPDY_SESSION_STATUS_FLUSHING;
+			return SPDY_YES;
+
+		case SPDY_IO_ERROR_AGAIN:
+			//read or write should be called again; leave it for the
+			//next time
+			return SPDY_NO;
+
+		//default:
+			//something was really read from the TLS subsystem
+			//just continue
+	}
+
+	session->read_buffer_offset += bytes_read;
+
+	return SPDY_YES;
+}
+
+
+int
+SPDYF_session_write (struct SPDY_Session *session,
+                     bool only_one_frame)
+{
+	unsigned int i;
+	int bytes_written;
+	struct SPDYF_Response_Queue *queue_head;
+	struct SPDYF_Response_Queue *response_queue;
+
+	if(SPDY_SESSION_STATUS_CLOSING == session->status)
+		return SPDY_NO;
+
+  if(SPDY_NO == session->fio_before_write(session))
+    return SPDY_NO;
+
+	for(i=0;
+		only_one_frame
+		? i < 1
+		: i < session->max_num_frames;
+		++i)
+	{
+		//if the buffer is not null, part of the last frame is still
+		//pending to be sent
+		if(NULL == session->write_buffer)
+		{
+			//discard frames on closed streams
+			response_queue = session->response_queue_head;
+
+			while(NULL != response_queue)
+			{
+				//if stream is closed, remove not yet sent frames
+				//associated with it
+				//GOAWAY frames are not associated to streams
+				//and still need to be sent
+				if(NULL == response_queue->stream
+					|| !response_queue->stream->is_out_closed)
+					break;
+
+				DLL_remove(session->response_queue_head,session->response_queue_tail,response_queue);
+
+				if(NULL != response_queue->frqcb)
+				{
+					response_queue->frqcb(response_queue->frqcb_cls, response_queue, SPDY_RESPONSE_RESULT_STREAM_CLOSED);
+				}
+
+				SPDYF_response_queue_destroy(response_queue);
+				response_queue = session->response_queue_head;
+			}
+
+			if(NULL == session->response_queue_head)
+				break;//nothing on the queue
+
+			//get next data from queue and put it to the write buffer
+			// to send it
+			if(SPDY_NO == session->response_queue_head->process_response_handler(session))
+			{
+				//error occured and the handler changed or not the
+				//session's status appropriately
+				if(SPDY_SESSION_STATUS_CLOSING == session->status)
+				{
+					//try to send GOAWAY first if the current frame is different
+					if(session->response_queue_head->is_data
+						|| SPDY_CONTROL_FRAME_TYPES_GOAWAY
+							!= session->response_queue_head->control_frame->type)
+					{
+						session->status = SPDY_SESSION_STATUS_FLUSHING;
+						SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_INTERNAL_ERROR, true);
+						SPDYF_session_write(session,true);
+						session->status = SPDY_SESSION_STATUS_CLOSING;
+					}
+					return SPDY_YES;
+				}
+
+				//just return from the loop to return from this function
+        ++i;
+				break;
+			}
+
+			//check if something was prepared for writing
+			//on respones with callbacks it is possible that their is no
+			//data available
+			if(0 == session->write_buffer_size)//nothing to write
+      {
+				if(response_queue != session->response_queue_head)
+				{
+					//the handler modified the queue
+					continue;
+				}
+				else
+				{
+					//no need to try the same frame again
+          ++i;
+					break;
+				}
+      }
+		}
+
+		session->last_activity = SPDYF_monotonic_time();
+
+		//actual write to the IO
+		bytes_written = session->fio_send(session,
+			session->write_buffer + session->write_buffer_beginning,
+			session->write_buffer_offset - session->write_buffer_beginning);
+
+		switch(bytes_written)
+		{
+			case SPDY_IO_ERROR_CLOSED:
+				//The TLS connection was closed by the other party, clean
+				//or not
+				shutdown (session->socket_fd, SHUT_RD);
+				session->read_closed = true;
+				session->status = SPDY_SESSION_STATUS_CLOSING;
+				return SPDY_YES;
+
+			case SPDY_IO_ERROR_ERROR:
+				//any kind of error in the TLS subsystem
+				//forbid more writing
+				session->status = SPDY_SESSION_STATUS_CLOSING;
+				return SPDY_YES;
+
+			case SPDY_IO_ERROR_AGAIN:
+				//read or write should be called again; leave it for the
+				//next time; return from the function as we do not now
+				//whether reading or writing is needed
+				return i>0 ? SPDY_YES : SPDY_NO;
+
+			//default:
+				//something was really read from the TLS subsystem
+				//just continue
+		}
+
+		session->write_buffer_beginning += bytes_written;
+
+		//check if the full buffer was written
+		if(session->write_buffer_beginning == session->write_buffer_size)
+		{
+			//that response is handled, remove it from queue
+      free(session->write_buffer);
+			session->write_buffer = NULL;
+			session->write_buffer_size = 0;
+			queue_head = session->response_queue_head;
+			if(NULL == queue_head->next)
+			{
+				session->response_queue_head = NULL;
+				session->response_queue_tail = NULL;
+			}
+			else
+			{
+				session->response_queue_head = queue_head->next;
+				session->response_queue_head->prev = NULL;
+			}
+
+			//set stream to closed if the frame's fin flag is set
+			SPDYF_stream_set_flags_on_write(queue_head);
+
+			if(NULL != queue_head->frqcb)
+			{
+				//application layer callback to notify sending of the response
+				queue_head->frqcb(queue_head->frqcb_cls, queue_head, SPDY_RESPONSE_RESULT_SUCCESS);
+			}
+
+			SPDYF_response_queue_destroy(queue_head);
+		}
+	}
+
+	if(SPDY_SESSION_STATUS_FLUSHING == session->status
+		&& NULL == session->response_queue_head)
+		session->status = SPDY_SESSION_STATUS_CLOSING;
+
+	//return i>0 ? SPDY_YES : SPDY_NO;
+	return session->fio_after_write(session, i>0 ? SPDY_YES : SPDY_NO);
+}
+
+
+int
+SPDYF_session_idle (struct SPDY_Session *session)
+{
+	size_t read_buffer_beginning;
+	size_t frame_length;
+	struct SPDYF_Control_Frame* control_frame;
+	struct SPDYF_Data_Frame *data_frame;
+
+	//prepare session for closing if timeout is used and already passed
+	if(SPDY_SESSION_STATUS_CLOSING != session->status
+		&& session->daemon->session_timeout
+		&& (session->last_activity + session->daemon->session_timeout < SPDYF_monotonic_time()))
+	{
+		session->status = SPDY_SESSION_STATUS_CLOSING;
+		//best effort for sending GOAWAY
+		SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_OK, true);
+		SPDYF_session_write(session,true);
+	}
+
+	switch(session->status)
+	{
+		//expect new frame to arrive
+		case SPDY_SESSION_STATUS_WAIT_FOR_HEADER:
+			session->current_stream_id = 0;
+			//check if the whole frame header is already here
+			//both frame types have the same length
+			if(session->read_buffer_offset - session->read_buffer_beginning
+				< sizeof(struct SPDYF_Control_Frame))
+				return SPDY_NO;
+
+			/* check the first bit to see if it is data or control frame
+			 * and also if the version is supported */
+			if(0x80 == *(uint8_t *)(session->read_buffer + session->read_buffer_beginning)
+				&& SPDY_VERSION == *((uint8_t *)session->read_buffer + session->read_buffer_beginning + 1))
+			{
+				//control frame
+				if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame))))
+				{
+					SPDYF_DEBUG("No memory");
+					return SPDY_NO;
+				}
+
+				//get frame headers
+				memcpy(control_frame,
+					session->read_buffer + session->read_buffer_beginning,
+					sizeof(struct SPDYF_Control_Frame));
+				session->read_buffer_beginning += sizeof(struct SPDYF_Control_Frame);
+				SPDYF_CONTROL_FRAME_NTOH(control_frame);
+
+				session->status = SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER;
+				//assign different frame handler according to frame type
+				switch(control_frame->type){
+					case SPDY_CONTROL_FRAME_TYPES_SYN_STREAM:
+						session->frame_handler = &spdyf_handler_read_syn_stream;
+						break;
+					case SPDY_CONTROL_FRAME_TYPES_GOAWAY:
+						session->frame_handler = &spdyf_handler_read_goaway;
+						break;
+					case SPDY_CONTROL_FRAME_TYPES_RST_STREAM:
+						session->frame_handler = &spdyf_handler_read_rst_stream;
+						break;
+					default:
+						session->frame_handler = &SPDYF_handler_ignore_frame;
+				}
+				session->frame_handler_cls = control_frame;
+				//DO NOT break the outer case
+			}
+			else if(0 == *(uint8_t *)(session->read_buffer + session->read_buffer_beginning))
+			{
+				//needed for POST
+				//data frame
+				if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame))))
+				{
+					SPDYF_DEBUG("No memory");
+					return SPDY_NO;
+				}
+
+				//get frame headers
+				memcpy(data_frame,
+					session->read_buffer + session->read_buffer_beginning,
+					sizeof(struct SPDYF_Data_Frame));
+				session->read_buffer_beginning += sizeof(struct SPDYF_Data_Frame);
+				SPDYF_DATA_FRAME_NTOH(data_frame);
+
+				session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY;
+				session->frame_handler = &spdyf_handler_read_data;
+				session->frame_handler_cls = data_frame;
+				//DO NOT brake the outer case
+			}
+			else
+			{
+				SPDYF_DEBUG("another protocol or version received!");
+
+				/* According to the draft the lib should send here
+				 * RST_STREAM with status UNSUPPORTED_VERSION. I don't
+				 * see any sense of keeping the session open since
+				 * we don't know how many bytes is the bogus "frame".
+				 * And the latter normally will be HTTP request.
+				 *
+				 */
+
+				//shutdown(session->socket_fd, SHUT_RD);
+				session->status = SPDY_SESSION_STATUS_FLUSHING;
+				SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_PROTOCOL_ERROR,false);
+				//SPDYF_session_write(session,false);
+				/* close connection since the client expects another
+				protocol from us */
+				//SPDYF_session_close(session);
+				return SPDY_YES;
+			}
+
+		//expect specific header fields after the standard header
+		case SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER:
+			if(NULL!=session->frame_handler)
+			{
+				read_buffer_beginning = session->read_buffer_beginning;
+				//if everything is ok, the "body" will also be processed
+				//by the handler
+				session->frame_handler(session);
+
+				if(SPDY_SESSION_STATUS_IGNORE_BYTES == session->status)
+				{
+					//check for larger than max supported frame
+					if(session->frame_handler != &spdyf_handler_read_data)
+					{
+						frame_length = ((struct SPDYF_Control_Frame *)session->frame_handler_cls)->length;
+					}
+					else
+					{
+						frame_length = ((struct SPDYF_Data_Frame *)session->frame_handler_cls)->length;
+					}
+
+					//if(SPDY_MAX_SUPPORTED_FRAME_SIZE < frame_length)
+					{
+						SPDYF_DEBUG("received frame with unsupported size: %zu", frame_length);
+						//the data being received must be ignored and
+						//RST_STREAM sent
+
+						//ignore bytes that will arive later
+						session->read_ignore_bytes = frame_length
+							+ read_buffer_beginning
+							- session->read_buffer_offset;
+						//ignore what is already in read buffer
+						session->read_buffer_beginning = session->read_buffer_offset;
+
+						SPDYF_prepare_rst_stream(session,
+							session->current_stream_id > 0 ? session->streams_head : NULL, //may be 0 here which is not good
+							SPDY_RST_STREAM_STATUS_FRAME_TOO_LARGE);
+
+						//actually the read buffer can be bigger than the
+						//max supported size
+						session->status = session->read_ignore_bytes
+							? SPDY_SESSION_STATUS_IGNORE_BYTES
+							: SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
+
+						free(session->frame_handler_cls);
+					}
+				}
+			}
+
+			if(SPDY_SESSION_STATUS_IGNORE_BYTES != session->status)
+			{
+				break;
+			}
+
+		//ignoring data in read buffer
+		case SPDY_SESSION_STATUS_IGNORE_BYTES:
+			SPDYF_ASSERT(session->read_ignore_bytes > 0,
+				"Session is in wrong state");
+			if(session->read_ignore_bytes
+				> session->read_buffer_offset - session->read_buffer_beginning)
+			{
+				session->read_ignore_bytes -=
+					session->read_buffer_offset - session->read_buffer_beginning;
+				session->read_buffer_beginning = session->read_buffer_offset;
+			}
+			else
+			{
+				session->read_buffer_beginning += session->read_ignore_bytes;
+				session->read_ignore_bytes = 0;
+				session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
+			}
+			break;
+
+		//expect frame body (name/value pairs)
+		case SPDY_SESSION_STATUS_WAIT_FOR_BODY:
+			if(NULL!=session->frame_handler)
+				session->frame_handler(session);
+			break;
+
+		case SPDY_SESSION_STATUS_FLUSHING:
+
+			return SPDY_NO;
+
+		//because of error the session needs to be closed
+		case SPDY_SESSION_STATUS_CLOSING:
+			//error should be already sent to the client
+			SPDYF_session_close(session);
+			return SPDY_YES;
+	}
+
+	return SPDY_YES;
+}
+
+
+void
+SPDYF_session_close (struct SPDY_Session *session)
+{
+	struct SPDY_Daemon *daemon = session->daemon;
+	int by_client = session->read_closed ? SPDY_YES : SPDY_NO;
+
+	//shutdown the tls and deinit the tls context
+	session->fio_close_session(session);
+	shutdown (session->socket_fd,
+		session->read_closed ? SHUT_WR : SHUT_RDWR);
+	session->read_closed = true;
+
+	//remove session from the list
+	DLL_remove (daemon->sessions_head,
+		daemon->sessions_tail,
+		session);
+	//add the session for the list for cleaning up
+	DLL_insert (daemon->cleanup_head,
+		daemon->cleanup_tail,
+		session);
+
+	//call callback for closed session
+	if(NULL != daemon->session_closed_cb)
+	{
+		daemon->session_closed_cb(daemon->cls, session, by_client);
+	}
+}
+
+
+int
+SPDYF_session_accept(struct SPDY_Daemon *daemon)
+{
+	int new_socket_fd;
+  int ret;
+	struct SPDY_Session *session = NULL;
+	socklen_t addr_len;
+	struct sockaddr *addr;
+
+#if HAVE_INET6
+	struct sockaddr_in6 addr6;
+
+	addr = (struct sockaddr *)&addr6;
+	addr_len = sizeof(addr6);
+#else
+	struct sockaddr_in addr4;
+
+	addr = (struct sockaddr *)&addr4;
+	addr_len = sizeof(addr6);
+#endif
+
+  new_socket_fd = accept (daemon->socket_fd, addr, &addr_len);
+
+  if(new_socket_fd < 1)
+		return SPDY_NO;
+
+	if (NULL == (session = malloc (sizeof (struct SPDY_Session))))
+  {
+		goto free_and_fail;
+	}
+	memset (session, 0, sizeof (struct SPDY_Session));
+
+	session->daemon = daemon;
+	session->socket_fd = new_socket_fd;
+  session->max_num_frames = daemon->max_num_frames;
+
+  ret = SPDYF_io_set_session(session, daemon->io_subsystem);
+  SPDYF_ASSERT(SPDY_YES == ret, "Somehow daemon->io_subsystem iswrong here");
+
+	//init TLS context, handshake will be done
+	if(SPDY_YES != session->fio_new_session(session))
+	{
+		goto free_and_fail;
+	}
+
+	//read buffer
+	session->read_buffer_size = SPDYF_BUFFER_SIZE;
+	if (NULL == (session->read_buffer = malloc (session->read_buffer_size)))
+    {
+		session->fio_close_session(session);
+		goto free_and_fail;
+	}
+
+	//address of the client
+	if (NULL == (session->addr = malloc (addr_len)))
+    {
+		session->fio_close_session(session);
+		goto free_and_fail;
+	}
+	memcpy (session->addr, addr, addr_len);
+
+	session->addr_len = addr_len;
+	session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
+
+	//init zlib context for the whole session
+	if(SPDY_YES != SPDYF_zlib_deflate_init(&session->zlib_send_stream))
+    {
+		session->fio_close_session(session);
+		goto free_and_fail;
+	}
+	if(SPDY_YES != SPDYF_zlib_inflate_init(&session->zlib_recv_stream))
+    {
+		session->fio_close_session(session);
+		SPDYF_zlib_deflate_end(&session->zlib_send_stream);
+		goto free_and_fail;
+	}
+
+	//add it to daemon's list
+	DLL_insert(daemon->sessions_head,daemon->sessions_tail,session);
+
+	session->last_activity = SPDYF_monotonic_time();
+
+	if(NULL != daemon->new_session_cb)
+		daemon->new_session_cb(daemon->cls, session);
+
+	return SPDY_YES;
+
+	//for GOTO
+	free_and_fail:
+	/* something failed, so shutdown, close and free memory */
+	shutdown (new_socket_fd, SHUT_RDWR);
+	(void)close (new_socket_fd);
+
+	if(NULL != session)
+	{
+		if(NULL != session->addr)
+			free (session->addr);
+		if(NULL != session->read_buffer)
+			free (session->read_buffer);
+		free (session);
+	}
+	return SPDY_NO;
+}
+
+
+void
+SPDYF_queue_response (struct SPDYF_Response_Queue *response_to_queue,
+						struct SPDY_Session *session,
+						int consider_priority)
+{
+	struct SPDYF_Response_Queue *pos;
+	struct SPDYF_Response_Queue *last;
+	uint8_t priority;
+
+	SPDYF_ASSERT(SPDY_YES != consider_priority || NULL != response_to_queue->stream,
+		"called with consider_priority but no stream provided");
+
+	last = response_to_queue;
+	while(NULL != last->next)
+	{
+		last = last->next;
+	}
+
+	if(SPDY_NO == consider_priority)
+	{
+		//put it at the end of the queue
+		response_to_queue->prev = session->response_queue_tail;
+		if (NULL == session->response_queue_head)
+			session->response_queue_head = response_to_queue;
+		else
+			session->response_queue_tail->next = response_to_queue;
+		session->response_queue_tail = last;
+		return;
+	}
+	else if(-1 == consider_priority)
+	{
+		//put it at the head of the queue
+		last->next = session->response_queue_head;
+		if (NULL == session->response_queue_tail)
+			session->response_queue_tail = last;
+		else
+			session->response_queue_head->prev = response_to_queue;
+		session->response_queue_head = response_to_queue;
+		return;
+	}
+
+	if(NULL == session->response_queue_tail)
+	{
+		session->response_queue_head = response_to_queue;
+		session->response_queue_tail = last;
+		return;
+	}
+
+	//search for the right position to put it
+	pos = session->response_queue_tail;
+	priority = response_to_queue->stream->priority;
+	while(NULL != pos
+		&& pos->stream->priority > priority)
+	{
+		pos = pos->prev;
+	}
+
+	if(NULL == pos)
+	{
+		//put it on the head
+		session->response_queue_head->prev = last;
+		last->next = session->response_queue_head;
+		session->response_queue_head = response_to_queue;
+	}
+	else if(NULL == pos->next)
+	{
+		//put it at the end
+		response_to_queue->prev = pos;
+		pos->next = response_to_queue;
+		session->response_queue_tail = last;
+	}
+	else
+	{
+		response_to_queue->prev = pos;
+		last->next = pos->next;
+		pos->next = response_to_queue;
+		last->next->prev = last;
+	}
+}
+
+
+void
+SPDYF_session_destroy(struct SPDY_Session *session)
+{
+	struct SPDYF_Stream *stream;
+	struct SPDYF_Response_Queue *response_queue;
+
+	(void)close (session->socket_fd);
+	SPDYF_zlib_deflate_end(&session->zlib_send_stream);
+	SPDYF_zlib_inflate_end(&session->zlib_recv_stream);
+
+	//clean up unsent data in the output queue
+	while (NULL != (response_queue = session->response_queue_head))
+	{
+		DLL_remove (session->response_queue_head,
+			session->response_queue_tail,
+			response_queue);
+
+		if(NULL != response_queue->frqcb)
+		{
+			response_queue->frqcb(response_queue->frqcb_cls, response_queue, SPDY_RESPONSE_RESULT_SESSION_CLOSED);
+		}
+
+		SPDYF_response_queue_destroy(response_queue);
+	}
+
+	//clean up the streams belonging to this session
+	while (NULL != (stream = session->streams_head))
+	{
+		DLL_remove (session->streams_head,
+			session->streams_tail,
+			stream);
+
+		SPDYF_stream_destroy(stream);
+	}
+
+	free(session->addr);
+	free(session->read_buffer);
+	free(session->write_buffer);
+	free(session);
+}
+
+
+int
+SPDYF_prepare_goaway (struct SPDY_Session *session,
+					enum SPDY_GOAWAY_STATUS status,
+					bool in_front)
+{
+	struct SPDYF_Response_Queue *response_to_queue;
+	struct SPDYF_Control_Frame *control_frame;
+	uint32_t *data;
+
+	if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
+	{
+		return SPDY_NO;
+	}
+	memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));
+
+	if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame))))
+	{
+		free(response_to_queue);
+		return SPDY_NO;
+	}
+	memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame));
+
+	if(NULL == (data = malloc(4)))
+	{
+		free(control_frame);
+		free(response_to_queue);
+		return SPDY_NO;
+	}
+	*(data) = htonl(status);
+
+	control_frame->control_bit = 1;
+	control_frame->version = SPDY_VERSION;
+	control_frame->type = SPDY_CONTROL_FRAME_TYPES_GOAWAY;
+	control_frame->flags = 0;
+
+	response_to_queue->control_frame = control_frame;
+	response_to_queue->process_response_handler = &SPDYF_handler_write_goaway;
+	response_to_queue->data = data;
+	response_to_queue->data_size = 4;
+
+	SPDYF_queue_response (response_to_queue,
+						session,
+						in_front ? -1 : SPDY_NO);
+
+	return SPDY_YES;
+}
+
+
+int
+SPDYF_prepare_rst_stream (struct SPDY_Session *session,
+					struct SPDYF_Stream * stream,
+					enum SPDY_RST_STREAM_STATUS status)
+{
+	struct SPDYF_Response_Queue *response_to_queue;
+	struct SPDYF_Control_Frame *control_frame;
+	uint32_t *data;
+	uint32_t stream_id;
+
+  if(NULL == stream)
+    stream_id = 0;
+  else
+    stream_id = stream->stream_id;
+
+	if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
+	{
+		return SPDY_NO;
+	}
+	memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));
+
+	if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame))))
+	{
+		free(response_to_queue);
+		return SPDY_NO;
+	}
+	memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame));
+
+	if(NULL == (data = malloc(8)))
+	{
+		free(control_frame);
+		free(response_to_queue);
+		return SPDY_NO;
+	}
+	*(data) = HTON31(stream_id);
+	*(data + 1) = htonl(status);
+
+	control_frame->control_bit = 1;
+	control_frame->version = SPDY_VERSION;
+	control_frame->type = SPDY_CONTROL_FRAME_TYPES_RST_STREAM;
+	control_frame->flags = 0;
+
+	response_to_queue->control_frame = control_frame;
+	response_to_queue->process_response_handler = &SPDYF_handler_write_rst_stream;
+	response_to_queue->data = data;
+	response_to_queue->data_size = 8;
+	response_to_queue->stream = stream;
+
+	SPDYF_queue_response (response_to_queue,
+						session,
+						-1);
+
+	return SPDY_YES;
+}
+
+
+int
+SPDYF_prepare_window_update (struct SPDY_Session *session,
+					struct SPDYF_Stream * stream,
+					int32_t delta_window_size)
+{
+	struct SPDYF_Response_Queue *response_to_queue;
+	struct SPDYF_Control_Frame *control_frame;
+	uint32_t *data;
+
+  SPDYF_ASSERT(NULL != stream, "stream cannot be NULL");
+
+	if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
+	{
+		return SPDY_NO;
+	}
+	memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));
+
+	if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame))))
+	{
+		free(response_to_queue);
+		return SPDY_NO;
+	}
+	memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame));
+
+	if(NULL == (data = malloc(8)))
+	{
+		free(control_frame);
+		free(response_to_queue);
+		return SPDY_NO;
+	}
+	*(data) = HTON31(stream->stream_id);
+	*(data + 1) = HTON31(delta_window_size);
+
+	control_frame->control_bit = 1;
+	control_frame->version = SPDY_VERSION;
+	control_frame->type = SPDY_CONTROL_FRAME_TYPES_WINDOW_UPDATE;
+	control_frame->flags = 0;
+
+	response_to_queue->control_frame = control_frame;
+	response_to_queue->process_response_handler = &SPDYF_handler_write_window_update;
+	response_to_queue->data = data;
+	response_to_queue->data_size = 8;
+	response_to_queue->stream = stream;
+
+	SPDYF_queue_response (response_to_queue,
+						session,
+						-1);
+
+	return SPDY_YES;
+}
diff --git a/src/microspdy/session.h b/src/microspdy/session.h
new file mode 100644
index 0000000..29ab550
--- /dev/null
+++ b/src/microspdy/session.h
@@ -0,0 +1,281 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file session.h
+ * @brief  TCP connection/SPDY session handling
+ * @author Andrey Uzunov
+ */
+
+#ifndef SESSION_H
+#define SESSION_H
+
+#include "platform.h"
+#include "structures.h"
+
+/**
+ * Called by the daemon when the socket for the session has available
+ * data to be read. Reads data from the TLS socket and puts it to the
+ * session's read buffer. The latte
+ *
+ * @param session SPDY_Session for which data will be read.
+ * @return SPDY_YES if something was read or session's status was
+ *         changed. It is possible that error occurred but was handled
+ *         and the status was therefore changed.
+ *         SPDY_NO if nothing happened, e.g. the subsystem wants read/
+ *         write to be called again.
+ */
+int
+SPDYF_session_read (struct SPDY_Session *session);
+
+
+/**
+ * Called by the daemon when the socket for the session is ready for some
+ * data to be written to it. For one or more objects on the response
+ * queue tries to fill in the write buffer, based on the frame on the
+ * queue, and to write data to the TLS socket. 
+ *
+ * @param session SPDY_Session for which data will be written.
+ * @param only_one_frame when true, the function will write at most one
+ *        SPDY frame to the underlying IO subsystem;
+ *        when false, the function will write up to
+ *        session->max_num_frames SPDY frames
+ * @return SPDY_YES if the session's internal writing state has changed:
+ *         something was written and/or session's status was
+ *         changed and/or response callback was called but did not provide
+ *         data. It is possible that error occurred but was handled
+ *         and the status was therefore changed.
+ *         SPDY_NO if nothing happened. However, it is possible that some
+ *         frames were discarded within the call, e.g. frames belonging
+ *         to a closed stream.
+ */
+int
+SPDYF_session_write (struct SPDY_Session *session,
+                     bool only_one_frame);
+
+
+/**
+ * Called by the daemon on SPDY_run to handle the data in the read and write
+ * buffer of a session. Based on the state and the content of the read
+ * buffer new frames are received and interpreted, appropriate user
+ * callbacks are called and maybe something is put on the response queue
+ * ready to be handled by session_write.
+ * 
+ * @param session SPDY_Session which will be handled.
+ * @return SPDY_YES if something from the read buffers was processed,
+ *         session's status was changed and/or the session was closed.
+ *         SPDY_NO if nothing happened, e.g. the session is in a state,
+ *         not allowing processing read buffers.
+ */
+int
+SPDYF_session_idle (struct SPDY_Session *session);
+
+
+/**
+ * This function shutdowns the socket, moves the session structure to
+ * daemon's queue for sessions to be cleaned up.
+ * 
+ * @param session SPDY_Session which will be handled.
+ */
+void
+SPDYF_session_close (struct SPDY_Session *session);
+
+
+/**
+ * Called to accept new TCP connection and create SPDY session.
+ * 
+ * @param daemon SPDY_Daemon whose listening socket is used.
+ * @return SPDY_NO on any kind of error while accepting new TCP connection
+ * 			and initializing new SPDY_Session.
+ *         SPDY_YES otherwise.
+ */
+int
+SPDYF_session_accept(struct SPDY_Daemon *daemon);
+
+
+/**
+ * Puts SPDYF_Response_Queue object on the queue to be sent to the
+ * client later.
+ *
+ * @param response_to_queue linked list of objects containing SPDY
+ * 			frame and data to be added to the queue
+ * @param session SPDY session for which the response is sent
+ * @param consider_priority if SPDY_NO, the list will be added to the
+ * 			end of the queue.
+ * 			If SPDY_YES, the response will be added after
+ * 			the last previously added response with priority of the
+ * 			request grater or equal to that of the current one.
+ * 			If -1, the object will be put at the head of the queue.
+ */
+void
+SPDYF_queue_response (struct SPDYF_Response_Queue *response_to_queue,
+						struct SPDY_Session *session,
+						int consider_priority);
+
+
+/**
+ * Cleans up the TSL context for the session, closes the TCP connection,
+ * cleans up any data pointed by members of the session structure
+ * (buffers, queue of responses, etc.) and frees the memory allocated by
+ * the session itself.
+ */						
+void
+SPDYF_session_destroy(struct SPDY_Session *session);
+
+
+/**
+ * Prepares GOAWAY frame to tell the client to stop creating new streams.
+ * The session should be closed soon after this call.
+ * 
+ * @param session SPDY session
+ * @param status code for the GOAWAY frame
+ * @param in_front whether or not to put the frame in front of everything
+ * 			on the response queue
+ * @return SPDY_NO on error (not enough memory) or
+ * 			SPDY_YES on success
+ */
+int
+SPDYF_prepare_goaway (struct SPDY_Session *session,
+					enum SPDY_GOAWAY_STATUS status,
+					bool in_front);
+
+
+/**
+ * Prepares RST_STREAM frame to terminate a stream. This frame may or
+ * not indicate an error. The frame will be put at the head of the queue.
+ * This means that frames for this stream which are still in the queue
+ * will be discarded soon.
+ * 
+ * @param session SPDY session
+ * @param stream stream to terminate
+ * @param status code for the RST_STREAM frame
+ * @return SPDY_NO on memory error or
+ * 			SPDY_YES on success
+ */
+int
+SPDYF_prepare_rst_stream (struct SPDY_Session *session,
+					struct SPDYF_Stream * stream,
+					enum SPDY_RST_STREAM_STATUS status);
+
+
+/**
+ * Prepares WINDOW_UPDATE frame to tell the other party that more
+ * data can be sent on the stream. The frame will be put at the head of
+ * the queue.
+ * 
+ * @param session SPDY session
+ * @param stream stream to which the changed window will apply
+ * @param delta_window_size how much the window grows
+ * @return SPDY_NO on memory error or
+ * 			SPDY_YES on success
+ */
+int
+SPDYF_prepare_window_update (struct SPDY_Session *session,
+					struct SPDYF_Stream * stream,
+					int32_t delta_window_size);
+          
+
+/**
+ * Handler called by session_write to fill the write buffer according to
+ * the data frame waiting in the response queue.
+ * When response data is given by user callback, the lib does not know
+ * how many frames are needed. In such case this call produces
+ * another ResponseQueue object and puts it on the queue while the the
+ * user callback says that there will be more data.
+ * 
+ * @return SPDY_NO on error (not enough memory or the user calback for
+ *         providing response data did something wrong). If
+ *         the error is unrecoverable the handler changes session's
+ *         status.
+ *         SPDY_YES on success
+ */	
+int
+SPDYF_handler_write_data (struct SPDY_Session *session);
+
+
+/**
+ * Handler called by session_write to fill the write buffer based on the
+ * control frame (SYN_REPLY) waiting in the response queue.
+ * 
+ * @param session SPDY session
+ * @return SPDY_NO on error (zlib state is broken; the session MUST be
+ *         closed). If
+ *         the error is unrecoverable the handler changes session's
+ *         status.
+ * 			SPDY_YES on success
+ */ 			
+int
+SPDYF_handler_write_syn_reply (struct SPDY_Session *session);
+
+
+/**
+ * Handler called by session_write to fill the write buffer based on the
+ * control frame (GOAWAY) waiting in the response queue.
+ * 
+ * @param session SPDY session
+ * @return SPDY_NO on error (not enough memory; by specification the
+ *         session must be closed
+ *         soon, thus there is no need to handle the error) or
+ * 			SPDY_YES on success
+ */					
+int
+SPDYF_handler_write_goaway (struct SPDY_Session *session);
+
+
+/**
+ * Handler called by session_write to fill the write buffer based on the
+ * control frame (RST_STREAM) waiting in the response queue.
+ * 
+ * @param session SPDY session
+ * @return SPDY_NO on error (not enough memory). If
+ *         the error is unrecoverable the handler changes session's
+ *         status.
+ * 			SPDY_YES on success
+ */				
+int
+SPDYF_handler_write_rst_stream (struct SPDY_Session *session);
+
+
+/**
+ * Handler called by session_write to fill the write buffer based on the
+ * control frame (WINDOW_UPDATE) waiting in the response queue.
+ * 
+ * @param session SPDY session
+ * @return SPDY_NO on error (not enough memory). If
+ *         the error is unrecoverable the handler changes session's
+ *         status.
+ * 			SPDY_YES on success
+ */			
+int
+SPDYF_handler_write_window_update (struct SPDY_Session *session);
+
+
+/**
+ * Carefully ignore the full size of frames which are not yet supported
+ * by the lib.
+ * TODO Ignoring frames containing compressed bodies means that the
+ * compress state will be corrupted on next received frame. According to
+ * the draft the lib SHOULD try to decompress data also in corrupted
+ * frames just to keep right compression state.
+ * 
+ * @param session SPDY_Session whose read buffer is used.
+ */
+void
+SPDYF_handler_ignore_frame (struct SPDY_Session *session);
+
+#endif
diff --git a/src/microspdy/stream.c b/src/microspdy/stream.c
new file mode 100644
index 0000000..9b6dc08
--- /dev/null
+++ b/src/microspdy/stream.c
@@ -0,0 +1,169 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file stream.c
+ * @brief  SPDY streams handling
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include "structures.h"
+#include "internal.h"
+#include "session.h"
+
+
+int
+SPDYF_stream_new (struct SPDY_Session *session)
+{
+	uint32_t stream_id;
+	uint32_t assoc_stream_id;
+	uint8_t priority;
+	uint8_t slot;
+	size_t buffer_pos = session->read_buffer_beginning;
+	struct SPDYF_Stream *stream;
+	struct SPDYF_Control_Frame *frame;
+	
+	if((session->read_buffer_offset - session->read_buffer_beginning) < 10)
+	{
+		//not all fields are received to create new stream
+		return SPDY_NO;
+	}
+	
+	frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls;
+	
+	//get stream id of the new stream
+    memcpy(&stream_id, session->read_buffer + session->read_buffer_beginning, 4);
+	stream_id = NTOH31(stream_id);
+	session->read_buffer_beginning += 4;
+	if(stream_id <= session->last_in_stream_id || 0==(stream_id % 2))
+	{
+		//wrong stream id sent by client
+		//GOAWAY with PROTOCOL_ERROR MUST be sent
+		//TODO
+		
+		//ignore frame
+		session->frame_handler = &SPDYF_handler_ignore_frame;
+		return SPDY_NO;
+	}
+	else if(session->is_goaway_sent)
+	{
+		//the client is not allowed to create new streams anymore
+		//we MUST ignore the frame
+		session->frame_handler = &SPDYF_handler_ignore_frame;
+		return SPDY_NO;
+	}
+	
+	//set highest stream id for session
+	session->last_in_stream_id = stream_id;
+	
+	//get assoc stream id of the new stream
+	//this value is used with SPDY PUSH, thus nothing to do with it here
+    memcpy(&assoc_stream_id, session->read_buffer + session->read_buffer_beginning, 4);
+	assoc_stream_id = NTOH31(assoc_stream_id);
+	session->read_buffer_beginning += 4;
+
+	//get stream priority (3 bits)
+	//after it there are 5 bits that are not used
+	priority = *(uint8_t *)(session->read_buffer + session->read_buffer_beginning) >> 5;
+	session->read_buffer_beginning++;
+	
+	//get slot (see SPDY draft)
+	slot = *(uint8_t *)(session->read_buffer + session->read_buffer_beginning);
+	session->read_buffer_beginning++;
+	
+	if(NULL == (stream = malloc(sizeof(struct SPDYF_Stream))))
+	{
+		SPDYF_DEBUG("No memory");
+		//revert buffer state
+		session->read_buffer_beginning = buffer_pos;
+		return SPDY_NO;
+	}
+	memset(stream,0, sizeof(struct SPDYF_Stream));
+	stream->session = session;
+	stream->stream_id = stream_id;
+	stream->assoc_stream_id = assoc_stream_id;
+	stream->priority = priority;
+	stream->slot = slot;
+	stream->is_in_closed = (frame->flags & SPDY_SYN_STREAM_FLAG_FIN) != 0;
+	stream->flag_unidirectional = (frame->flags & SPDY_SYN_STREAM_FLAG_UNIDIRECTIONAL) != 0;
+	stream->is_out_closed = stream->flag_unidirectional;
+	stream->is_server_initiator = false;
+	stream->window_size = SPDYF_INITIAL_WINDOW_SIZE;
+	
+	//put the stream to the list of streams for the session
+	DLL_insert(session->streams_head, session->streams_tail, stream);
+	
+	return SPDY_YES;
+}
+
+
+void
+SPDYF_stream_destroy(struct SPDYF_Stream *stream)
+{
+	SPDY_name_value_destroy(stream->headers);
+	free(stream);
+	stream = NULL;
+}
+
+
+void
+SPDYF_stream_set_flags_on_write(struct SPDYF_Response_Queue *response_queue)
+{
+	struct SPDYF_Stream * stream = response_queue->stream;
+	
+	if(NULL != response_queue->data_frame)
+	{
+		stream->is_out_closed = (bool)(response_queue->data_frame->flags & SPDY_DATA_FLAG_FIN);
+	}
+	else if(NULL != response_queue->control_frame)
+	{
+		switch(response_queue->control_frame->type)
+		{
+			case SPDY_CONTROL_FRAME_TYPES_SYN_REPLY:
+				stream->is_out_closed = (bool)(response_queue->control_frame->flags & SPDY_SYN_REPLY_FLAG_FIN);
+				break;
+				
+			case SPDY_CONTROL_FRAME_TYPES_RST_STREAM:
+				if(NULL != stream)
+				{
+					stream->is_out_closed = true;
+					stream->is_in_closed = true;
+				}
+				break;
+				
+		}
+	}
+}
+
+
+//TODO add function *on_read
+
+
+struct SPDYF_Stream * 
+SPDYF_stream_find(uint32_t stream_id, struct SPDY_Session * session)
+{
+  struct SPDYF_Stream * stream = session->streams_head;
+  
+  while(NULL != stream && stream_id != stream->stream_id)
+  {
+    stream = stream->next;
+  }
+  
+  return stream;
+}
diff --git a/src/microspdy/stream.h b/src/microspdy/stream.h
new file mode 100644
index 0000000..220231f
--- /dev/null
+++ b/src/microspdy/stream.h
@@ -0,0 +1,76 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file stream.h
+ * @brief  SPDY streams handling
+ * @author Andrey Uzunov
+ */
+
+#ifndef STREAM_H
+#define STREAM_H
+
+#include "platform.h"
+
+
+/**
+ * Reads data from session's read buffer and tries to create a new SPDY
+ * stream. This function is called after control frame's header has been
+ * read from the buffer (after the length field). If bogus frame is
+ * received the function changes the read handler of the session and
+ * fails, i.e. there is no need of further error handling by the caller.
+ *
+ * @param session SPDY_Session whose read buffer is being read
+ * @return SPDY_YES if a new SPDY stream request was correctly received
+ * 			and handled. SPDY_NO if the whole SPDY frame was not yet
+ * 			received or memory error occurred.
+ */
+int
+SPDYF_stream_new (struct SPDY_Session *session);
+
+
+/**
+ * Destroys stream structure and whatever is in it.
+ *
+ * @param stream SPDY_Stream to destroy
+ */
+void
+SPDYF_stream_destroy(struct SPDYF_Stream *stream);
+
+
+/**
+ * Set stream flags if needed based on the type of the frame that was
+ * just sent (e.g., close stream if it was RST_STREAM).
+ *
+ * @param response_queue sent for this stream
+ */
+void
+SPDYF_stream_set_flags_on_write(struct SPDYF_Response_Queue *response_queue);
+
+
+/**
+ * Find and return a session's stream, based on stream's ID.
+ *
+ * @param stream_id to search for
+ * @param session whose streams are considered
+ * @return SPDY_Stream with the desired ID. Can be NULL.
+ */
+struct SPDYF_Stream * 
+SPDYF_stream_find(uint32_t stream_id, struct SPDY_Session * session);
+
+#endif
diff --git a/src/microspdy/structures.c b/src/microspdy/structures.c
new file mode 100644
index 0000000..f00806b
--- /dev/null
+++ b/src/microspdy/structures.c
@@ -0,0 +1,638 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file structures.c
+ * @brief  Functions for handling most of the structures in defined
+ * 			in structures.h
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include "structures.h"
+#include "internal.h"
+#include "session.h"
+//TODO not for here?
+#include <ctype.h>
+
+
+int
+SPDYF_name_value_is_empty(struct SPDY_NameValue *container)
+{
+  SPDYF_ASSERT(NULL != container, "NULL is not an empty container!");
+  return (NULL == container->name && NULL == container->value) ? SPDY_YES : SPDY_NO;
+}
+
+struct SPDY_NameValue *
+SPDY_name_value_create ()
+{
+	struct SPDY_NameValue *pair;
+
+	if(NULL == (pair = malloc(sizeof(struct SPDY_NameValue))))
+		return NULL;
+
+	memset (pair, 0, sizeof (struct SPDY_NameValue));
+
+	return pair;
+}
+
+
+int
+SPDY_name_value_add (struct SPDY_NameValue *container,
+					const char *name,
+					const char *value)
+{
+	unsigned int i;
+	unsigned int len;
+	struct SPDY_NameValue *pair;
+	struct SPDY_NameValue *temp;
+	char **temp_value;
+	char *temp_string;
+
+	if(NULL == container || NULL == name || NULL == value || 0 == (len = strlen(name)))
+		return SPDY_INPUT_ERROR;
+  //TODO there is old code handling value==NULL
+  //update it to handle strlen(value)==0
+
+	for(i=0; i<len; ++i)
+	{
+	  if(isupper((int) name[i]))
+			return SPDY_INPUT_ERROR;
+	}
+
+	if(SPDYF_name_value_is_empty(container))
+	{
+		//container is empty/just created
+		if (NULL == (container->name = strdup (name)))
+		{
+			return SPDY_NO;
+		}
+		if (NULL == (container->value = malloc(sizeof(char *))))
+		{
+			free(container->name);
+			return SPDY_NO;
+		}
+    /*if(NULL == value)
+      container->value[0] = NULL;
+		else */if (NULL == (container->value[0] = strdup (value)))
+		{
+			free(container->value);
+			free(container->name);
+			return SPDY_NO;
+		}
+		container->num_values = 1;
+		return SPDY_YES;
+	}
+
+	pair = container;
+	while(NULL != pair)
+	{
+		if(0 == strcmp(pair->name, name))
+		{
+			//the value will be added to this pair
+			break;
+		}
+		pair = pair->next;
+	}
+
+	if(NULL == pair)
+	{
+		//the name doesn't exist in container, add new pair
+		if(NULL == (pair = malloc(sizeof(struct SPDY_NameValue))))
+			return SPDY_NO;
+
+		memset(pair, 0, sizeof(struct SPDY_NameValue));
+
+		if (NULL == (pair->name = strdup (name)))
+		{
+			free(pair);
+			return SPDY_NO;
+		}
+		if (NULL == (pair->value = malloc(sizeof(char *))))
+		{
+			free(pair->name);
+			free(pair);
+			return SPDY_NO;
+		}
+    /*if(NULL == value)
+      pair->value[0] = NULL;
+		else */if (NULL == (pair->value[0] = strdup (value)))
+		{
+			free(pair->value);
+			free(pair->name);
+			free(pair);
+			return SPDY_NO;
+		}
+		pair->num_values = 1;
+
+		temp = container;
+		while(NULL != temp->next)
+			temp = temp->next;
+		temp->next = pair;
+		pair->prev = temp;
+
+		return SPDY_YES;
+	}
+
+	//check for duplication (case sensitive)
+	for(i=0; i<pair->num_values; ++i)
+		if(0 == strcmp(pair->value[i], value))
+			return SPDY_NO;
+
+	if(strlen(pair->value[0]) > 0)
+	{
+		//the value will be appended to the others for this name
+		if (NULL == (temp_value = malloc((pair->num_values + 1) * sizeof(char *))))
+		{
+			return SPDY_NO;
+		}
+		memcpy(temp_value, pair->value, pair->num_values * sizeof(char *));
+		if (NULL == (temp_value[pair->num_values] = strdup (value)))
+		{
+			free(temp_value);
+			return SPDY_NO;
+		}
+		free(pair->value);
+		pair->value = temp_value;
+		++pair->num_values;
+		return SPDY_YES;
+	}
+
+	//just replace the empty value
+
+	if (NULL == (temp_string = strdup (value)))
+	{
+		return SPDY_NO;
+	}
+	free(pair->value[0]);
+	pair->value[0] = temp_string;
+
+	return SPDY_YES;
+}
+
+
+const char * const *
+SPDY_name_value_lookup (struct SPDY_NameValue *container,
+						const char *name,
+						int *num_values)
+{
+	struct SPDY_NameValue *temp = container;
+
+	if(NULL == container || NULL == name || NULL == num_values)
+		return NULL;
+	if(SPDYF_name_value_is_empty(container))
+		return NULL;
+
+	do
+	{
+		if(strcmp(name, temp->name) == 0)
+		{
+			*num_values = temp->num_values;
+			return (const char * const *)temp->value;
+		}
+
+		temp = temp->next;
+	}
+	while(NULL != temp);
+
+	return NULL;
+}
+
+
+void
+SPDY_name_value_destroy (struct SPDY_NameValue *container)
+{
+	unsigned int i;
+	struct SPDY_NameValue *temp = container;
+
+	while(NULL != temp)
+	{
+		container = container->next;
+		free(temp->name);
+		for(i=0; i<temp->num_values; ++i)
+			free(temp->value[i]);
+		free(temp->value);
+		free(temp);
+		temp=container;
+	}
+}
+
+
+int
+SPDY_name_value_iterate (struct SPDY_NameValue *container,
+                           SPDY_NameValueIterator iterator,
+                           void *iterator_cls)
+{
+	int count;
+	int ret;
+	struct SPDY_NameValue *temp = container;
+
+	if(NULL == container)
+		return SPDY_INPUT_ERROR;
+
+	//check if container is an empty struct
+	if(SPDYF_name_value_is_empty(container))
+		return 0;
+
+	count = 0;
+
+	if(NULL == iterator)
+	{
+		do
+		{
+			++count;
+			temp=temp->next;
+		}
+		while(NULL != temp);
+
+		return count;
+	}
+
+	//code duplication for avoiding if here
+	do
+	{
+		++count;
+		ret = iterator(iterator_cls, temp->name, (const char * const *)temp->value, temp->num_values);
+		temp=temp->next;
+	}
+	while(NULL != temp && SPDY_YES == ret);
+
+	return count;
+}
+
+void
+SPDY_destroy_response(struct SPDY_Response *response)
+{
+  if(NULL == response)
+    return;
+	free(response->data);
+	free(response->headers);
+	free(response);
+}
+
+
+struct SPDYF_Response_Queue *
+SPDYF_response_queue_create(bool is_data,
+						void *data,
+						size_t data_size,
+						struct SPDY_Response *response,
+						struct SPDYF_Stream *stream,
+						bool closestream,
+						SPDYF_ResponseQueueResultCallback frqcb,
+						void *frqcb_cls,
+						SPDY_ResponseResultCallback rrcb,
+						void *rrcb_cls)
+{
+	struct SPDYF_Response_Queue *head = NULL;
+	struct SPDYF_Response_Queue *prev;
+	struct SPDYF_Response_Queue *response_to_queue;
+	struct SPDYF_Control_Frame *control_frame;
+	struct SPDYF_Data_Frame *data_frame;
+	unsigned int i;
+	bool is_last;
+
+	SPDYF_ASSERT((! is_data)
+		     || ((0 == data_size) && (NULL != response->rcb))
+		     || ((0 < data_size) && (NULL == response->rcb)),
+		     "either data or request->rcb must not be null");
+
+	if (is_data && (data_size > SPDY_MAX_SUPPORTED_FRAME_SIZE))
+	{
+		//separate the data in more frames and add them to the queue
+
+		prev=NULL;
+		for(i = 0; i < data_size; i += SPDY_MAX_SUPPORTED_FRAME_SIZE)
+		{
+			is_last = (i + SPDY_MAX_SUPPORTED_FRAME_SIZE) >= data_size;
+
+			if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
+				goto free_and_fail;
+
+			memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));
+			if(0 == i)
+				head = response_to_queue;
+
+			if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame))))
+			{
+				free(response_to_queue);
+				goto free_and_fail;
+			}
+			memset(data_frame, 0, sizeof(struct SPDYF_Data_Frame));
+			data_frame->control_bit = 0;
+			data_frame->stream_id = stream->stream_id;
+			if(is_last && closestream)
+				data_frame->flags |= SPDY_DATA_FLAG_FIN;
+
+			response_to_queue->data_frame = data_frame;
+			response_to_queue->process_response_handler = &SPDYF_handler_write_data;
+			response_to_queue->is_data = is_data;
+			response_to_queue->stream = stream;
+			if(is_last)
+			{
+				response_to_queue->frqcb = frqcb;
+				response_to_queue->frqcb_cls = frqcb_cls;
+				response_to_queue->rrcb = rrcb;
+				response_to_queue->rrcb_cls = rrcb_cls;
+			}
+			response_to_queue->data = data + i;
+			response_to_queue->data_size = is_last
+				? (data_size - 1) % SPDY_MAX_SUPPORTED_FRAME_SIZE + 1
+				: SPDY_MAX_SUPPORTED_FRAME_SIZE;
+			response_to_queue->response = response;
+
+			response_to_queue->prev = prev;
+			if(NULL != prev)
+				prev->next = response_to_queue;
+			prev = response_to_queue;
+		}
+
+		return head;
+
+		//for GOTO
+		free_and_fail:
+		while(NULL != head)
+		{
+			response_to_queue = head;
+			head = head->next;
+			free(response_to_queue->data_frame);
+			free(response_to_queue);
+		}
+		return NULL;
+	}
+
+	//create only one frame for data, data with callback or control frame
+
+	if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue))))
+	{
+		return NULL;
+	}
+	memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue));
+
+	if(is_data)
+	{
+		if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame))))
+		{
+			free(response_to_queue);
+			return NULL;
+		}
+		memset(data_frame, 0, sizeof(struct SPDYF_Data_Frame));
+		data_frame->control_bit = 0;
+		data_frame->stream_id = stream->stream_id;
+		if(closestream && NULL == response->rcb)
+			data_frame->flags |= SPDY_DATA_FLAG_FIN;
+
+		response_to_queue->data_frame = data_frame;
+		response_to_queue->process_response_handler = &SPDYF_handler_write_data;
+	}
+	else
+	{
+		if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame))))
+		{
+			free(response_to_queue);
+			return NULL;
+		}
+		memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame));
+		control_frame->control_bit = 1;
+		control_frame->version = SPDY_VERSION;
+		control_frame->type = SPDY_CONTROL_FRAME_TYPES_SYN_REPLY;
+		if(closestream)
+			control_frame->flags |= SPDY_SYN_REPLY_FLAG_FIN;
+
+		response_to_queue->control_frame = control_frame;
+		response_to_queue->process_response_handler = &SPDYF_handler_write_syn_reply;
+	}
+
+	response_to_queue->is_data = is_data;
+	response_to_queue->stream = stream;
+	response_to_queue->frqcb = frqcb;
+	response_to_queue->frqcb_cls = frqcb_cls;
+	response_to_queue->rrcb = rrcb;
+	response_to_queue->rrcb_cls = rrcb_cls;
+	response_to_queue->data = data;
+	response_to_queue->data_size = data_size;
+	response_to_queue->response = response;
+
+	return response_to_queue;
+}
+
+
+void
+SPDYF_response_queue_destroy(struct SPDYF_Response_Queue *response_queue)
+{
+	//data is not copied to the struct but only linked
+	//but this is not valid for GOAWAY and RST_STREAM
+	if(!response_queue->is_data
+		&& (SPDY_CONTROL_FRAME_TYPES_RST_STREAM == response_queue->control_frame->type
+		|| SPDY_CONTROL_FRAME_TYPES_GOAWAY == response_queue->control_frame->type))
+	{
+		free(response_queue->data);
+	}
+	if(response_queue->is_data)
+		free(response_queue->data_frame);
+	else
+		free(response_queue->control_frame);
+
+	free(response_queue);
+}
+
+
+/* Needed by testcase to be extern -- should this be
+   in the header? */
+_MHD_EXTERN ssize_t
+SPDYF_name_value_to_stream(struct SPDY_NameValue * container[],
+                           int num_containers,
+                           void **stream)
+{
+	size_t size;
+	int32_t num_pairs = 0;
+	int32_t value_size;
+	int32_t name_size;
+	int32_t temp;
+	unsigned int i;
+	unsigned int offset;
+	unsigned int value_offset;
+	struct SPDY_NameValue * iterator;
+	int j;
+
+	size = 4; //for num pairs
+
+	for(j=0; j<num_containers; ++j)
+	{
+    iterator = container[j];
+    while(iterator != NULL)
+    {
+      ++num_pairs;
+      size += 4 + strlen(iterator->name); //length + string
+
+      SPDYF_ASSERT(iterator->num_values>0, "num_values is 0");
+
+      size += 4; //value length
+
+      for(i=0; i<iterator->num_values; ++i)
+      {
+        //if(NULL == iterator->value[i])
+        //  continue;
+        size += strlen(iterator->value[i]); // string
+        if(i/* || !strlen(iterator->value[i])*/) ++size; //NULL separator
+      }
+
+      iterator = iterator->next;
+    }
+  }
+
+	if(NULL == (*stream = malloc(size)))
+	{
+		return -1;
+	}
+
+	//put num_pairs to the stream
+	num_pairs = htonl(num_pairs);
+	memcpy(*stream, &num_pairs, 4);
+	offset = 4;
+
+	//put all other headers to the stream
+	for(j=0; j<num_containers; ++j)
+	{
+    iterator = container[j];
+    while(iterator != NULL)
+    {
+      name_size = strlen(iterator->name);
+      temp = htonl(name_size);
+      memcpy(*stream + offset, &temp, 4);
+      offset += 4;
+      strncpy(*stream + offset, iterator->name, name_size);
+      offset += name_size;
+
+      value_offset = offset;
+      offset += 4;
+      for(i=0; i<iterator->num_values; ++i)
+      {
+        if(i /*|| !strlen(iterator->value[0])*/)
+        {
+          memset(*stream + offset, 0, 1);
+          ++offset;
+          //if(!i) continue;
+        }
+        //else if(NULL != iterator->value[i])
+        //{
+          strncpy(*stream + offset, iterator->value[i], strlen(iterator->value[i]));
+          offset += strlen(iterator->value[i]);
+        //}
+      }
+      value_size = offset - value_offset - 4;
+      value_size = htonl(value_size);
+      memcpy(*stream + value_offset, &value_size, 4);
+
+      iterator = iterator->next;
+    }
+  }
+
+	SPDYF_ASSERT(offset == size,"offset is wrong");
+
+	return size;
+}
+
+
+/* Needed by testcase to be extern -- should this be
+   in the header? */
+_MHD_EXTERN int
+SPDYF_name_value_from_stream(void *stream,
+							size_t size,
+							struct SPDY_NameValue ** container)
+{
+	int32_t num_pairs;
+	int32_t value_size;
+	int32_t name_size;
+	int i;
+	unsigned int offset = 0;
+	unsigned int value_end_offset;
+	char *name;
+	char *value;
+
+	if(NULL == (*container = SPDY_name_value_create ()))
+	{
+		return SPDY_NO;
+	}
+
+	//get number of pairs
+	memcpy(&num_pairs, stream, 4);
+	offset = 4;
+	num_pairs = ntohl(num_pairs);
+
+	if(num_pairs > 0)
+	{
+		for(i = 0; i < num_pairs; ++i)
+		{
+			//get name size
+			memcpy(&name_size, stream + offset, 4);
+			offset += 4;
+			name_size = ntohl(name_size);
+			//get name
+			if(NULL == (name = strndup(stream + offset, name_size)))
+			{
+				SPDY_name_value_destroy(*container);
+				return SPDY_NO;
+			}
+			offset+=name_size;
+
+			//get value size
+			memcpy(&value_size, stream + offset, 4);
+			offset += 4;
+			value_size = ntohl(value_size);
+			value_end_offset = offset + value_size;
+			//get value
+			do
+			{
+				if(NULL == (value = strndup(stream + offset, value_size)))
+				{
+					free(name);
+					SPDY_name_value_destroy(*container);
+					return SPDY_NO;
+				}
+				offset += strlen(value);
+				if(offset < value_end_offset)
+					++offset; //NULL separator
+
+				//add name/value to the struct
+				if(SPDY_YES != SPDY_name_value_add(*container, name, value))
+				{
+					free(name);
+					free(value);
+					SPDY_name_value_destroy(*container);
+					return SPDY_NO;
+				}
+				free(value);
+			}
+			while(offset < value_end_offset);
+
+			free(name);
+
+			if(offset != value_end_offset)
+			{
+				SPDY_name_value_destroy(*container);
+				return SPDY_INPUT_ERROR;
+			}
+		}
+	}
+
+	if(offset == size)
+		return SPDY_YES;
+
+	SPDY_name_value_destroy(*container);
+	return SPDY_INPUT_ERROR;
+}
diff --git a/src/microspdy/structures.h b/src/microspdy/structures.h
new file mode 100644
index 0000000..e1f8797
--- /dev/null
+++ b/src/microspdy/structures.h
@@ -0,0 +1,1246 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file structures.h
+ * @brief  internal and public structures -- most of the structs used by
+ * 			the library are defined here
+ * @author Andrey Uzunov
+ */
+
+#ifndef STRUCTURES_H
+#define STRUCTURES_H
+
+#include "platform.h"
+#include "microspdy.h"
+#include "io.h"
+
+
+/**
+ * All possible SPDY control frame types. The number is used in the header
+ * of the control frame.
+ */
+enum SPDY_CONTROL_FRAME_TYPES
+{
+	/**
+	 * The SYN_STREAM control frame allows the sender to asynchronously
+	 * create a stream between the endpoints.
+	 */
+	SPDY_CONTROL_FRAME_TYPES_SYN_STREAM = 1,
+	
+	/**
+	 * SYN_REPLY indicates the acceptance of a stream creation by
+	 * the recipient of a SYN_STREAM frame.
+	 */
+	SPDY_CONTROL_FRAME_TYPES_SYN_REPLY = 2,
+	
+	/**
+	 * The RST_STREAM frame allows for abnormal termination of a stream.
+	 * When sent by the creator of a stream, it indicates the creator
+	 * wishes to cancel the stream. When sent by the recipient of a
+	 * stream, it indicates an error or that the recipient did not want
+	 * to accept the stream, so the stream should be closed.
+	 */
+	SPDY_CONTROL_FRAME_TYPES_RST_STREAM = 3,
+	
+	/**
+	 * A SETTINGS frame contains a set of id/value pairs for
+	 * communicating configuration data about how the two endpoints may
+	 * communicate. SETTINGS frames can be sent at any time by either
+	 * endpoint, are optionally sent, and are fully asynchronous. When
+	 * the server is the sender, the sender can request that
+	 * configuration data be persisted by the client across SPDY
+	 * sessions and returned to the server in future communications.
+	 */
+	SPDY_CONTROL_FRAME_TYPES_SETTINGS = 4,
+	
+	/**
+	 * The PING control frame is a mechanism for measuring a minimal
+	 * round-trip time from the sender. It can be sent from the client
+	 * or the server. Recipients of a PING frame should send an
+	 * identical frame to the sender as soon as possible (if there is
+	 * other pending data waiting to be sent, PING should take highest
+	 * priority). Each ping sent by a sender should use a unique ID.
+	 */
+	SPDY_CONTROL_FRAME_TYPES_PING = 6,
+	
+	/**
+	 * The GOAWAY control frame is a mechanism to tell the remote side
+	 * of the connection to stop creating streams on this session. It
+	 * can be sent from the client or the server.
+	 */
+	SPDY_CONTROL_FRAME_TYPES_GOAWAY = 7,
+	
+	/**
+	 * The HEADERS frame augments a stream with additional headers. It
+	 * may be optionally sent on an existing stream at any time.
+	 * Specific application of the headers in this frame is
+	 * application-dependent. The name/value header block within this
+	 * frame is compressed.
+	 */
+	SPDY_CONTROL_FRAME_TYPES_HEADERS = 8,
+	
+	/**
+	 * The WINDOW_UPDATE control frame is used to implement per stream
+	 * flow control in SPDY. Flow control in SPDY is per hop, that is,
+	 * only between the two endpoints of a SPDY connection. If there are
+	 * one or more intermediaries between the client and the origin
+	 * server, flow control signals are not explicitly forwarded by the
+	 * intermediaries.
+	 */
+	SPDY_CONTROL_FRAME_TYPES_WINDOW_UPDATE = 9,
+	
+	/**
+	 * The CREDENTIAL control frame is used by the client to send
+	 * additional client certificates to the server. A SPDY client may
+	 * decide to send requests for resources from different origins on
+	 * the same SPDY session if it decides that that server handles both
+	 * origins. For example if the IP address associated with both
+	 * hostnames matches and the SSL server certificate presented in the
+	 * initial handshake is valid for both hostnames. However, because
+	 * the SSL connection can contain at most one client certificate,
+	 * the client needs a mechanism to send additional client
+	 * certificates to the server.
+	 */
+	SPDY_CONTROL_FRAME_TYPES_CREDENTIAL = 11
+};
+
+
+/**
+ * SPDY_SESSION_STATUS is used to show the current receiving state 
+ * of each session, i.e. what is expected to come now, and how it should
+ * be handled.
+ */
+enum SPDY_SESSION_STATUS
+{
+	/**
+	 * The session is in closing state, do not read read anything from
+	 * it. Do not write anything to it.
+	 */
+	SPDY_SESSION_STATUS_CLOSING = 0,
+	
+	/**
+	 * Wait for new SPDY frame to come.
+	 */
+	SPDY_SESSION_STATUS_WAIT_FOR_HEADER = 1,
+	
+	/**
+	 * The standard 8 byte header of the SPDY frame was received and
+	 * handled. Wait for the specific (sub)headers according to the
+	 * frame type.
+	 */
+	SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER = 2,
+	
+	/**
+	 * The specific (sub)headers were received and handled. Wait for the
+	 * "body", i.e. wait for the name/value pairs compressed by zlib.
+	 */
+	SPDY_SESSION_STATUS_WAIT_FOR_BODY = 3,
+	
+	/**
+	 * Ignore all the bytes read from the socket, e.g. larger frames.
+	 */
+	SPDY_SESSION_STATUS_IGNORE_BYTES= 4,
+	
+	/**
+	 * The session is in pre-closing state, do not read read anything
+	 * from it. In this state the output queue will be written to the
+	 * socket.
+	 */
+	SPDY_SESSION_STATUS_FLUSHING = 5,
+};
+
+
+/**
+ * Specific flags for the SYN_STREAM control frame.
+ */
+enum SPDY_SYN_STREAM_FLAG
+{
+	/**
+	 * The sender won't send any more frames on this stream.
+	 */
+	SPDY_SYN_STREAM_FLAG_FIN = 1,
+	
+	/**
+	 * The sender creates this stream as unidirectional.
+	 */
+	SPDY_SYN_STREAM_FLAG_UNIDIRECTIONAL = 2
+};
+
+
+/**
+ * Specific flags for the SYN_REPLY control frame.
+ */
+enum SPDY_SYN_REPLY_FLAG
+{
+	/**
+	 * The sender won't send any more frames on this stream.
+	 */
+	SPDY_SYN_REPLY_FLAG_FIN = 1
+};
+
+
+/**
+ * Specific flags for the data frame.
+ */
+enum SPDY_DATA_FLAG
+{
+	/**
+	 * The sender won't send any more frames on this stream.
+	 */
+	SPDY_DATA_FLAG_FIN = 1,
+	
+	/**
+	 * The data in the frame is compressed. 
+	 * This flag appears only in the draft on ietf.org but not on
+	 * chromium.org.
+	 */
+	SPDY_DATA_FLAG_COMPRESS = 2
+};
+
+/**
+ * Status code within RST_STREAM control frame.
+ */
+enum SPDY_RST_STREAM_STATUS
+{
+	/**
+	 * This is a generic error, and should only be used if a more
+	 * specific error is not available.
+	 */
+	SPDY_RST_STREAM_STATUS_PROTOCOL_ERROR = 1,
+	
+	/**
+	 * This is returned when a frame is received for a stream which is
+	 * not active.
+	 */
+	SPDY_RST_STREAM_STATUS_INVALID_STREAM = 2,
+	
+	/**
+	 * Indicates that the stream was refused before any processing has
+	 * been done on the stream.
+	 */
+	SPDY_RST_STREAM_STATUS_REFUSED_STREAM = 3,
+	
+	/**
+	 * Indicates that the recipient of a stream does not support the
+	 * SPDY version requested.
+	 */
+	SPDY_RST_STREAM_STATUS_UNSUPPORTED_VERSION = 4,
+	
+	/**
+	 * Used by the creator of a stream to indicate that the stream is
+	 * no longer needed.
+	 */
+	SPDY_RST_STREAM_STATUS_CANCEL = 5,
+	
+	/**
+	 * This is a generic error which can be used when the implementation
+	 * has internally failed, not due to anything in the protocol.
+	 */
+	SPDY_RST_STREAM_STATUS_INTERNAL_ERROR = 6,
+	
+	/**
+	 * The endpoint detected that its peer violated the flow control
+	 * protocol.
+	 */
+	SPDY_RST_STREAM_STATUS_FLOW_CONTROL_ERROR = 7,
+	
+	/**
+	 * The endpoint received a SYN_REPLY for a stream already open.
+	 */
+	SPDY_RST_STREAM_STATUS_STREAM_IN_USE = 8,
+	
+	/**
+	 * The endpoint received a data or SYN_REPLY frame for a stream
+	 * which is half closed.
+	 */
+	SPDY_RST_STREAM_STATUS_STREAM_ALREADY_CLOSED = 9,
+	
+	/**
+	 * The server received a request for a resource whose origin does
+	 * not have valid credentials in the client certificate vector.
+	 */
+	SPDY_RST_STREAM_STATUS_INVALID_CREDENTIALS = 10,
+	
+	/**
+	 * The endpoint received a frame which this implementation could not
+	 * support. If FRAME_TOO_LARGE is sent for a SYN_STREAM, HEADERS,
+	 * or SYN_REPLY frame without fully processing the compressed
+	 * portion of those frames, then the compression state will be
+	 * out-of-sync with the other endpoint. In this case, senders of
+	 * FRAME_TOO_LARGE MUST close the session.
+	 */
+	SPDY_RST_STREAM_STATUS_FRAME_TOO_LARGE = 11
+};
+
+
+/**
+ * Status code within GOAWAY control frame.
+ */
+enum SPDY_GOAWAY_STATUS
+{
+	/**
+	 * This is a normal session teardown.
+	 */
+	SPDY_GOAWAY_STATUS_OK = 0,
+	
+	/**
+	 * This is a generic error, and should only be used if a more
+	 * specific error is not available.
+	 */
+	SPDY_GOAWAY_STATUS_PROTOCOL_ERROR = 1,
+	
+	/**
+	 * This is a generic error which can be used when the implementation
+	 * has internally failed, not due to anything in the protocol.
+	 */
+	SPDY_GOAWAY_STATUS_INTERNAL_ERROR = 11
+};
+
+
+struct SPDYF_Stream;
+
+struct SPDYF_Response_Queue;
+
+
+/**
+ * Callback for received new data chunk.
+ *
+ * @param cls client-defined closure
+ * @param stream handler
+ * @param buf data chunk from the data
+ * @param size the size of the data chunk 'buf' in bytes
+ * @param more false if this is the last frame received on this stream. Note:
+ *             true does not mean that more data will come, exceptional
+ *             situation is possible
+ * @return SPDY_YES to continue calling the function,
+ *         SPDY_NO to stop calling the function for this stream
+ */
+typedef int
+(*SPDYF_NewDataCallback) (void * cls,
+					 struct SPDYF_Stream *stream,
+					 const void * buf,
+					 size_t size,
+					 bool more);
+           
+           
+/**
+ * Callback for new stream. To be used in the application layer of the
+ * lib.
+ *
+ * @param cls
+ * @param stream the new stream
+ * @return SPDY_YES on success,
+ *         SPDY_NO if error occurs
+ */
+typedef int
+(*SPDYF_NewStreamCallback) (void *cls,
+						struct SPDYF_Stream * stream);
+
+
+/**
+ * Callback to be called when the response queue object was handled and 
+ * the data was already sent. 
+ *
+ * @param cls
+ * @param response_queue the SPDYF_Response_Queue structure which will
+ * 			be cleaned very soon
+ * @param status shows if actually the response was sent or it was
+ * 			discarded by the lib for any reason (e.g., closing session,
+ * 			closing stream, stopping daemon, etc.). It is possible that
+ * 			status indicates an error but part of the response (in one
+ * 			or several frames) was sent to the client.
+ */
+typedef void
+(*SPDYF_ResponseQueueResultCallback) (void * cls,
+								struct SPDYF_Response_Queue *response_queue,
+								enum SPDY_RESPONSE_RESULT status);
+
+
+/**
+ * Representation of the control frame's headers, which are common for
+ * all types.
+ */
+struct __attribute__((__packed__)) SPDYF_Control_Frame
+{
+	uint16_t version : 15;
+	uint16_t control_bit : 1; /* always 1 for control frames */
+	uint16_t type;
+	uint32_t flags : 8;
+	uint32_t length : 24;
+};
+
+
+/**
+ * Representation of the data frame's headers.
+ */
+struct __attribute__((__packed__)) SPDYF_Data_Frame
+{
+	uint32_t stream_id : 31;
+	uint32_t control_bit : 1; /* always 0 for data frames */
+	uint32_t flags : 8;
+	uint32_t length : 24;
+};
+
+
+/**
+ * Queue of the responses, to be handled (e.g. compressed) and sent later.
+ */
+struct SPDYF_Response_Queue
+{
+	/**
+	 * This is a doubly-linked list.
+	 */
+	struct SPDYF_Response_Queue *next;
+
+	/**
+	 * This is a doubly-linked list.
+	 */
+	struct SPDYF_Response_Queue *prev;
+
+	/**
+	 * Stream (Request) for which is the response.
+	 */
+	struct SPDYF_Stream *stream;
+
+	/**
+	 * Response structure with all the data (uncompressed headers) to be sent.
+	 */
+	struct SPDY_Response *response;
+
+	/**
+	 * Control frame. The length field should be set after compressing
+	 * the headers!
+	 */
+	struct SPDYF_Control_Frame *control_frame;
+
+	/**
+	 * Data frame. The length field should be set after compressing
+	 * the body!
+	 */
+	struct SPDYF_Data_Frame *data_frame;
+
+	/**
+	 * Data to be sent: name/value pairs in control frames or body in data frames.
+	 */
+	void *data;
+
+	/**
+	 * Specific handler for different frame types.
+	 */
+	int (* process_response_handler)(struct SPDY_Session *session);
+
+	/**
+	 * Callback to be called when the last bytes from the response was sent
+	 * to the client.
+	 */
+	SPDYF_ResponseQueueResultCallback frqcb;
+	
+	/**
+	 * Closure for frqcb.
+	 */
+	void *frqcb_cls;
+
+	/**
+	 * Callback to be used by the application layer.
+	 */
+	SPDY_ResponseResultCallback rrcb;
+	
+	/**
+	 * Closure for rcb.
+	 */
+	void *rrcb_cls;
+
+	/**
+	 * Data size.
+	 */
+	size_t data_size;
+
+	/**
+	 * True if data frame should be sent. False if control frame should
+	 * be sent.
+	 */
+	bool is_data;
+};
+
+
+
+/**
+ * Collection of HTTP headers used in requests and responses.
+ */
+struct SPDY_NameValue
+{
+	/**
+	* This is a doubly-linked list.
+	*/
+	struct SPDY_NameValue *next;
+
+	/**
+	* This is a doubly-linked list.
+	*/
+	struct SPDY_NameValue *prev;
+
+	/**
+	* Null terminated string for name.
+	*/
+    char *name;
+
+	/**
+	* Array of Null terminated strings for value. num_values is the
+	* length of the array.
+	*/
+	char **value;
+
+	/**
+	* Number of values, this is >= 0.
+	*/
+	unsigned int num_values;
+};
+
+
+/**
+ * Represents a SPDY stream
+ */ 
+struct SPDYF_Stream
+{
+	/**
+	 * This is a doubly-linked list.
+	 */
+	struct SPDYF_Stream *next;
+
+	/**
+	 * This is a doubly-linked list.
+	 */
+	struct SPDYF_Stream *prev;
+
+	/**
+	 * Reference to the SPDY_Session struct.
+	 */
+	struct SPDY_Session *session;
+	
+	/**
+	 * Name value pairs, sent within the frame which created the stream.
+	 */
+	struct SPDY_NameValue *headers;
+	
+	/**
+	 * Any object to be used by the application layer.
+	 */
+	void *cls;
+  
+	/**
+	 * This stream's ID.
+	 */
+	uint32_t stream_id;
+	
+	/**
+	 * Stream to which this one is associated.
+	 */
+	uint32_t assoc_stream_id;
+	
+	/**
+	 * The window of the data within data frames.
+	 */
+	uint32_t window_size;
+	
+	/**
+	 * Stream priority. 0 is the highest, 7 is the lowest.
+	 */
+	uint8_t priority;
+	
+	/**
+	 * Integer specifying the index in the server's CREDENTIAL vector of
+	 * the client certificate to be used for this request The value 0
+	 * means no client certificate should be associated with this stream.
+	 */
+	uint8_t slot;
+	
+	/**
+	 * If initially the stream was created as unidirectional.
+	 */
+	bool flag_unidirectional;
+	
+	/**
+	 * If the stream won't be used for receiving frames anymore. The 
+	 * client has sent FLAG_FIN or the stream was terminated with
+	 * RST_STREAM.
+	 */
+	bool is_in_closed;
+	
+	/**
+	 * If the stream won't be used for sending out frames anymore. The 
+	 * server has sent FLAG_FIN or the stream was terminated with
+	 * RST_STREAM.
+	 */
+	bool is_out_closed;
+	
+	/**
+	 * Which entity (server/client) has created the stream.
+	 */
+	bool is_server_initiator;
+};
+
+
+/**
+ * Represents a SPDY session which is just a TCP connection
+ */ 
+struct SPDY_Session
+{
+	/**
+	 * zlib stream for decompressing all the name/pair values from the
+	 * received frames. All the received compressed data must be
+	 * decompressed within one context: this stream. Thus, it should be
+	 * unique for the session and initialized at its creation.
+	 */
+	z_stream zlib_recv_stream;
+
+	/**
+	 * zlib stream for compressing all the name/pair values from the
+	 * frames to be sent. All the sent compressed data must be
+	 * compressed within one context: this stream. Thus, it should be
+	 * unique for the session and initialized at its creation.
+	 */
+	z_stream zlib_send_stream;
+	
+	/**
+	 * This is a doubly-linked list.
+	 */
+	struct SPDY_Session *next;
+
+	/**
+	 * This is a doubly-linked list.
+	 */
+	struct SPDY_Session *prev;
+
+	/**
+	 * Reference to the SPDY_Daemon struct.
+	 */
+	struct SPDY_Daemon *daemon;
+
+	/**
+	 * Foreign address (of length addr_len).
+	 */
+	struct sockaddr *addr;
+
+	/**
+	 * Head of doubly-linked list of the SPDY streams belonging to the
+	 * session.
+	 */
+	struct SPDYF_Stream *streams_head;
+
+	/**
+	 * Tail of doubly-linked list of the streams.
+	 */
+	struct SPDYF_Stream *streams_tail;
+
+	/**
+	 * Unique IO context for the session. Initialized on each creation
+	 * (actually when the TCP connection is established).
+	 */
+	void *io_context;
+	
+	/**
+	 * Head of doubly-linked list of the responses.
+	 */
+	struct SPDYF_Response_Queue *response_queue_head;
+	
+	/**
+	 * Tail of doubly-linked list of the responses.
+	 */
+	struct SPDYF_Response_Queue *response_queue_tail;
+
+	/**
+	 * Buffer for reading requests.
+	 */
+	void *read_buffer;
+
+	/**
+	 * Buffer for writing responses.
+	 */
+	void *write_buffer;
+
+	/**
+	 * Specific handler for the frame that is currently being received.
+	 */
+	void (*frame_handler) (struct SPDY_Session * session);
+
+	/**
+	 * Closure for frame_handler.
+	 */
+	void *frame_handler_cls;
+
+	/**
+	 * Extra field to be used by the user with set/get func for whatever
+	 * purpose he wants.
+	 */
+	void *user_cls;
+
+	/**
+	 * Function to initialize the IO context for a new session.
+	 */
+	SPDYF_IONewSession fio_new_session;
+
+	/**
+	 * Function to deinitialize the IO context for a session.
+	 */
+	SPDYF_IOCloseSession fio_close_session;
+
+	/**
+	 * Function to read data from socket.
+	 */
+	SPDYF_IORecv fio_recv;
+
+	/**
+	 * Function to write data to socket.
+	 */
+	SPDYF_IOSend fio_send;
+
+	/**
+	 * Function to check for pending data in IO buffers.
+	 */
+	SPDYF_IOIsPending fio_is_pending;
+
+	/**
+	 * Function to call before writing set of frames.
+	 */
+	SPDYF_IOBeforeWrite fio_before_write;
+
+	/**
+	 * Function to call after writing set of frames.
+	 */
+	SPDYF_IOAfterWrite fio_after_write;
+
+	/**
+	 * Number of bytes that the lib must ignore immediately after they 
+	 * are read from the TLS socket without adding them to the read buf.
+	 * This is needed, for instance, when receiving frame bigger than
+	 * the buffer to avoid deadlock situations.
+	 */
+	size_t read_ignore_bytes;
+
+	/**
+	 * Size of read_buffer (in bytes).  This value indicates
+	 * how many bytes we're willing to read into the buffer;
+	 * the real buffer is one byte longer to allow for
+	 * adding zero-termination (when needed).
+	 */
+	size_t read_buffer_size;
+
+	/**
+	 * Position where we currently append data in
+	 * read_buffer (last valid position).
+	 */
+	size_t read_buffer_offset;
+
+	/**
+	 * Position until where everything was already read
+	 */
+	size_t read_buffer_beginning;
+
+	/**
+	 * Size of write_buffer (in bytes).  This value indicates
+	 * how many bytes we're willing to prepare for writing.
+	 */
+	size_t write_buffer_size;
+
+	/**
+	 * Position where we currently append data in
+	 * write_buffer (last valid position).
+	 */
+	size_t write_buffer_offset;
+
+	/**
+	 * Position until where everything was already written to the socket
+	 */
+	size_t write_buffer_beginning;
+	
+	/**
+	 * Last time this connection had any activity
+	 * (reading or writing). In milliseconds.
+	 */
+	unsigned long long last_activity;
+
+	/**
+	 * Socket for this connection.  Set to -1 if
+	 * this connection has died (daemon should clean
+	 * up in that case).
+	 */
+	int socket_fd;
+
+	/**
+	 * Length of the foreign address.
+	 */
+	socklen_t addr_len;
+	
+	/**
+	 * The biggest stream ID for this session for streams initiated
+	 * by the client.
+	 */
+	uint32_t last_in_stream_id;
+	
+	/**
+	 * The biggest stream ID for this session for streams initiated
+	 * by the server.
+	 */
+	uint32_t last_out_stream_id;
+	
+	/**
+	 * This value is updated whenever SYN_REPLY or RST_STREAM are sent
+	 * and is used later in GOAWAY frame.
+	 * TODO it is not clear in the draft what happens when streams are
+	 * not answered in the order of their IDs. Moreover, why should we
+	 * send GOAWAY with the ID of received bogus SYN_STREAM with huge ID?
+	 */
+	uint32_t last_replied_to_stream_id;
+	
+	/**
+	 * Shows the stream id of the currently handled frame. This value is
+	 * to be used when sending RST_STREAM in answer to a problematic
+	 * frame, e.g. larger than supported.
+	 */
+	uint32_t current_stream_id;
+	
+	/**
+	 * Maximum number of frames to be written to the socket at once. The
+   * library tries to send max_num_frames in a single call to SPDY_run
+   * for a single session. This means no requests can be received nor
+   * other sessions can send data as long the current one has enough
+   * frames to send and there is no error on writing.
+	 */
+	uint32_t max_num_frames;
+
+	/**
+	 * Shows the current receiving state the session, i.e. what is
+	 * expected to come now, and how it shold be handled.
+	 */
+	enum SPDY_SESSION_STATUS status;
+
+	/**
+	 * Has this socket been closed for reading (i.e.
+	 * other side closed the connection)?  If so,
+	 * we must completely close the connection once
+	 * we are done sending our response (and stop
+	 * trying to read from this socket).
+	 */
+	bool read_closed;
+
+	/**
+	 * If the server sends GOAWAY, it must ignore all SYN_STREAMS for
+	 * this session. Normally the server will soon close the TCP session.
+	 */
+	bool is_goaway_sent;
+
+	/**
+	 * If the server receives GOAWAY, it must not send new SYN_STREAMS 
+	 * on this session. Normally the client will soon close the TCP
+	 * session.
+	 */
+	bool is_goaway_received;
+};
+
+
+/**
+ * State and settings kept for each SPDY daemon.
+ */
+struct SPDY_Daemon
+{
+
+	/**
+	 * Tail of doubly-linked list of our current, active sessions.
+	 */
+	struct SPDY_Session *sessions_head;
+
+	/**
+	 * Tail of doubly-linked list of our current, active sessions.
+	 */
+	struct SPDY_Session *sessions_tail;
+	
+	/**
+	 * Tail of doubly-linked list of connections to clean up.
+	 */
+	struct SPDY_Session *cleanup_head;
+
+	/**
+	 * Tail of doubly-linked list of connections to clean up.
+	 */
+	struct SPDY_Session *cleanup_tail;
+
+	/**
+	 * Unique IO context for the daemon. Initialized on daemon start.
+	 */
+	void *io_context;
+
+	/**
+	 * Certificate file of the server. File path is kept here.
+	 */
+	char *certfile;
+
+	/**
+	 * Key file for the certificate of the server. File path is
+	 * kept here.
+	 */
+	char *keyfile;
+	
+
+	/**
+	 * The address to which the listening socket is bound.
+	 */
+	struct sockaddr *address;
+	
+	/**
+	 * Callback called when a new SPDY session is
+	 * established by a client
+	 */
+	SPDY_NewSessionCallback new_session_cb;
+
+	/**
+	 * Callback called when a client closes the session
+	 */
+	SPDY_SessionClosedCallback session_closed_cb;
+
+	/**
+	 * Callback called when a client sends request
+	 */
+	SPDY_NewRequestCallback new_request_cb;
+
+	/**
+	* Callback called when HTTP POST params are received
+	* after request. To be used by the application layer
+	*/
+	SPDY_NewDataCallback received_data_cb;
+
+	/**
+	* Callback called when DATA frame is received.
+	*/
+	SPDYF_NewDataCallback freceived_data_cb;
+
+	/**
+	 * Closure argument for all the callbacks that can be used by the client.
+	 */
+	void *cls;
+
+	/**
+	 * Callback called when new stream is created.
+	 */
+	SPDYF_NewStreamCallback fnew_stream_cb;
+
+	/**
+	 * Closure argument for all the callbacks defined in the framing layer.
+	 */
+	void *fcls;
+
+	/**
+	 * Function to initialize the IO context for the daemon.
+	 */
+	SPDYF_IOInit fio_init;
+
+	/**
+	 * Function to deinitialize the IO context for the daemon.
+	 */
+	SPDYF_IODeinit fio_deinit;
+
+	/**
+	 * After how many milliseconds of inactivity should
+	 * connections time out? Zero for no timeout.
+	 */
+	unsigned long long session_timeout;
+
+	/**
+	 * Listen socket.
+	 */
+	int socket_fd;
+	
+	/**
+   * This value is inherited by all sessions of the daemon.
+	 * Maximum number of frames to be written to the socket at once. The
+   * library tries to send max_num_frames in a single call to SPDY_run
+   * for a single session. This means no requests can be received nor
+   * other sessions can send data as long the current one has enough
+   * frames to send and there is no error on writing.
+	 */
+	uint32_t max_num_frames;
+
+	/**
+	 * Daemon's options.
+	 */
+	enum SPDY_DAEMON_OPTION options;
+
+	/**
+	 * Daemon's flags.
+	 */
+	enum SPDY_DAEMON_FLAG flags;
+
+	/**
+	 * IO subsystem type used by daemon and all its sessions.
+	 */
+	enum SPDY_IO_SUBSYSTEM io_subsystem;
+
+	/**
+	 * Listen port.
+	 */
+	uint16_t port;
+};
+
+
+/**
+ * Represents a SPDY response.
+ */
+struct SPDY_Response
+{
+	/**
+	 * Raw uncompressed stream of the name/value pairs in SPDY frame
+	 * used for the HTTP headers.
+	 */
+    void *headers;
+	
+	/**
+	 * Raw stream of the data to be sent. Equivalent to the body in HTTP
+	 * response.
+	 */
+	void *data;
+	
+	/**
+	 * Callback function to be used when the response data is provided
+	 * with callbacks. In this case data must be NULL and data_size must
+	 * be 0.
+	 */
+	SPDY_ResponseCallback rcb;
+	
+	/**
+	 * Extra argument to rcb.
+	 */
+	void *rcb_cls;
+	
+	/**
+	 * Length of headers.
+	 */
+	size_t headers_size;
+	
+	/**
+	 * Length of data.
+	 */
+	size_t data_size;
+	
+	/**
+	 * The callback func will be called to get that amount of bytes to
+	 * put them into a DATA frame. It is either user preffered or
+	 * the maximum supported by the lib value.
+	 */
+	uint32_t rcb_block_size;
+};
+
+
+/* Macros for handling data and structures */
+
+
+/**
+ * Insert an element at the head of a DLL. Assumes that head, tail and
+ * element are structs with prev and next fields.
+ *
+ * @param head pointer to the head of the DLL (struct ? *)
+ * @param tail pointer to the tail of the DLL (struct ? *)
+ * @param element element to insert (struct ? *)
+ */
+#define DLL_insert(head,tail,element) do { \
+	(element)->next = (head); \
+	(element)->prev = NULL; \
+	if ((tail) == NULL) \
+		(tail) = element; \
+	else \
+		(head)->prev = element; \
+	(head) = (element); } while (0)
+
+
+/**
+ * Remove an element from a DLL. Assumes
+ * that head, tail and element are structs
+ * with prev and next fields.
+ *
+ * @param head pointer to the head of the DLL (struct ? *)
+ * @param tail pointer to the tail of the DLL (struct ? *)
+ * @param element element to remove (struct ? *)
+ */
+#define DLL_remove(head,tail,element) do { \
+	if ((element)->prev == NULL) \
+		(head) = (element)->next;  \
+	else \
+		(element)->prev->next = (element)->next; \
+	if ((element)->next == NULL) \
+		(tail) = (element)->prev;  \
+	else \
+		(element)->next->prev = (element)->prev; \
+	(element)->next = NULL; \
+	(element)->prev = NULL; } while (0)
+
+
+/**
+ * Convert all integers in a SPDY control frame headers structure from
+ * host byte order to network byte order.
+ *
+ * @param frame input and output structure (struct SPDY_Control_Frame *)
+ */
+#if HAVE_BIG_ENDIAN
+#define SPDYF_CONTROL_FRAME_HTON(frame)
+#else
+#define SPDYF_CONTROL_FRAME_HTON(frame) do { \
+	(*((uint16_t *) frame  )) = (*((uint8_t *) (frame) +1 )) | ((*((uint8_t *) frame  ))<<8);\
+	(frame)->type = htons((frame)->type); \
+	(frame)->length = HTON24((frame)->length); \
+	} while (0)
+#endif
+
+
+/**
+ * Convert all integers in a SPDY control frame headers structure from
+ * network byte order to host byte order.
+ *
+ * @param frame input and output structure (struct SPDY_Control_Frame *)
+ */	
+#if HAVE_BIG_ENDIAN
+#define SPDYF_CONTROL_FRAME_NTOH(frame)
+#else
+#define SPDYF_CONTROL_FRAME_NTOH(frame) do { \
+	(*((uint16_t *) frame  )) = (*((uint8_t *) (frame) +1 )) | ((*((uint8_t *) frame  ))<<8);\
+	(frame)->type = ntohs((frame)->type); \
+	(frame)->length = NTOH24((frame)->length); \
+	} while (0)
+#endif
+
+
+/**
+ * Convert all integers in a SPDY data frame headers structure from
+ * host byte order to network byte order.
+ *
+ * @param frame input and output structure (struct SPDY_Data_Frame *)
+ */
+#if HAVE_BIG_ENDIAN
+#define SPDYF_DATA_FRAME_HTON(frame)
+#else
+#define SPDYF_DATA_FRAME_HTON(frame) do { \
+	*((uint32_t *) frame  ) = htonl(*((uint32_t *) frame  ));\
+	(frame)->length = HTON24((frame)->length); \
+	} while (0)
+#endif
+
+
+/**
+ * Convert all integers in a SPDY data frame headers structure from
+ * network byte order to host byte order.
+ *
+ * @param frame input and output structure (struct SPDY_Data_Frame *)
+ */	
+#if HAVE_BIG_ENDIAN
+#define SPDYF_DATA_FRAME_NTOH(frame)
+#else
+#define SPDYF_DATA_FRAME_NTOH(frame) do { \
+	*((uint32_t *) frame  ) = ntohl(*((uint32_t *) frame  ));\
+	(frame)->length = NTOH24((frame)->length); \
+	} while (0)
+#endif
+
+
+/**
+ * Creates one or more new SPDYF_Response_Queue object to be put on the
+ * response queue.
+ *
+ * @param is_data whether new data frame or new control frame will be
+ *                crerated
+ * @param data the row stream which will be used as the body of the frame
+ * @param data_size length of data
+ * @param response object, part of which is the frame
+ * @param stream on which data is to be sent 
+ * @param closestream TRUE if the frame must close the stream (with flag) 
+ * @param frqcb callback to notify application layer when the frame
+ *              has been sent or discarded 
+ * @param frqcb_cls closure for frqcb
+ * @param rrcb callback used by the application layer to notify the
+ *             application when the frame has been sent or discarded.
+ *             frqcb will call it 
+ * @param rrcb_cls closure for rrcb
+ * @return double linked list of SPDYF_Response_Queue structures: one or
+ *         more frames are returned based on the size of the data
+ */
+struct SPDYF_Response_Queue *
+SPDYF_response_queue_create(bool is_data,
+						void *data,
+						size_t data_size,
+						struct SPDY_Response *response,
+						struct SPDYF_Stream *stream,
+						bool closestream,
+						SPDYF_ResponseQueueResultCallback frqcb,
+						void *frqcb_cls,
+						SPDY_ResponseResultCallback rrcb,
+						void *rrcb_cls);
+
+
+/**
+ * Destroys SPDYF_Response_Queue structure and whatever is in it.
+ *
+ * @param response_queue to destroy
+ */
+void
+SPDYF_response_queue_destroy(struct SPDYF_Response_Queue *response_queue);
+
+
+/**
+ * Checks if the container is empty, i.e. created but no values were
+ * added to it.
+ *
+ * @param container
+ * @return SPDY_YES if empty
+ *         SPDY_NO if not
+ */
+int
+SPDYF_name_value_is_empty(struct SPDY_NameValue *container);
+
+
+/**
+ * Transforms raw binary decomressed stream of headers
+ * into SPDY_NameValue, containing all of the headers and values.
+ *
+ * @param stream that is to be transformed
+ * @param size length of the stream
+ * @param container will contain the newly created SPDY_NameValue
+ *        container. Should point to NULL.
+ * @return SPDY_YES on success
+ *         SPDY_NO on memory error
+ *         SPDY_INPUT_ERROR if the provided stream is not valid
+ */
+int
+SPDYF_name_value_from_stream(void *stream,
+							size_t size,
+							struct SPDY_NameValue ** container);
+
+
+/**
+ * Transforms array of objects of name/values tuples, containing HTTP
+ * headers, into raw binary stream. The resulting stream is ready to
+ * be compressed and sent.
+ *
+ * @param container one or more SPDY_NameValue objects. Each object
+ *        contains multiple number of name/value tuples.
+ * @param num_containers length of the array
+ * @param stream will contain the resulting stream. Should point to NULL.
+ * @return length of stream or value less than 0 indicating error
+ */
+ssize_t
+SPDYF_name_value_to_stream(struct SPDY_NameValue * container[],
+							int num_containers,
+							void **stream);
+
+#endif
diff --git a/src/platform/Makefile.am b/src/platform/Makefile.am
new file mode 100644
index 0000000..448efae
--- /dev/null
+++ b/src/platform/Makefile.am
@@ -0,0 +1,20 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = \
+  -I$(top_srcdir)/src/include
+
+AM_CFLAGS = $(HIDDEN_VISIBILITY_CFLAGS)
+
+if USE_COVERAGE
+  AM_CFLAGS += --coverage
+endif
+
+if HAVE_W32
+noinst_LTLIBRARIES = \
+  libplatform_interface.la
+libplatform_interface_la_CPPFLAGS = \
+  $(AM_CPPFLAGS) \
+  -DBUILDING_MHD_LIB=1
+libplatform_interface_la_SOURCES = \
+  w32functions.c
+endif
+
diff --git a/src/platform/Makefile.in b/src/platform/Makefile.in
new file mode 100644
index 0000000..31c85e8
--- /dev/null
+++ b/src/platform/Makefile.in
@@ -0,0 +1,658 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 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 = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+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@
+@USE_COVERAGE_TRUE@am__append_1 = --coverage
+subdir = src/platform
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_compile_flags.m4 \
+	$(top_srcdir)/m4/ax_append_flag.m4 \
+	$(top_srcdir)/m4/ax_check_compile_flag.m4 \
+	$(top_srcdir)/m4/ax_check_link_flag.m4 \
+	$(top_srcdir)/m4/ax_check_openssl.m4 \
+	$(top_srcdir)/m4/ax_count_cpus.m4 \
+	$(top_srcdir)/m4/ax_have_epoll.m4 \
+	$(top_srcdir)/m4/ax_pthread.m4 \
+	$(top_srcdir)/m4/ax_require_defined.m4 \
+	$(top_srcdir)/m4/libcurl.m4 $(top_srcdir)/m4/libgcrypt.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)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/MHD_config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libplatform_interface_la_LIBADD =
+am__libplatform_interface_la_SOURCES_DIST = w32functions.c
+@HAVE_W32_TRUE@am_libplatform_interface_la_OBJECTS =  \
+@HAVE_W32_TRUE@	libplatform_interface_la-w32functions.lo
+libplatform_interface_la_OBJECTS =  \
+	$(am_libplatform_interface_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 = 
+@HAVE_W32_TRUE@am_libplatform_interface_la_rpath =
+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__depfiles_maybe = depfiles
+am__mv = mv -f
+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 = $(libplatform_interface_la_SOURCES)
+DIST_SOURCES = $(am__libplatform_interface_la_SOURCES_DIST)
+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
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPU_COUNT = @CPU_COUNT@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_CPPFLAGS = @GNUTLS_CPPFLAGS@
+GNUTLS_LDFLAGS = @GNUTLS_LDFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+HAVE_CURL_BINARY = @HAVE_CURL_BINARY@
+HAVE_MAKEINFO_BINARY = @HAVE_MAKEINFO_BINARY@
+HIDDEN_VISIBILITY_CFLAGS = @HIDDEN_VISIBILITY_CFLAGS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSPDY_VERSION_AGE = @LIBSPDY_VERSION_AGE@
+LIBSPDY_VERSION_CURRENT = @LIBSPDY_VERSION_CURRENT@
+LIBSPDY_VERSION_REVISION = @LIBSPDY_VERSION_REVISION@
+LIBTOOL = @LIBTOOL@
+LIB_VERSION_AGE = @LIB_VERSION_AGE@
+LIB_VERSION_CURRENT = @LIB_VERSION_CURRENT@
+LIB_VERSION_REVISION = @LIB_VERSION_REVISION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MHD_LIBDEPS = @MHD_LIBDEPS@
+MHD_LIBDEPS_PKGCFG = @MHD_LIBDEPS_PKGCFG@
+MHD_LIB_CFLAGS = @MHD_LIB_CFLAGS@
+MHD_LIB_CPPFLAGS = @MHD_LIB_CPPFLAGS@
+MHD_LIB_LDFLAGS = @MHD_LIB_LDFLAGS@
+MHD_REQ_PRIVATE = @MHD_REQ_PRIVATE@
+MKDIR_P = @MKDIR_P@
+MS_LIB_TOOL = @MS_LIB_TOOL@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_INCLUDES = @OPENSSL_INCLUDES@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@
+PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@
+PACKAGE_VERSION_SUBMINOR = @PACKAGE_VERSION_SUBMINOR@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPDY_LIBDEPS = @SPDY_LIBDEPS@
+SPDY_LIB_CFLAGS = @SPDY_LIB_CFLAGS@
+SPDY_LIB_CPPFLAGS = @SPDY_LIB_CPPFLAGS@
+SPDY_LIB_LDFLAGS = @SPDY_LIB_LDFLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+_libcurl_config = @_libcurl_config@
+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_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@
+ax_pthread_config = @ax_pthread_config@
+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@
+have_socat = @have_socat@
+have_zzuf = @have_zzuf@
+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@
+lt_cv_objdir = @lt_cv_objdir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = \
+  -I$(top_srcdir)/src/include
+
+AM_CFLAGS = $(HIDDEN_VISIBILITY_CFLAGS) $(am__append_1)
+@HAVE_W32_TRUE@noinst_LTLIBRARIES = \
+@HAVE_W32_TRUE@  libplatform_interface.la
+
+@HAVE_W32_TRUE@libplatform_interface_la_CPPFLAGS = \
+@HAVE_W32_TRUE@  $(AM_CPPFLAGS) \
+@HAVE_W32_TRUE@  -DBUILDING_MHD_LIB=1
+
+@HAVE_W32_TRUE@libplatform_interface_la_SOURCES = \
+@HAVE_W32_TRUE@  w32functions.c
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .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) --gnu src/platform/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/platform/Makefile
+.PRECIOUS: 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__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	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}; \
+	}
+
+libplatform_interface.la: $(libplatform_interface_la_OBJECTS) $(libplatform_interface_la_DEPENDENCIES) $(EXTRA_libplatform_interface_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(LINK) $(am_libplatform_interface_la_rpath) $(libplatform_interface_la_OBJECTS) $(libplatform_interface_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libplatform_interface_la-w32functions.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libplatform_interface_la-w32functions.lo: w32functions.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libplatform_interface_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libplatform_interface_la-w32functions.lo -MD -MP -MF $(DEPDIR)/libplatform_interface_la-w32functions.Tpo -c -o libplatform_interface_la-w32functions.lo `test -f 'w32functions.c' || echo '$(srcdir)/'`w32functions.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libplatform_interface_la-w32functions.Tpo $(DEPDIR)/libplatform_interface_la-w32functions.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='w32functions.c' object='libplatform_interface_la-w32functions.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libplatform_interface_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libplatform_interface_la-w32functions.lo `test -f 'w32functions.c' || echo '$(srcdir)/'`w32functions.c
+
+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: $(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 -rf ./$(DEPDIR)
+	-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 -rf ./$(DEPDIR)
+	-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 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
+
+
+# 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/src/platform/w32functions.c b/src/platform/w32functions.c
new file mode 100644
index 0000000..bc42fef
--- /dev/null
+++ b/src/platform/w32functions.c
@@ -0,0 +1,704 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2014 Karlson2k (Evgeny Grin)
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library. 
+  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file platform/w32functions.h
+ * @brief  internal functions for W32 systems
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "w32functions.h"
+#include <errno.h>
+#include <winsock2.h>
+#include <string.h>
+#include <stdint.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+
+/**
+ * Return errno equivalent of last winsock error
+ * @return errno equivalent of last winsock error
+ */
+int MHD_W32_errno_from_winsock_(void)
+{
+  switch(WSAGetLastError())
+  {
+  case 0:                      return 0;
+  case WSA_INVALID_HANDLE:     return EBADF;
+  case WSA_NOT_ENOUGH_MEMORY:  return ENOMEM;
+  case WSA_INVALID_PARAMETER:  return EINVAL;
+  case WSAEINTR:               return EINTR;
+  case WSAEWOULDBLOCK:         return EWOULDBLOCK;
+  case WSAEINPROGRESS:         return EINPROGRESS;
+  case WSAEALREADY:            return EALREADY;
+  case WSAENOTSOCK:            return ENOTSOCK;
+  case WSAEDESTADDRREQ:        return EDESTADDRREQ;
+  case WSAEMSGSIZE:            return EMSGSIZE;
+  case WSAEPROTOTYPE:          return EPROTOTYPE;
+  case WSAENOPROTOOPT:         return ENOPROTOOPT;
+  case WSAEPROTONOSUPPORT:     return EPROTONOSUPPORT;
+  case WSAESOCKTNOSUPPORT:     return ESOCKTNOSUPPORT;
+  case WSAEOPNOTSUPP:          return EOPNOTSUPP;
+  case WSAEPFNOSUPPORT:        return EPFNOSUPPORT;
+  case WSAEAFNOSUPPORT:        return EAFNOSUPPORT;
+  case WSAEADDRINUSE:          return EADDRINUSE;
+  case WSAEADDRNOTAVAIL:       return EADDRNOTAVAIL;
+  case WSAENETDOWN:            return ENETDOWN;
+  case WSAENETUNREACH:         return ENETUNREACH;
+  case WSAENETRESET:           return ENETRESET;
+  case WSAECONNABORTED:        return ECONNABORTED;
+  case WSAECONNRESET:          return ECONNRESET;
+  case WSAENOBUFS:             return ENOBUFS;
+  case WSAEISCONN:             return EISCONN;
+  case WSAENOTCONN:            return ENOTCONN;
+  case WSAESHUTDOWN:           return ESHUTDOWN;
+  case WSAETOOMANYREFS:        return ETOOMANYREFS;
+  case WSAETIMEDOUT:           return ETIMEDOUT;
+  case WSAECONNREFUSED:        return ECONNREFUSED;
+  case WSAELOOP:               return ELOOP;
+  case WSAENAMETOOLONG:        return ENAMETOOLONG;
+  case WSAEHOSTDOWN:           return EHOSTDOWN;
+  case WSAEHOSTUNREACH:        return EHOSTUNREACH;
+  case WSAENOTEMPTY:           return ENOTEMPTY;
+  case WSAEPROCLIM:            return EPROCLIM;
+  case WSAEUSERS:              return EUSERS;
+  case WSAEDQUOT:              return EDQUOT;
+  case WSAESTALE:              return ESTALE;
+  case WSAEREMOTE:             return EREMOTE;
+  case WSAEINVAL:              return EINVAL;
+  case WSAEFAULT:              return EFAULT;
+  case WSANO_DATA:             return ENODATA;
+  /* Rough equivalents */
+  case WSAEDISCON:             return ECONNRESET;
+  case WSAEINVALIDPROCTABLE:   return EFAULT;
+  case WSASYSNOTREADY:
+  case WSANOTINITIALISED:
+  case WSASYSCALLFAILURE:      return ENOBUFS;
+  case WSAVERNOTSUPPORTED:     return EOPNOTSUPP;
+  case WSAEREFUSED:            return EIO;
+  }
+  return EINVAL;
+}
+
+/**
+ * Return pointer to string description of errnum error
+ * Works fine with both standard errno errnums
+ * and errnums from MHD_W32_errno_from_winsock_
+ * @param errnum the errno or value from MHD_W32_errno_from_winsock_()
+ * @return pointer to string description of error
+ */
+const char* MHD_W32_strerror_(int errnum)
+{
+  switch(errnum)
+  {
+  case 0:
+    return "No error";
+  case EWOULDBLOCK:
+    return "Operation would block";
+  case EINPROGRESS:
+    return "Connection already in progress";
+  case EALREADY:
+    return "Socket already connected";
+  case ENOTSOCK:
+    return "Socket operation on non-socket";
+  case EDESTADDRREQ:
+    return "Destination address required";
+  case EMSGSIZE:
+    return "Message too long";
+  case EPROTOTYPE:
+    return "Protocol wrong type for socket";
+  case ENOPROTOOPT:
+    return "Protocol not available";
+  case EPROTONOSUPPORT:
+    return "Unknown protocol";
+  case ESOCKTNOSUPPORT:
+    return "Socket type not supported";
+  case EOPNOTSUPP:
+    return "Operation not supported on socket";
+  case EPFNOSUPPORT:
+    return "Protocol family not supported";
+  case EAFNOSUPPORT:
+    return "Address family not supported by protocol family";
+  case EADDRINUSE:
+    return "Address already in use";
+  case EADDRNOTAVAIL:
+    return "Cannot assign requested address";
+  case ENETDOWN:
+    return "Network is down";
+  case ENETUNREACH:
+    return "Network is unreachable";
+  case ENETRESET:
+    return "Network dropped connection on reset";
+  case ECONNABORTED:
+    return "Software caused connection abort";
+  case ECONNRESET:
+    return "Connection reset by peer";
+  case ENOBUFS:
+    return "No system resources available";
+  case EISCONN:
+    return "Socket is already connected";
+  case ENOTCONN:
+    return "Socket is not connected";
+  case ESHUTDOWN:
+    return "Can't send after socket shutdown";
+  case ETOOMANYREFS:
+    return "Too many references: cannot splice";
+  case ETIMEDOUT:
+    return "Connection timed out";
+  case ECONNREFUSED:
+    return "Connection refused";
+  case ELOOP:
+    return "Cannot translate name";
+  case EHOSTDOWN:
+    return "Host is down";
+  case EHOSTUNREACH:
+    return "Host is unreachable";
+  case EPROCLIM:
+    return "Too many processes";
+  case EUSERS:
+    return "Too many users";
+  case EDQUOT:
+    return "Disk quota exceeded";
+  case ESTALE:
+    return "Stale file handle reference";
+  case EREMOTE:
+    return "Resource is remote";
+  case ENODATA:
+    return "No data available";
+  }
+  return strerror(errnum);
+}
+
+/**
+ * Return pointer to string description of last winsock error
+ * @return pointer to string description of last winsock error
+ */
+const char* MHD_W32_strerror_last_winsock_(void)
+{
+  switch (WSAGetLastError())
+    {
+  case 0:
+    return "No error";
+  case WSA_INVALID_HANDLE:
+    return "Specified event object handle is invalid";
+  case WSA_NOT_ENOUGH_MEMORY:
+    return "Insufficient memory available";
+  case WSA_INVALID_PARAMETER:
+    return "One or more parameters are invalid";
+  case WSA_OPERATION_ABORTED:
+    return "Overlapped operation aborted";
+  case WSA_IO_INCOMPLETE:
+    return "Overlapped I/O event object not in signaled state";
+  case WSA_IO_PENDING:
+    return "Overlapped operations will complete later";
+  case WSAEINTR:
+    return "Interrupted function call";
+  case WSAEBADF:
+    return "File handle is not valid";
+  case WSAEACCES:
+    return "Permission denied";
+  case WSAEFAULT:
+    return "Bad address";
+  case WSAEINVAL:
+    return "Invalid argument";
+  case WSAEMFILE:
+    return "Too many open files";
+  case WSAEWOULDBLOCK:
+    return "Resource temporarily unavailable";
+  case WSAEINPROGRESS:
+    return "Operation now in progress";
+  case WSAEALREADY:
+    return "Operation already in progress";
+  case WSAENOTSOCK:
+    return "Socket operation on nonsocket";
+  case WSAEDESTADDRREQ:
+    return "Destination address required";
+  case WSAEMSGSIZE:
+    return "Message too long";
+  case WSAEPROTOTYPE:
+    return "Protocol wrong type for socket";
+  case WSAENOPROTOOPT:
+    return "Bad protocol option";
+  case WSAEPROTONOSUPPORT:
+    return "Protocol not supported";
+  case WSAESOCKTNOSUPPORT:
+    return "Socket type not supported";
+  case WSAEOPNOTSUPP:
+    return "Operation not supported";
+  case WSAEPFNOSUPPORT:
+    return "Protocol family not supported";
+  case WSAEAFNOSUPPORT:
+    return "Address family not supported by protocol family";
+  case WSAEADDRINUSE:
+    return "Address already in use";
+  case WSAEADDRNOTAVAIL:
+    return "Cannot assign requested address";
+  case WSAENETDOWN:
+    return "Network is down";
+  case WSAENETUNREACH:
+    return "Network is unreachable";
+  case WSAENETRESET:
+    return "Network dropped connection on reset";
+  case WSAECONNABORTED:
+    return "Software caused connection abort";
+  case WSAECONNRESET:
+    return "Connection reset by peer";
+  case WSAENOBUFS:
+    return "No buffer space available";
+  case WSAEISCONN:
+    return "Socket is already connected";
+  case WSAENOTCONN:
+    return "Socket is not connected";
+  case WSAESHUTDOWN:
+    return "Cannot send after socket shutdown";
+  case WSAETOOMANYREFS:
+    return "Too many references";
+  case WSAETIMEDOUT:
+    return "Connection timed out";
+  case WSAECONNREFUSED:
+    return "Connection refused";
+  case WSAELOOP:
+    return "Cannot translate name";
+  case WSAENAMETOOLONG:
+    return "Name too long";
+  case WSAEHOSTDOWN:
+    return "Host is down";
+  case WSAEHOSTUNREACH:
+    return "No route to host";
+  case WSAENOTEMPTY:
+    return "Directory not empty";
+  case WSAEPROCLIM:
+    return "Too many processes";
+  case WSAEUSERS:
+    return "User quota exceeded";
+  case WSAEDQUOT:
+    return "Disk quota exceeded";
+  case WSAESTALE:
+    return "Stale file handle reference";
+  case WSAEREMOTE:
+    return "Item is remote";
+  case WSASYSNOTREADY:
+    return "Network subsystem is unavailable";
+  case WSAVERNOTSUPPORTED:
+    return "Winsock.dll version out of range";
+  case WSANOTINITIALISED:
+    return "Successful WSAStartup not yet performed";
+  case WSAEDISCON:
+    return "Graceful shutdown in progress";
+  case WSAENOMORE:
+    return "No more results";
+  case WSAECANCELLED:
+    return "Call has been canceled";
+  case WSAEINVALIDPROCTABLE:
+    return "Procedure call table is invalid";
+  case WSAEINVALIDPROVIDER:
+    return "Service provider is invalid";
+  case WSAEPROVIDERFAILEDINIT:
+    return "Service provider failed to initialize";
+  case WSASYSCALLFAILURE:
+    return "System call failure";
+  case WSASERVICE_NOT_FOUND:
+    return "Service not found";
+  case WSATYPE_NOT_FOUND:
+    return "Class type not found";
+  case WSA_E_NO_MORE:
+    return "No more results";
+  case WSA_E_CANCELLED:
+    return "Call was canceled";
+  case WSAEREFUSED:
+    return "Database query was refused";
+  case WSAHOST_NOT_FOUND:
+    return "Host not found";
+  case WSATRY_AGAIN:
+    return "Nonauthoritative host not found";
+  case WSANO_RECOVERY:
+    return "This is a nonrecoverable error";
+  case WSANO_DATA:
+    return "Valid name, no data record of requested type";
+  case WSA_QOS_RECEIVERS:
+    return "QoS receivers";
+  case WSA_QOS_SENDERS:
+    return "QoS senders";
+  case WSA_QOS_NO_SENDERS:
+    return "No QoS senders";
+  case WSA_QOS_NO_RECEIVERS:
+    return "QoS no receivers";
+  case WSA_QOS_REQUEST_CONFIRMED:
+    return "QoS request confirmed";
+  case WSA_QOS_ADMISSION_FAILURE:
+    return "QoS admission error";
+  case WSA_QOS_POLICY_FAILURE:
+    return "QoS policy failure";
+  case WSA_QOS_BAD_STYLE:
+    return "QoS bad style";
+  case WSA_QOS_BAD_OBJECT:
+    return "QoS bad object";
+  case WSA_QOS_TRAFFIC_CTRL_ERROR:
+    return "QoS traffic control error";
+  case WSA_QOS_GENERIC_ERROR:
+    return "QoS generic error";
+  case WSA_QOS_ESERVICETYPE:
+    return "QoS service type error";
+  case WSA_QOS_EFLOWSPEC:
+    return "QoS flowspec error";
+  case WSA_QOS_EPROVSPECBUF:
+    return "Invalid QoS provider buffer";
+  case WSA_QOS_EFILTERSTYLE:
+    return "Invalid QoS filter style";
+  case WSA_QOS_EFILTERTYPE:
+    return "Invalid QoS filter type";
+  case WSA_QOS_EFILTERCOUNT:
+    return "Incorrect QoS filter count";
+  case WSA_QOS_EOBJLENGTH:
+    return "Invalid QoS object length";
+  case WSA_QOS_EFLOWCOUNT:
+    return "Incorrect QoS flow count";
+  case WSA_QOS_EUNKOWNPSOBJ:
+    return "Unrecognized QoS object";
+  case WSA_QOS_EPOLICYOBJ:
+    return "Invalid QoS policy object";
+  case WSA_QOS_EFLOWDESC:
+    return "Invalid QoS flow descriptor";
+  case WSA_QOS_EPSFLOWSPEC:
+    return "Invalid QoS provider-specific flowspec";
+  case WSA_QOS_EPSFILTERSPEC:
+    return "Invalid QoS provider-specific filterspec";
+  case WSA_QOS_ESDMODEOBJ:
+    return "Invalid QoS shape discard mode object";
+  case WSA_QOS_ESHAPERATEOBJ:
+    return "Invalid QoS shaping rate object";
+  case WSA_QOS_RESERVED_PETYPE:
+    return "Reserved policy QoS element type";
+    }
+  return "Unknown winsock error";
+}
+
+/**
+ * Set last winsock error to equivalent of given errno value
+ * @param errnum the errno value to set
+ */
+void MHD_W32_set_last_winsock_error_(int errnum)
+{
+  switch (errnum)
+    {
+  case 0:
+    WSASetLastError(0);
+    break;
+  case EBADF:
+    WSASetLastError(WSA_INVALID_HANDLE);
+    break;
+  case ENOMEM:
+    WSASetLastError(WSA_NOT_ENOUGH_MEMORY);
+    break;
+  case EINVAL:
+    WSASetLastError(WSA_INVALID_PARAMETER);
+    break;
+  case EINTR:
+    WSASetLastError(WSAEINTR);
+    break;
+  case EWOULDBLOCK:
+    WSASetLastError(WSAEWOULDBLOCK);
+    break;
+  case EINPROGRESS:
+    WSASetLastError(WSAEINPROGRESS);
+    break;
+  case EALREADY:
+    WSASetLastError(WSAEALREADY);
+    break;
+  case ENOTSOCK:
+    WSASetLastError(WSAENOTSOCK);
+    break;
+  case EDESTADDRREQ:
+    WSASetLastError(WSAEDESTADDRREQ);
+    break;
+  case EMSGSIZE:
+    WSASetLastError(WSAEMSGSIZE);
+    break;
+  case EPROTOTYPE:
+    WSASetLastError(WSAEPROTOTYPE);
+    break;
+  case ENOPROTOOPT:
+    WSASetLastError(WSAENOPROTOOPT);
+    break;
+  case EPROTONOSUPPORT:
+    WSASetLastError(WSAEPROTONOSUPPORT);
+    break;
+  case ESOCKTNOSUPPORT:
+    WSASetLastError(WSAESOCKTNOSUPPORT);
+    break;
+  case EOPNOTSUPP:
+    WSASetLastError(WSAEOPNOTSUPP);
+    break;
+  case EPFNOSUPPORT:
+    WSASetLastError(WSAEPFNOSUPPORT);
+    break;
+  case EAFNOSUPPORT:
+    WSASetLastError(WSAEAFNOSUPPORT);
+    break;
+  case EADDRINUSE:
+    WSASetLastError(WSAEADDRINUSE);
+    break;
+  case EADDRNOTAVAIL:
+    WSASetLastError(WSAEADDRNOTAVAIL);
+    break;
+  case ENETDOWN:
+    WSASetLastError(WSAENETDOWN);
+    break;
+  case ENETUNREACH:
+    WSASetLastError(WSAENETUNREACH);
+    break;
+  case ENETRESET:
+    WSASetLastError(WSAENETRESET);
+    break;
+  case ECONNABORTED:
+    WSASetLastError(WSAECONNABORTED);
+    break;
+  case ECONNRESET:
+    WSASetLastError(WSAECONNRESET);
+    break;
+  case ENOBUFS:
+    WSASetLastError(WSAENOBUFS);
+    break;
+  case EISCONN:
+    WSASetLastError(WSAEISCONN);
+    break;
+  case ENOTCONN:
+    WSASetLastError(WSAENOTCONN);
+    break;
+  case ESHUTDOWN:
+    WSASetLastError(WSAESHUTDOWN);
+    break;
+  case ETOOMANYREFS:
+    WSASetLastError(WSAETOOMANYREFS);
+    break;
+  case ETIMEDOUT:
+    WSASetLastError(WSAETIMEDOUT);
+    break;
+  case ECONNREFUSED:
+    WSASetLastError(WSAECONNREFUSED);
+    break;
+  case ELOOP:
+    WSASetLastError(WSAELOOP);
+    break;
+  case ENAMETOOLONG:
+    WSASetLastError(WSAENAMETOOLONG);
+    break;
+  case EHOSTDOWN:
+    WSASetLastError(WSAEHOSTDOWN);
+    break;
+  case EHOSTUNREACH:
+    WSASetLastError(WSAEHOSTUNREACH);
+    break;
+  case ENOTEMPTY:
+    WSASetLastError(WSAENOTEMPTY);
+    break;
+  case EPROCLIM:
+    WSASetLastError(WSAEPROCLIM);
+    break;
+  case EUSERS:
+    WSASetLastError(WSAEUSERS);
+    break;
+  case EDQUOT:
+    WSASetLastError(WSAEDQUOT);
+    break;
+  case ESTALE:
+    WSASetLastError(WSAESTALE);
+    break;
+  case EREMOTE:
+    WSASetLastError(WSAEREMOTE);
+    break;
+  case EFAULT:
+    WSASetLastError(WSAEFAULT);
+    break;
+  case ENODATA:
+    WSASetLastError(WSANO_DATA);
+    break;
+#if EAGAIN != EWOULDBLOCK
+  case EAGAIN:
+    WSASetLastError(WSAEWOULDBLOCK);
+    break;
+#endif
+  /* Rough equivalent */
+  case EIO:
+    WSASetLastError(WSAEREFUSED);
+    break;
+
+  default: /* Unmapped errors */
+    WSASetLastError(WSAENOBUFS);
+    break;
+    }
+}
+
+/**
+ * Create pair of mutually connected TCP/IP sockets on loopback address
+ * @param sockets_pair array to receive resulted sockets
+ * @return zero on success, -1 otherwise
+ */
+int MHD_W32_pair_of_sockets_(SOCKET sockets_pair[2])
+{
+  int i;
+  if (!sockets_pair)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+#define PAIRMAXTRYIES 800
+  for (i = 0; i < PAIRMAXTRYIES; i++)
+    {
+      struct sockaddr_in listen_addr;
+      SOCKET listen_s;
+      static const int c_addinlen = sizeof(struct sockaddr_in); /* help compiler to optimize */
+      int addr_len = c_addinlen;
+      int opt = 1;
+
+      listen_s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+      if (INVALID_SOCKET == listen_s)
+        break; /* can't create even single socket */
+
+      listen_addr.sin_family = AF_INET;
+      listen_addr.sin_port = htons(0);
+      listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+      if (0 == bind(listen_s, (struct sockaddr*) &listen_addr, c_addinlen)
+          && 0 == listen(listen_s, 1)
+          && 0 == getsockname(listen_s, (struct sockaddr*) &listen_addr,
+                  &addr_len))
+        {
+          SOCKET client_s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+          if (INVALID_SOCKET != client_s)
+            {
+              if (0 == ioctlsocket(client_s, FIONBIO, (u_long*) &opt)
+                  && (0 == connect(client_s, (struct sockaddr*) &listen_addr, c_addinlen)
+                      || WSAGetLastError() == WSAEWOULDBLOCK))
+                {
+                  struct sockaddr_in accepted_from_addr;
+                  addr_len = c_addinlen;
+                  SOCKET server_s = accept(listen_s,
+                      (struct sockaddr*) &accepted_from_addr, &addr_len);
+                  if (INVALID_SOCKET != server_s)
+                    {
+                      struct sockaddr_in client_addr;
+                      addr_len = c_addinlen;
+                      opt = 0;
+                      if (0 == getsockname(client_s, (struct sockaddr*) &client_addr, &addr_len)
+                          && accepted_from_addr.sin_family == client_addr.sin_family
+                          && accepted_from_addr.sin_port == client_addr.sin_port
+                          && accepted_from_addr.sin_addr.s_addr == client_addr.sin_addr.s_addr
+                          && 0 == ioctlsocket(client_s, FIONBIO, (u_long*) &opt)
+                          && 0 == ioctlsocket(server_s, FIONBIO, (u_long*) &opt))
+                        {
+                          closesocket(listen_s);
+                          sockets_pair[0] = client_s;
+                          sockets_pair[1] = server_s;
+                          return 0;
+                        }
+                      closesocket(server_s);
+                    }
+                }
+              closesocket(client_s);
+            }
+        }
+      closesocket(listen_s);
+    }
+
+  sockets_pair[0] = INVALID_SOCKET;
+  sockets_pair[1] = INVALID_SOCKET;
+  return -1;
+}
+
+/**
+ * Static variable used by pseudo random number generator
+ */
+static int32_t rnd_val = 0;
+/**
+ * Generate 31-bit pseudo random number.
+ * Function initialize itself at first call to current time.
+ * @return 31-bit pseudo random number.
+ */
+int MHD_W32_random_(void)
+{
+  if (0 == rnd_val)
+    rnd_val = (int32_t)time(NULL);
+  /* stolen from winsup\cygwin\random.cc */
+  rnd_val = (16807 * (rnd_val % 127773) - 2836 * (rnd_val / 127773))
+               & 0x7fffffff;
+  return (int)rnd_val;
+}
+
+/* Emulate snprintf function on W32 */
+int W32_snprintf(char *__restrict s, size_t n, const char *__restrict format, ...)
+{
+  int ret;
+  va_list args;
+  if (0 != n && NULL != s )
+  {
+    va_start(args, format);
+    ret = _vsnprintf(s, n, format, args);
+    va_end(args);
+    if (n == ret)
+      s[n - 1] = 0;
+    if (ret >= 0)
+      return ret;
+  }
+  va_start(args, format);
+  ret = _vscprintf(format, args);
+  va_end(args);
+  if (0 <= ret && 0 != n && NULL == s)
+    return -1;
+
+  return ret;
+}
+
+#ifdef _MSC_FULL_VER
+/**
+ * Set thread name
+ * @param thread_id ID of thread, -1 for current thread
+ * @param thread_name name to set
+ */
+void W32_SetThreadName(const DWORD thread_id, const char *thread_name)
+{
+  static const DWORD VC_SETNAME_EXC = 0x406D1388;
+#pragma pack(push,8)
+  struct thread_info_struct
+  {
+    DWORD type;   // Must be 0x1000.
+    LPCSTR name;  // Pointer to name (in user address space).
+    DWORD ID;     // Thread ID (-1=caller thread).
+    DWORD flags;  // Reserved for future use, must be zero.
+  } thread_info;
+#pragma pack(pop)
+
+  if (NULL == thread_name)
+    return;
+
+  thread_info.type  = 0x1000;
+  thread_info.name  = thread_name;
+  thread_info.ID    = thread_id;
+  thread_info.flags = 0;
+
+  __try
+  { /* This exception is intercepted by debugger */
+    RaiseException(VC_SETNAME_EXC, 0, sizeof(thread_info) / sizeof(ULONG_PTR), (ULONG_PTR*)&thread_info);
+  }
+  __except (EXCEPTION_EXECUTE_HANDLER)
+  {}
+}
+#endif /* _MSC_FULL_VER */
diff --git a/src/spdy2http/Makefile.am b/src/spdy2http/Makefile.am
new file mode 100644
index 0000000..f7432a9
--- /dev/null
+++ b/src/spdy2http/Makefile.am
@@ -0,0 +1,29 @@
+# This Makefile.am is in the public domain
+SUBDIRS  = .
+
+AM_CFLAGS =
+
+if USE_COVERAGE
+  AM_CFLAGS += -fprofile-arcs -ftest-coverage
+endif
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/src/include \
+ -I$(top_srcdir)/src/applicationlayer \
+ -DDATA_DIR=\"$(top_srcdir)/src/datadir/\" \
+ $(LIBCURL_CPPFLAGS)
+
+if !HAVE_W32
+PERF_GET_CONCURRENT=perf_get_concurrent
+endif
+
+bin_PROGRAMS = \
+ microspdy2http
+
+microspdy2http_SOURCES = \
+ proxy.c 
+microspdy2http_LDADD = \
+  $(top_builddir)/src/microspdy/libmicrospdy.la \
+ -lz \
+ $(LIBCURL)
diff --git a/src/spdy2http/Makefile.in b/src/spdy2http/Makefile.in
new file mode 100644
index 0000000..ef67ffc
--- /dev/null
+++ b/src/spdy2http/Makefile.in
@@ -0,0 +1,813 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 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 = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+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@
+@USE_COVERAGE_TRUE@am__append_1 = -fprofile-arcs -ftest-coverage
+bin_PROGRAMS = microspdy2http$(EXEEXT)
+subdir = src/spdy2http
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_compile_flags.m4 \
+	$(top_srcdir)/m4/ax_append_flag.m4 \
+	$(top_srcdir)/m4/ax_check_compile_flag.m4 \
+	$(top_srcdir)/m4/ax_check_link_flag.m4 \
+	$(top_srcdir)/m4/ax_check_openssl.m4 \
+	$(top_srcdir)/m4/ax_count_cpus.m4 \
+	$(top_srcdir)/m4/ax_have_epoll.m4 \
+	$(top_srcdir)/m4/ax_pthread.m4 \
+	$(top_srcdir)/m4/ax_require_defined.m4 \
+	$(top_srcdir)/m4/libcurl.m4 $(top_srcdir)/m4/libgcrypt.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)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/MHD_config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_microspdy2http_OBJECTS = proxy.$(OBJEXT)
+microspdy2http_OBJECTS = $(am_microspdy2http_OBJECTS)
+am__DEPENDENCIES_1 =
+microspdy2http_DEPENDENCIES =  \
+	$(top_builddir)/src/microspdy/libmicrospdy.la \
+	$(am__DEPENDENCIES_1)
+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__depfiles_maybe = depfiles
+am__mv = mv -f
+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 = $(microspdy2http_SOURCES)
+DIST_SOURCES = $(microspdy2http_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-recursive dvi-recursive html-recursive info-recursive \
+	install-data-recursive install-dvi-recursive \
+	install-exec-recursive install-html-recursive \
+	install-info-recursive install-pdf-recursive \
+	install-ps-recursive install-recursive installcheck-recursive \
+	installdirs-recursive pdf-recursive ps-recursive \
+	tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	distdir
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPU_COUNT = @CPU_COUNT@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_CPPFLAGS = @GNUTLS_CPPFLAGS@
+GNUTLS_LDFLAGS = @GNUTLS_LDFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+HAVE_CURL_BINARY = @HAVE_CURL_BINARY@
+HAVE_MAKEINFO_BINARY = @HAVE_MAKEINFO_BINARY@
+HIDDEN_VISIBILITY_CFLAGS = @HIDDEN_VISIBILITY_CFLAGS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSPDY_VERSION_AGE = @LIBSPDY_VERSION_AGE@
+LIBSPDY_VERSION_CURRENT = @LIBSPDY_VERSION_CURRENT@
+LIBSPDY_VERSION_REVISION = @LIBSPDY_VERSION_REVISION@
+LIBTOOL = @LIBTOOL@
+LIB_VERSION_AGE = @LIB_VERSION_AGE@
+LIB_VERSION_CURRENT = @LIB_VERSION_CURRENT@
+LIB_VERSION_REVISION = @LIB_VERSION_REVISION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MHD_LIBDEPS = @MHD_LIBDEPS@
+MHD_LIBDEPS_PKGCFG = @MHD_LIBDEPS_PKGCFG@
+MHD_LIB_CFLAGS = @MHD_LIB_CFLAGS@
+MHD_LIB_CPPFLAGS = @MHD_LIB_CPPFLAGS@
+MHD_LIB_LDFLAGS = @MHD_LIB_LDFLAGS@
+MHD_REQ_PRIVATE = @MHD_REQ_PRIVATE@
+MKDIR_P = @MKDIR_P@
+MS_LIB_TOOL = @MS_LIB_TOOL@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_INCLUDES = @OPENSSL_INCLUDES@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@
+PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@
+PACKAGE_VERSION_SUBMINOR = @PACKAGE_VERSION_SUBMINOR@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPDY_LIBDEPS = @SPDY_LIBDEPS@
+SPDY_LIB_CFLAGS = @SPDY_LIB_CFLAGS@
+SPDY_LIB_CPPFLAGS = @SPDY_LIB_CPPFLAGS@
+SPDY_LIB_LDFLAGS = @SPDY_LIB_LDFLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+_libcurl_config = @_libcurl_config@
+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_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@
+ax_pthread_config = @ax_pthread_config@
+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@
+have_socat = @have_socat@
+have_zzuf = @have_zzuf@
+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@
+lt_cv_objdir = @lt_cv_objdir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# This Makefile.am is in the public domain
+SUBDIRS = .
+AM_CFLAGS = $(am__append_1)
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/src/include \
+ -I$(top_srcdir)/src/applicationlayer \
+ -DDATA_DIR=\"$(top_srcdir)/src/datadir/\" \
+ $(LIBCURL_CPPFLAGS)
+
+@HAVE_W32_FALSE@PERF_GET_CONCURRENT = perf_get_concurrent
+microspdy2http_SOURCES = \
+ proxy.c 
+
+microspdy2http_LDADD = \
+  $(top_builddir)/src/microspdy/libmicrospdy.la \
+ -lz \
+ $(LIBCURL)
+
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .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) --gnu src/spdy2http/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/spdy2http/Makefile
+.PRECIOUS: 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__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	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):
+install-binPROGRAMS: $(bin_PROGRAMS)
+	@$(NORMAL_INSTALL)
+	@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+	fi; \
+	for p in $$list; do echo "$$p $$p"; done | \
+	sed 's/$(EXEEXT)$$//' | \
+	while read p p1; do if test -f $$p \
+	 || test -f $$p1 \
+	  ; then echo "$$p"; echo "$$p"; else :; fi; \
+	done | \
+	sed -e 'p;s,.*/,,;n;h' \
+	    -e 's|.*|.|' \
+	    -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+	sed 'N;N;N;s,\n, ,g' | \
+	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+	  { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+	    if ($$2 == $$4) files[d] = files[d] " " $$1; \
+	    else { print "f", $$3 "/" $$4, $$1; } } \
+	  END { for (d in files) print "f", d, files[d] }' | \
+	while read type dir files; do \
+	    if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+	    test -z "$$files" || { \
+	    echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+	    $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+	    } \
+	; done
+
+uninstall-binPROGRAMS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+	files=`for p in $$list; do echo "$$p"; done | \
+	  sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+	      -e 's/$$/$(EXEEXT)/' \
+	`; \
+	test -n "$$list" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+	@list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+
+microspdy2http$(EXEEXT): $(microspdy2http_OBJECTS) $(microspdy2http_DEPENDENCIES) $(EXTRA_microspdy2http_DEPENDENCIES) 
+	@rm -f microspdy2http$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(microspdy2http_OBJECTS) $(microspdy2http_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proxy.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+#     (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    $(am__make_dryrun) \
+	      || test -d "$(distdir)/$$subdir" \
+	      || $(MKDIR_P) "$(distdir)/$$subdir" \
+	      || exit 1; \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+	for dir in "$(DESTDIR)$(bindir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+	check-am clean clean-binPROGRAMS clean-generic clean-libtool \
+	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-binPROGRAMS install-data install-data-am install-dvi \
+	install-dvi-am install-exec install-exec-am install-html \
+	install-html-am install-info install-info-am install-man \
+	install-pdf install-pdf-am install-ps install-ps-am \
+	install-strip installcheck installcheck-am installdirs \
+	installdirs-am maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+	uninstall-am uninstall-binPROGRAMS
+
+
+# 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/src/spdy2http/proxy.c b/src/spdy2http/proxy.c
new file mode 100644
index 0000000..02144ad
--- /dev/null
+++ b/src/spdy2http/proxy.c
@@ -0,0 +1,1411 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file proxy.c
+ * @brief   Translates incoming SPDY requests to http server on localhost.
+ * 			Uses libcurl.
+ * 			No error handling for curl requests.
+ *      TODO:
+ * - test all options!
+ * - don't abort on lack of memory
+ * - Correct recapitalizetion of header names before giving the headers
+ * to curl.
+ * - curl does not close sockets when connection is closed and no
+ * new sockets are opened (they stay in CLOSE_WAIT)
+ * - add '/' when a user requests http://example.com . Now this is a bad
+ * request
+ * - curl returns 0 or 1 ms for timeout even when nothing will be done;
+ * thus the loop uses CPU for nothing
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include "microspdy.h"
+#include <curl/curl.h>
+#include <assert.h>
+#include <getopt.h>
+#include <regex.h>
+
+#define ERROR_RESPONSE "502 Bad Gateway"
+
+
+struct global_options
+{
+  char *http_backend;
+  char *cert;
+  char *cert_key;
+  char *listen_host;
+  unsigned int timeout;
+  uint16_t listen_port;
+  bool verbose;
+  bool curl_verbose;
+  bool transparent;
+  bool http10;
+  bool notls;
+  bool nodelay;
+  bool ipv4;
+  bool ipv6;
+} glob_opt;
+
+
+struct URI
+{
+  char * full_uri;
+  char * scheme;
+  char * host_and_port;
+  //char * host_and_port_for_connecting;
+  char * host;
+  char * path;
+  char * path_and_more;
+  char * query;
+  char * fragment;
+  uint16_t port;
+};
+
+
+#define PRINT_INFO(msg) do{\
+	fprintf(stdout, "%i:%s\n", __LINE__, msg);\
+	fflush(stdout);\
+	}\
+	while(0)
+
+
+#define PRINT_INFO2(fmt, ...) do{\
+	fprintf(stdout, "%i\n", __LINE__);\
+	fprintf(stdout, fmt,##__VA_ARGS__);\
+	fprintf(stdout, "\n");\
+	fflush(stdout);\
+	}\
+	while(0)
+
+
+#define PRINT_VERBOSE(msg) do{\
+  if(glob_opt.verbose){\
+	fprintf(stdout, "%i:%s\n", __LINE__, msg);\
+	fflush(stdout);\
+	}\
+  }\
+	while(0)
+
+
+#define PRINT_VERBOSE2(fmt, ...) do{\
+  if(glob_opt.verbose){\
+	fprintf(stdout, "%i\n", __LINE__);\
+	fprintf(stdout, fmt,##__VA_ARGS__);\
+	fprintf(stdout, "\n");\
+	fflush(stdout);\
+	}\
+	}\
+	while(0)
+
+
+#define CURL_SETOPT(handle, opt, val) do{\
+	int ret; \
+	if(CURLE_OK != (ret = curl_easy_setopt(handle, opt, val))) \
+	{ \
+		PRINT_INFO2("curl_easy_setopt failed (%i = %i)", opt, ret); \
+		abort(); \
+	} \
+	}\
+	while(0)
+
+
+#define DIE(msg) do{\
+	printf("FATAL ERROR (line %i): %s\n", __LINE__, msg);\
+	fflush(stdout);\
+  exit(EXIT_FAILURE);\
+	}\
+	while(0)
+
+
+static int loop = 1;
+
+static CURLM *multi_handle;
+
+static int still_running = 0; /* keep number of running handles */
+
+static regex_t uri_preg;
+
+static bool call_spdy_run;
+static bool call_curl_run;
+
+int debug_num_curls;
+
+
+struct Proxy
+{
+	char *url;
+	struct SPDY_Request *request;
+	struct SPDY_Response *response;
+	CURL *curl_handle;
+	struct curl_slist *curl_headers;
+	struct SPDY_NameValue *headers;
+	char *version;
+	char *status_msg;
+	void *http_body;
+	void *received_body;
+  bool *session_alive;
+	size_t http_body_size;
+	size_t received_body_size;
+	//ssize_t length;
+	int status;
+  //bool done;
+  bool receiving_done;
+  bool is_curl_read_paused;
+  bool is_with_body_data;
+  //bool error;
+  bool curl_done;
+  bool curl_error;
+  bool spdy_done;
+  bool spdy_error;
+};
+
+
+static void
+free_uri(struct URI * uri)
+{
+  if(NULL != uri)
+  {
+    free(uri->full_uri);
+    free(uri->scheme);
+    free(uri->host_and_port);
+    //free(uri->host_and_port_for_connecting);
+    free(uri->host);
+    free(uri->path);
+    free(uri->path_and_more);
+    free(uri->query);
+    free(uri->fragment);
+    uri->port = 0;
+    free(uri);
+  }
+}
+
+
+static int
+init_parse_uri(regex_t * preg)
+{
+  // RFC 2396
+  // ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
+      /*
+        scheme    = $2
+      authority = $4
+      path      = $5
+      query     = $7
+      fragment  = $9
+      */
+
+  return regcomp(preg, "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?", REG_EXTENDED);
+}
+
+
+static void
+deinit_parse_uri(regex_t * preg)
+{
+  regfree(preg);
+}
+
+
+static int
+parse_uri(regex_t * preg, const char * full_uri, struct URI ** uri)
+{
+  //TODO memeory checks
+  int ret;
+  char *colon;
+  long long port;
+  size_t nmatch = 10;
+  regmatch_t pmatch[10];
+
+  if (0 != (ret = regexec(preg, full_uri, nmatch, pmatch, 0)))
+    return ret;
+
+  *uri = malloc(sizeof(struct URI));
+  if(NULL == *uri)
+    return -200;
+
+  (*uri)->full_uri = strdup(full_uri);
+
+  asprintf(&((*uri)->scheme),
+	   "%.*s",
+	   (int) (pmatch[2].rm_eo - pmatch[2].rm_so),
+	   &full_uri[pmatch[2].rm_so]);
+  asprintf(&((*uri)->host_and_port), "%.*s",
+	   (int) (pmatch[4].rm_eo - pmatch[4].rm_so),
+	   &full_uri[pmatch[4].rm_so]);
+  asprintf(&((*uri)->path),
+	   "%.*s",
+	   (int) (pmatch[5].rm_eo - pmatch[5].rm_so),
+	   &full_uri[pmatch[5].rm_so]);
+  asprintf(&((*uri)->path_and_more),
+	   "%.*s",
+	   (int) (pmatch[9].rm_eo - pmatch[5].rm_so),
+	   &full_uri[pmatch[5].rm_so]);
+  asprintf(&((*uri)->query),
+	   "%.*s",
+	   (int) (pmatch[7].rm_eo - pmatch[7].rm_so),
+	   &full_uri[pmatch[7].rm_so]);
+  asprintf(&((*uri)->fragment),
+	   "%.*s",
+	   (int) (pmatch[9].rm_eo - pmatch[9].rm_so),
+	   &full_uri[pmatch[9].rm_so]);
+
+  colon = strrchr((*uri)->host_and_port, ':');
+  if(NULL == colon)
+  {
+    (*uri)->host = strdup((*uri)->host_and_port);
+    /*if(0 == strcasecmp("http", uri->scheme))
+    {
+      uri->port = 80;
+      asprintf(&(uri->host_and_port_for_connecting), "%s:80", uri->host_and_port);
+    }
+    else if(0 == strcasecmp("https", uri->scheme))
+    {
+      uri->port = 443;
+      asprintf(&(uri->host_and_port_for_connecting), "%s:443", uri->host_and_port);
+    }
+    else
+    {
+      PRINT_INFO("no standard scheme!");
+      */(*uri)->port = 0;
+      /*uri->host_and_port_for_connecting = strdup(uri->host_and_port);
+    }*/
+    return 0;
+  }
+
+  port = atoi(colon  + 1);
+  if(port<1 || port >= 256 * 256)
+  {
+    free_uri(*uri);
+    return -100;
+  }
+  (*uri)->port = port;
+  asprintf(&((*uri)->host), "%.*s", (int)(colon - (*uri)->host_and_port), (*uri)->host_and_port);
+
+  return 0;
+}
+
+
+static bool
+store_in_buffer(const void *src, size_t src_size, void **dst, size_t *dst_size)
+{
+  if(0 == src_size)
+    return true;
+
+  if(NULL == *dst)
+		*dst = malloc(src_size);
+	else
+		*dst = realloc(*dst, src_size + *dst_size);
+	if(NULL == *dst)
+		return false;
+
+	memcpy(*dst + *dst_size, src, src_size);
+	*dst_size += src_size;
+
+  return true;
+}
+
+
+static ssize_t
+get_from_buffer(void **src, size_t *src_size, void *dst, size_t max_size)
+{
+  size_t ret;
+  void *newbody;
+
+	if(max_size >= *src_size)
+	{
+		ret = *src_size;
+		newbody = NULL;
+	}
+	else
+	{
+		ret = max_size;
+		if(NULL == (newbody = malloc(*src_size - max_size)))
+			return -1;
+		memcpy(newbody, *src + ret, *src_size - ret);
+	}
+	memcpy(dst, *src, ret);
+	free(*src);
+	*src = newbody;
+	*src_size -= ret;
+
+  return ret;
+}
+
+
+static void
+catch_signal(int signal)
+{
+  (void)signal;
+
+  loop = 0;
+}
+
+static void
+new_session_cb (void * cls,
+							struct SPDY_Session * session)
+{
+  (void)cls;
+
+  bool *session_alive;
+
+  PRINT_VERBOSE("new session");
+  //TODO clean this memory
+  if(NULL == (session_alive = malloc(sizeof(bool))))
+  {
+			DIE("no memory");
+  }
+  *session_alive = true;
+  SPDY_set_cls_to_session(session,
+						session_alive);
+}
+
+static void
+session_closed_cb (void * cls,
+								struct SPDY_Session * session,
+								int by_client)
+{
+  (void)cls;
+
+  bool *session_alive;
+
+  PRINT_VERBOSE2("session closed; by client: %i", by_client);
+
+  session_alive = SPDY_get_cls_from_session(session);
+  assert(NULL != session_alive);
+
+  *session_alive = false;
+}
+
+
+static int
+spdy_post_data_cb (void * cls,
+					 struct SPDY_Request *request,
+					 const void * buf,
+					 size_t size,
+					 bool more)
+{
+  (void)cls;
+  int ret;
+	struct Proxy *proxy = (struct Proxy *)SPDY_get_cls_from_request(request);
+
+  if(!store_in_buffer(buf, size, &proxy->received_body, &proxy->received_body_size))
+	{
+		PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
+		return 0;
+	}
+
+  proxy->receiving_done = !more;
+
+  PRINT_VERBOSE2("POST bytes from SPDY: %zu", size);
+
+  call_curl_run = true;
+
+  if(proxy->is_curl_read_paused)
+  {
+    if(CURLE_OK != (ret = curl_easy_pause(proxy->curl_handle, CURLPAUSE_CONT)))
+    {
+      PRINT_INFO2("curl_easy_pause returned %i", ret);
+      abort();
+    }
+    PRINT_VERBOSE("curl_read_cb pause resumed");
+  }
+
+  return SPDY_YES;
+}
+
+
+ssize_t
+response_callback (void *cls,
+						void *buffer,
+						size_t max,
+						bool *more)
+{
+	ssize_t ret;
+	struct Proxy *proxy = (struct Proxy *)cls;
+
+  *more = true;
+
+  assert(!proxy->spdy_error);
+
+  if(proxy->curl_error)
+  {
+    PRINT_VERBOSE("tell spdy about the error");
+    return -1;
+  }
+
+	if(!proxy->http_body_size)//nothing to write now
+  {
+    PRINT_VERBOSE("nothing to write now");
+    if(proxy->curl_done || proxy->curl_error) *more = false;
+		return 0;
+  }
+
+  ret = get_from_buffer(&(proxy->http_body), &(proxy->http_body_size), buffer, max);
+  if(ret < 0)
+  {
+    PRINT_INFO("no memory");
+    //TODO error?
+    return -1;
+  }
+
+  if((proxy->curl_done || proxy->curl_error) && 0 == proxy->http_body_size) *more = false;
+
+  PRINT_VERBOSE2("given bytes to microspdy: %zd", ret);
+
+	return ret;
+}
+
+
+static void
+cleanup(struct Proxy *proxy)
+{
+  int ret;
+
+  //fprintf(stderr, "free proxy for %s\n", proxy->url);
+
+	if(CURLM_OK != (ret = curl_multi_remove_handle(multi_handle, proxy->curl_handle)))
+	{
+		PRINT_INFO2("curl_multi_remove_handle failed (%i)", ret);
+    DIE("bug in cleanup");
+	}
+  debug_num_curls--;
+  //TODO bug on ku6.com or amazon.cn
+  // after curl_multi_remove_handle returned CURLM_BAD_EASY_HANDLE
+	curl_slist_free_all(proxy->curl_headers);
+	curl_easy_cleanup(proxy->curl_handle);
+
+	free(proxy->url);
+	free(proxy);
+}
+
+
+static void
+response_done_callback(void *cls,
+						struct SPDY_Response *response,
+						struct SPDY_Request *request,
+						enum SPDY_RESPONSE_RESULT status,
+						bool streamopened)
+{
+	(void)streamopened;
+	struct Proxy *proxy = (struct Proxy *)cls;
+
+	if(SPDY_RESPONSE_RESULT_SUCCESS != status)
+	{
+    free(proxy->http_body);
+    proxy->http_body = NULL;
+    proxy->spdy_error = true;
+	}
+	cleanup(proxy);
+	SPDY_destroy_request(request);
+	SPDY_destroy_response(response);
+}
+
+
+static size_t
+curl_header_cb(void *ptr, size_t size, size_t nmemb, void *userp)
+{
+	size_t realsize = size * nmemb;
+	struct Proxy *proxy = (struct Proxy *)userp;
+	char *line = (char *)ptr;
+	char *name;
+	char *value;
+	char *status;
+	unsigned int i;
+	unsigned int pos;
+	int ret;
+  int num_values;
+  const char * const * values;
+  bool abort_it;
+
+	//printf("curl_header_cb %s\n", line);
+  if(!*(proxy->session_alive))
+  {
+    PRINT_VERBOSE("headers received, but session is dead");
+    proxy->spdy_error = true;
+    proxy->curl_error = true;
+    return 0;
+  }
+
+  //trailer
+  if(NULL != proxy->response) return 0;
+
+	if('\r' == line[0] || '\n' == line[0])
+	{
+		//all headers were already handled; prepare spdy frames
+		if(NULL == (proxy->response = SPDY_build_response_with_callback(proxy->status,
+							proxy->status_msg,
+							proxy->version,
+							proxy->headers,
+							&response_callback,
+							proxy,
+							0)))
+							//256)))
+			DIE("no response");
+
+		SPDY_name_value_destroy(proxy->headers);
+    proxy->headers = NULL;
+		free(proxy->status_msg);
+    proxy->status_msg = NULL;
+		free(proxy->version);
+    proxy->version = NULL;
+
+		if(SPDY_YES != SPDY_queue_response(proxy->request,
+							proxy->response,
+							true,
+							false,
+							&response_done_callback,
+							proxy))
+    {
+			//DIE("no queue");
+      //TODO right?
+      proxy->spdy_error = true;
+      proxy->curl_error = true;
+      PRINT_VERBOSE2("no queue in curl_header_cb for %s", proxy->url);
+      SPDY_destroy_response(proxy->response);
+      proxy->response = NULL;
+      return 0;
+    }
+
+    call_spdy_run = true;
+
+		return realsize;
+	}
+
+	pos = 0;
+	if(NULL == proxy->version)
+	{
+		//first line from headers
+		//version
+		for(i=pos; i<realsize && ' '!=line[i]; ++i);
+		if(i == realsize)
+			DIE("error on parsing headers");
+		if(NULL == (proxy->version = strndup(line, i - pos)))
+        DIE("No memory");
+		pos = i+1;
+
+		//status (number)
+		for(i=pos; i<realsize && ' '!=line[i] && '\r'!=line[i] && '\n'!=line[i]; ++i);
+		if(NULL == (status = strndup(&(line[pos]), i - pos)))
+        DIE("No memory");
+		proxy->status = atoi(status);
+		free(status);
+		if(i<realsize && '\r'!=line[i] && '\n'!=line[i])
+		{
+			//status (message)
+			pos = i+1;
+			for(i=pos; i<realsize && '\r'!=line[i] && '\n'!=line[i]; ++i);
+			if(NULL == (proxy->status_msg = strndup(&(line[pos]), i - pos)))
+        DIE("No memory");
+		}
+    PRINT_VERBOSE2("Header line received '%s' '%i' '%s' ", proxy->version, proxy->status, proxy->status_msg);
+		return realsize;
+	}
+
+	//other lines
+	//header name
+	for(i=pos; i<realsize && ':'!=line[i] && '\r'!=line[i] && '\n'!=line[i]; ++i)
+		line[i] = tolower(line[i]); //spdy requires lower case
+	if(NULL == (name = strndup(line, i - pos)))
+        DIE("No memory");
+	if(0 == strcmp(SPDY_HTTP_HEADER_CONNECTION, name)
+		|| 0 == strcmp(SPDY_HTTP_HEADER_KEEP_ALIVE, name)
+		|| 0 == strcmp(SPDY_HTTP_HEADER_TRANSFER_ENCODING, name)
+    )
+	{
+		//forbidden in spdy, ignore
+		free(name);
+		return realsize;
+	}
+	if(i == realsize || '\r'==line[i] || '\n'==line[i])
+	{
+		//no value. is it possible?
+		if(SPDY_YES != SPDY_name_value_add(proxy->headers, name, ""))
+			DIE("SPDY_name_value_add failed");
+		return realsize;
+	}
+
+	//header value
+	pos = i+1;
+	while(pos<realsize && isspace(line[pos])) ++pos; //remove leading space
+	for(i=pos; i<realsize && '\r'!=line[i] && '\n'!=line[i]; ++i);
+	if(NULL == (value = strndup(&(line[pos]), i - pos)))
+        DIE("No memory");
+  PRINT_VERBOSE2("Adding header: '%s': '%s'", name, value);
+	if(SPDY_YES != (ret = SPDY_name_value_add(proxy->headers, name, value)))
+	{
+    abort_it=true;
+    if(NULL != (values = SPDY_name_value_lookup(proxy->headers, name, &num_values)))
+      for(i=0; i<(unsigned int)num_values; ++i)
+        if(0 == strcasecmp(value, values[i]))
+        {
+          abort_it=false;
+          PRINT_VERBOSE2("header appears more than once with same value '%s: %s'", name, value);
+          break;
+        }
+
+    if(abort_it)
+    {
+      PRINT_INFO2("SPDY_name_value_add failed (%i) for '%s'", ret, name);
+      abort();
+    }
+	}
+	free(name);
+	free(value);
+
+	return realsize;
+}
+
+
+static size_t
+curl_write_cb(void *contents, size_t size, size_t nmemb, void *userp)
+{
+	size_t realsize = size * nmemb;
+	struct Proxy *proxy = (struct Proxy *)userp;
+
+	//printf("curl_write_cb %i\n", realsize);
+  if(!*(proxy->session_alive))
+  {
+    PRINT_VERBOSE("data received, but session is dead");
+      proxy->spdy_error = true;
+      proxy->curl_error = true;
+    return 0;
+  }
+
+  if(!store_in_buffer(contents, realsize, &proxy->http_body, &proxy->http_body_size))
+	{
+		PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
+      proxy->curl_error = true;
+		return 0;
+	}
+  /*
+	if(NULL == proxy->http_body)
+		proxy->http_body = malloc(realsize);
+	else
+		proxy->http_body = realloc(proxy->http_body, proxy->http_body_size + realsize);
+	if(NULL == proxy->http_body)
+	{
+		PRINT_INFO("not enough memory (realloc returned NULL)");
+		return 0;
+	}
+
+	memcpy(proxy->http_body + proxy->http_body_size, contents, realsize);
+	proxy->http_body_size += realsize;
+  */
+
+  PRINT_VERBOSE2("received bytes from curl: %zu", realsize);
+
+  call_spdy_run = true;
+
+	return realsize;
+}
+
+
+static size_t
+curl_read_cb(void *ptr, size_t size, size_t nmemb, void *userp)
+{
+	ssize_t ret;
+	size_t max = size * nmemb;
+	struct Proxy *proxy = (struct Proxy *)userp;
+	//void *newbody;
+
+
+  if((proxy->receiving_done && !proxy->received_body_size) || !proxy->is_with_body_data || max < 1)
+  {
+    PRINT_VERBOSE("curl_read_cb last call");
+    return 0;
+  }
+
+  if(!*(proxy->session_alive))
+  {
+    PRINT_VERBOSE("POST is still being sent, but session is dead");
+    return CURL_READFUNC_ABORT;
+  }
+
+	if(!proxy->received_body_size)//nothing to write now
+  {
+    PRINT_VERBOSE("curl_read_cb called paused");
+    proxy->is_curl_read_paused = true;
+		return CURL_READFUNC_PAUSE;//TODO curl pause should be used
+  }
+
+  ret = get_from_buffer(&(proxy->received_body), &(proxy->received_body_size), ptr, max);
+  if(ret < 0)
+  {
+    PRINT_INFO("no memory");
+    return CURL_READFUNC_ABORT;
+  }
+
+	/*
+	if(max >= proxy->received_body_size)
+	{
+		ret = proxy->received_body_size;
+		newbody = NULL;
+	}
+	else
+	{
+		ret = max;
+		if(NULL == (newbody = malloc(proxy->received_body_size - max)))
+		{
+			PRINT_INFO("no memory");
+			return CURL_READFUNC_ABORT;
+		}
+		memcpy(newbody, proxy->received_body + max, proxy->received_body_size - max);
+	}
+	memcpy(ptr, proxy->received_body, ret);
+	free(proxy->received_body);
+	proxy->received_body = newbody;
+	proxy->received_body_size -= ret;
+  * */
+
+  PRINT_VERBOSE2("given POST bytes to curl: %zd", ret);
+
+	return ret;
+}
+
+
+static int
+iterate_cb (void *cls, const char *name, const char * const * value, int num_values)
+{
+	struct Proxy *proxy = (struct Proxy *)cls;
+  struct curl_slist **curl_headers = (&(proxy->curl_headers));
+  char *line;
+  int line_len = strlen(name) + 3; //+ ": \0"
+  int i;
+
+  for(i=0; i<num_values; ++i)
+  {
+		if(i) line_len += 2; //", "
+		line_len += strlen(value[i]);
+	}
+
+	if(NULL == (line = malloc(line_len)))//no recovery
+    DIE("No memory");
+	line[0] = 0;
+
+  strcat(line, name);
+  strcat(line, ": ");
+  //all spdy header names are lower case;
+  //for simplicity here we just capitalize the first letter
+  line[0] = toupper(line[0]);
+
+	for(i=0; i<num_values; ++i)
+	{
+		if(i) strcat(line, ", ");
+  PRINT_VERBOSE2("Adding request header: '%s' len %ld", value[i], strlen(value[i]));
+		strcat(line, value[i]);
+	}
+  PRINT_VERBOSE2("Adding request header: '%s'", line);
+  if(NULL == (*curl_headers = curl_slist_append(*curl_headers, line)))
+		DIE("curl_slist_append failed");
+	free(line);
+
+	return SPDY_YES;
+}
+
+
+static void
+standard_request_handler(void *cls,
+                        struct SPDY_Request * request,
+                        uint8_t priority,
+                        const char *method,
+                        const char *path,
+                        const char *version,
+                        const char *host,
+                        const char *scheme,
+                        struct SPDY_NameValue * headers,
+                        bool more)
+{
+	(void)cls;
+	(void)priority;
+	(void)host;
+	(void)scheme;
+
+	struct Proxy *proxy;
+	int ret;
+  struct URI *uri;
+  struct SPDY_Session *session;
+
+  proxy = SPDY_get_cls_from_request(request);
+  if(NULL != proxy)
+  {
+    //ignore trailers or more headers
+    return;
+  }
+
+	PRINT_VERBOSE2("received request for '%s %s %s'\n", method, path, version);
+
+  //TODO not freed once in a while
+	if(NULL == (proxy = malloc(sizeof(struct Proxy))))
+    DIE("No memory");
+	memset(proxy, 0, sizeof(struct Proxy));
+
+  //fprintf(stderr, "new  proxy for %s\n", path);
+
+  session = SPDY_get_session_for_request(request);
+  assert(NULL != session);
+  proxy->session_alive = SPDY_get_cls_from_session(session);
+  assert(NULL != proxy->session_alive);
+
+  SPDY_set_cls_to_request(request, proxy);
+
+	proxy->request = request;
+	proxy->is_with_body_data = more;
+	if(NULL == (proxy->headers = SPDY_name_value_create()))
+        DIE("No memory");
+
+  if(glob_opt.transparent)
+  {
+    if(NULL != glob_opt.http_backend) //use always same host
+      ret = asprintf(&(proxy->url),"%s://%s%s", scheme, glob_opt.http_backend, path);
+    else //use host header
+      ret = asprintf(&(proxy->url),"%s://%s%s", scheme, host, path);
+    if(-1 == ret)
+        DIE("No memory");
+
+    ret = parse_uri(&uri_preg, proxy->url, &uri);
+    if(ret != 0)
+      DIE("parsing built uri failed");
+  }
+  else
+  {
+    ret = parse_uri(&uri_preg, path, &uri);
+    PRINT_INFO2("path %s '%s' '%s'", path, uri->scheme, uri->host);
+    if(ret != 0 || !strlen(uri->scheme) || !strlen(uri->host))
+      DIE("parsing received uri failed");
+
+    if(NULL != glob_opt.http_backend) //use backend host
+    {
+      ret = asprintf(&(proxy->url),"%s://%s%s", uri->scheme, glob_opt.http_backend, uri->path_and_more);
+      if(-1 == ret)
+        DIE("No memory");
+    }
+    else //use request path
+      if(NULL == (proxy->url = strdup(path)))
+        DIE("No memory");
+  }
+
+  free_uri(uri);
+
+  PRINT_VERBOSE2("curl will request '%s'", proxy->url);
+
+  SPDY_name_value_iterate(headers, &iterate_cb, proxy);
+
+	if(NULL == (proxy->curl_handle = curl_easy_init()))
+    {
+		PRINT_INFO("curl_easy_init failed");
+		abort();
+	}
+
+	if(glob_opt.curl_verbose)
+    CURL_SETOPT(proxy->curl_handle, CURLOPT_VERBOSE, 1);
+
+  if(0 == strcmp(SPDY_HTTP_METHOD_POST,method))
+  {
+    if(NULL == (proxy->curl_headers = curl_slist_append(proxy->curl_headers, "Expect:")))
+      DIE("curl_slist_append failed");
+    CURL_SETOPT(proxy->curl_handle, CURLOPT_POST, 1);
+    CURL_SETOPT(proxy->curl_handle, CURLOPT_READFUNCTION, curl_read_cb);
+    CURL_SETOPT(proxy->curl_handle, CURLOPT_READDATA, proxy);
+  }
+
+  if(glob_opt.timeout)
+    CURL_SETOPT(proxy->curl_handle, CURLOPT_TIMEOUT, glob_opt.timeout);
+	CURL_SETOPT(proxy->curl_handle, CURLOPT_URL, proxy->url);
+	if(glob_opt.http10)
+		CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+	CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEFUNCTION, curl_write_cb);
+	CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEDATA, proxy);
+	CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERFUNCTION, curl_header_cb);
+	CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERDATA, proxy);
+	CURL_SETOPT(proxy->curl_handle, CURLOPT_PRIVATE, proxy);
+	CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTPHEADER, proxy->curl_headers);
+  CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);//TODO
+  CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
+  if(glob_opt.ipv4 && !glob_opt.ipv6)
+    CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+  else if(glob_opt.ipv6 && !glob_opt.ipv4)
+    CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
+
+	if(CURLM_OK != (ret = curl_multi_add_handle(multi_handle, proxy->curl_handle)))
+	{
+		PRINT_INFO2("curl_multi_add_handle failed (%i)", ret);
+		abort();
+	}
+  debug_num_curls++;
+
+  //~5ms additional latency for calling this
+	if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running))
+		&& CURLM_CALL_MULTI_PERFORM != ret)
+	{
+		PRINT_INFO2("curl_multi_perform failed (%i)", ret);
+		abort();
+	}
+
+  call_curl_run = true;
+}
+
+
+static int
+run ()
+{
+  unsigned long long timeoutlong = 0;
+  unsigned long long timeout_spdy = 0;
+  long timeout_curl = -1;
+	struct timeval timeout;
+	int ret;
+	int ret_curl;
+	int ret_spdy;
+	fd_set rs;
+	fd_set ws;
+	fd_set es;
+	int maxfd = -1;
+	int maxfd_curl = -1;
+	struct SPDY_Daemon *daemon;
+  CURLMsg *msg;
+  int msgs_left;
+  struct Proxy *proxy;
+  struct sockaddr_in *addr;
+  struct addrinfo hints;
+  char service[NI_MAXSERV];
+  struct addrinfo *gai;
+  enum SPDY_IO_SUBSYSTEM io = glob_opt.notls ? SPDY_IO_SUBSYSTEM_RAW : SPDY_IO_SUBSYSTEM_OPENSSL;
+  enum SPDY_DAEMON_FLAG flags = SPDY_DAEMON_FLAG_NO;
+  //struct SPDY_Response *error_response;
+  char *curl_private;
+
+	signal(SIGPIPE, SIG_IGN);
+
+  if (signal(SIGINT, catch_signal) == SIG_ERR)
+    PRINT_VERBOSE("signal failed");
+
+  srand(time(NULL));
+  if(init_parse_uri(&uri_preg))
+    DIE("Regexp compilation failed");
+
+	SPDY_init();
+
+  if(glob_opt.nodelay)
+    flags |= SPDY_DAEMON_FLAG_NO_DELAY;
+
+  if(NULL == glob_opt.listen_host)
+	{
+    daemon = SPDY_start_daemon(glob_opt.listen_port,
+								glob_opt.cert,
+								glob_opt.cert_key,
+								&new_session_cb,
+								&session_closed_cb,
+								&standard_request_handler,
+								&spdy_post_data_cb,
+								NULL,
+								SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
+								1800,
+                SPDY_DAEMON_OPTION_IO_SUBSYSTEM,
+                io,
+                SPDY_DAEMON_OPTION_FLAGS,
+                flags,
+								SPDY_DAEMON_OPTION_END);
+  }
+  else
+  {
+    snprintf (service, sizeof(service), "%u", glob_opt.listen_port);
+    memset (&hints, 0, sizeof(struct addrinfo));
+    hints.ai_family = AF_INET;
+    hints.ai_socktype = SOCK_STREAM;
+
+    ret = getaddrinfo(glob_opt.listen_host, service, &hints, &gai);
+    if(ret != 0)
+      DIE("problem with specified host");
+
+    addr = (struct sockaddr_in *) gai->ai_addr;
+
+    daemon = SPDY_start_daemon(0,
+								glob_opt.cert,
+								glob_opt.cert_key,
+								&new_session_cb,
+								&session_closed_cb,
+								&standard_request_handler,
+								&spdy_post_data_cb,
+								NULL,
+								SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
+								1800,
+                SPDY_DAEMON_OPTION_IO_SUBSYSTEM,
+                io,
+                SPDY_DAEMON_OPTION_FLAGS,
+                flags,
+                SPDY_DAEMON_OPTION_SOCK_ADDR,
+                addr,
+                //SPDY_DAEMON_OPTION_MAX_NUM_FRAMES,
+                //1,
+								SPDY_DAEMON_OPTION_END);
+  }
+
+	if(NULL==daemon){
+		printf("no daemon\n");
+		return 1;
+	}
+
+	multi_handle = curl_multi_init();
+	if(NULL==multi_handle)
+		DIE("no multi_handle");
+
+	timeout.tv_usec = 0;
+
+	do
+	{
+		FD_ZERO(&rs);
+		FD_ZERO(&ws);
+		FD_ZERO(&es);
+
+    PRINT_VERBOSE2("num  curls %i", debug_num_curls);
+
+    ret_spdy = SPDY_get_timeout(daemon, &timeout_spdy);
+    if(SPDY_NO == ret_spdy || timeout_spdy > 5000)
+      timeoutlong = 5000;
+    else
+      timeoutlong = timeout_spdy;
+    PRINT_VERBOSE2("SPDY timeout %lld; %i", timeout_spdy, ret_spdy);
+
+    if(CURLM_OK != (ret_curl = curl_multi_timeout(multi_handle, &timeout_curl)))
+    {
+      PRINT_VERBOSE2("curl_multi_timeout failed (%i)", ret_curl);
+      //curl_timeo = timeoutlong;
+    }
+    else if(timeout_curl >= 0 && timeoutlong > (unsigned long)timeout_curl)
+      timeoutlong = (unsigned long)timeout_curl;
+
+    PRINT_VERBOSE2("curl timeout %ld", timeout_curl);
+
+    timeout.tv_sec = timeoutlong / 1000;
+		timeout.tv_usec = (timeoutlong % 1000) * 1000;
+
+		maxfd = SPDY_get_fdset (daemon,
+								&rs,
+								&ws,
+								&es);
+		assert(-1 != maxfd);
+
+		if(CURLM_OK != (ret = curl_multi_fdset(multi_handle, &rs,
+								&ws,
+								&es, &maxfd_curl)))
+		{
+			PRINT_INFO2("curl_multi_fdset failed (%i)", ret);
+			abort();
+		}
+
+    if(maxfd_curl > maxfd)
+      maxfd = maxfd_curl;
+
+    PRINT_VERBOSE2("timeout before %lld %lld", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec);
+    ret = select(maxfd+1, &rs, &ws, &es, &timeout);
+    PRINT_VERBOSE2("timeout after %lld %lld; ret is %i", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec, ret);
+
+		/*switch(ret) {
+			case -1:
+				PRINT_INFO2("select error: %i", errno);
+				break;
+			case 0:
+				break;
+			default:*/
+
+      //the second part should not happen with current implementation
+      if(ret > 0 || (SPDY_YES == ret_spdy && 0 == timeout_spdy))
+      {
+				PRINT_VERBOSE("run spdy");
+				SPDY_run(daemon);
+        call_spdy_run = false;
+      }
+
+      //if(ret > 0 || (CURLM_OK == ret_curl && 0 == timeout_curl) || call_curl_run)
+      {
+				PRINT_VERBOSE("run curl");
+				if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running))
+					&& CURLM_CALL_MULTI_PERFORM != ret)
+				{
+					PRINT_INFO2("curl_multi_perform failed (%i)", ret);
+					abort();
+				}
+        call_curl_run = false;
+      }
+			/*break;
+		}*/
+
+    while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
+      if (msg->msg == CURLMSG_DONE) {
+        PRINT_VERBOSE("A curl handler is done");
+        if(CURLE_OK != (ret = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &curl_private)))
+        {
+          PRINT_INFO2("err %i",ret);
+          abort();
+        }
+        assert(NULL != curl_private);
+        proxy = (struct Proxy *)curl_private;
+        if(CURLE_OK == msg->data.result)
+        {
+          proxy->curl_done = true;
+          call_spdy_run = true;
+          //TODO what happens with proxy when the client resets a stream
+          //and response_done is not yet set for the last frame? is it
+          //possible?
+        }
+        else
+        {
+          PRINT_VERBOSE2("bad curl result (%i) for '%s'", msg->data.result, proxy->url);
+          if(proxy->spdy_done || proxy->spdy_error || (NULL == proxy->response && !*(proxy->session_alive)))
+          {
+            PRINT_VERBOSE("cleaning");
+            SPDY_name_value_destroy(proxy->headers);
+            SPDY_destroy_request(proxy->request);
+            SPDY_destroy_response(proxy->response);
+            cleanup(proxy);
+          }
+          else if(NULL == proxy->response && *(proxy->session_alive))
+          {
+            //generate error for the client
+            PRINT_VERBOSE("will send Bad Gateway");
+            SPDY_name_value_destroy(proxy->headers);
+            proxy->headers = NULL;
+            if(NULL == (proxy->response = SPDY_build_response(SPDY_HTTP_BAD_GATEWAY,
+                  NULL,
+                  SPDY_HTTP_VERSION_1_1,
+                  NULL,
+                  ERROR_RESPONSE,
+                  strlen(ERROR_RESPONSE))))
+              DIE("no response");
+            if(SPDY_YES != SPDY_queue_response(proxy->request,
+                      proxy->response,
+                      true,
+                      false,
+                      &response_done_callback,
+                      proxy))
+            {
+              //clean and forget
+              PRINT_VERBOSE("cleaning");
+              SPDY_destroy_request(proxy->request);
+              SPDY_destroy_response(proxy->response);
+              cleanup(proxy);
+            }
+          }
+          else
+          {
+            proxy->curl_error = true;
+          }
+          call_spdy_run = true;
+          //TODO spdy should be notified to send RST_STREAM
+        }
+      }
+      else PRINT_INFO("shouldn't happen");
+    }
+
+    if(call_spdy_run)
+    {
+      PRINT_VERBOSE("second call to SPDY_run");
+      SPDY_run(daemon);
+      call_spdy_run = false;
+    }
+
+    if(glob_opt.verbose)
+    {
+
+#ifdef HAVE_CLOCK_GETTIME
+#ifdef CLOCK_MONOTONIC
+    struct timespec ts;
+
+    if (0 == clock_gettime(CLOCK_MONOTONIC, &ts))
+    PRINT_VERBOSE2 ("time now %lld %lld",
+		    (unsigned long long) ts.tv_sec,
+		    (unsigned long long) ts.tv_nsec);
+#endif
+#endif
+    }
+
+  }
+  while(loop);
+
+	SPDY_stop_daemon(daemon);
+
+	curl_multi_cleanup(multi_handle);
+
+	SPDY_deinit();
+
+  deinit_parse_uri(&uri_preg);
+
+	return 0;
+}
+
+
+static void
+display_usage()
+{
+  printf(
+    "Usage: microspdy2http -p <PORT> [-c <CERTIFICATE>] [-k <CERT-KEY>]\n"
+    "                      [-rvh0DtT] [-b <HTTP-SERVER>] [-l <HOST>]\n\n"
+    "OPTIONS:\n"
+    "    -p, --port            Listening port.\n"
+    "    -l, --host            Listening host. If not set, will listen on [::]\n"
+    "    -c, --certificate     Path to a certificate file. Requiered if\n"
+    "                          --no-tls is not set.\n"
+    "    -k, --certificate-key Path to a key file for the certificate.\n"
+    "                          Requiered if --no-tls is not set.\n"
+    "    -b, --backend-server  If set, the proxy will connect always to it.\n"
+    "                          Otherwise the proxy will connect to the URL\n"
+    "                          which is specified in the path or 'Host:'.\n"
+    "    -v, --verbose         Print debug information.\n"
+    "    -r, --no-tls          Do not use TLS. Client must use SPDY/3.\n"
+    "    -h, --curl-verbose    Print debug information for curl.\n"
+    "    -0, --http10          Prefer HTTP/1.0 connections to the next hop.\n"
+    "    -D, --no-delay        This makes sense only if --no-tls is used.\n"
+    "                          TCP_NODELAY will be used for all sessions' sockets.\n"
+    "    -4, --curl-ipv4       Curl may use IPv4 to connect to the final destination.\n"
+    "    -6, --curl-ipv6       Curl may use IPv6 to connect to the final destination.\n"
+    "                          If neither --curl-ipv4 nor --curl-ipv6 is set,\n"
+    "                          both will be used by default.\n"
+    "    -T, --timeout         Maximum time in seconds for each HTTP transfer.\n"
+    "                          Use 0 for no timeout; this is the default value.\n"
+    "    -t, --transparent     If set, the proxy will fetch an URL which\n"
+    "                          is based on 'Host:' header and requested path.\n"
+    "                          Otherwise, full URL in the requested path is required.\n\n"
+
+  );
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+
+  int getopt_ret;
+  int option_index;
+  struct option long_options[] = {
+    {"port",  required_argument, 0, 'p'},
+    {"certificate",  required_argument, 0, 'c'},
+    {"certificate-key",  required_argument, 0, 'k'},
+    {"backend-server",  required_argument, 0, 'b'},
+    {"no-tls",  no_argument, 0, 'r'},
+    {"verbose",  no_argument, 0, 'v'},
+    {"curl-verbose",  no_argument, 0, 'h'},
+    {"http10",  no_argument, 0, '0'},
+    {"no-delay",  no_argument, 0, 'D'},
+    {"transparent",  no_argument, 0, 't'},
+    {"curl-ipv4",  no_argument, 0, '4'},
+    {"curl-ipv6",  no_argument, 0, '6'},
+    {"timeout",  required_argument, 0, 'T'},
+    {0, 0, 0, 0}
+  };
+
+  while (1)
+  {
+    getopt_ret = getopt_long( argc, argv, "p:l:c:k:b:rv0Dth46T:", long_options, &option_index);
+    if (getopt_ret == -1)
+      break;
+
+    switch(getopt_ret)
+    {
+      case 'p':
+        glob_opt.listen_port = atoi(optarg);
+        break;
+
+      case 'l':
+        glob_opt.listen_host= strdup(optarg);
+        if(NULL == glob_opt.listen_host)
+          return 1;
+        break;
+
+      case 'c':
+        glob_opt.cert = strdup(optarg);
+        break;
+
+      case 'k':
+        glob_opt.cert_key = strdup(optarg);
+        break;
+
+      case 'b':
+        glob_opt.http_backend = strdup(optarg);
+        if(NULL == glob_opt.http_backend)
+          return 1;
+        break;
+
+      case 'r':
+        glob_opt.notls = true;
+        break;
+
+      case 'v':
+        glob_opt.verbose = true;
+        break;
+
+      case 'h':
+        glob_opt.curl_verbose = true;
+        break;
+
+      case '0':
+        glob_opt.http10 = true;
+        break;
+
+      case 'D':
+        glob_opt.nodelay = true;
+        break;
+
+      case 't':
+        glob_opt.transparent = true;
+        break;
+
+      case '4':
+        glob_opt.ipv4 = true;
+        break;
+
+      case '6':
+        glob_opt.ipv6 = true;
+        break;
+
+      case 'T':
+        glob_opt.timeout = atoi(optarg);
+        break;
+
+      case 0:
+        PRINT_INFO("0 from getopt");
+        break;
+
+      case '?':
+        display_usage();
+        return 1;
+
+      default:
+        DIE("default from getopt");
+    }
+  }
+
+  if(
+    0 == glob_opt.listen_port
+    || (!glob_opt.notls && (NULL == glob_opt.cert || NULL == glob_opt.cert_key))
+    //|| !glob_opt.transparent && NULL != glob_opt.http_backend
+    )
+  {
+    display_usage();
+    return 1;
+  }
+
+  return run();
+}
+
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am
new file mode 100644
index 0000000..eb165bd
--- /dev/null
+++ b/src/testcurl/Makefile.am
@@ -0,0 +1,310 @@
+# This Makefile.am is in the public domain
+SUBDIRS  = .
+
+if USE_COVERAGE
+  AM_CFLAGS = -fprofile-arcs -ftest-coverage
+endif
+
+if ENABLE_HTTPS
+  SUBDIRS += https
+endif
+
+AM_CPPFLAGS = \
+-DCPU_COUNT=$(CPU_COUNT) \
+-I$(top_srcdir) \
+-I$(top_srcdir)/src/microhttpd \
+-I$(top_srcdir)/src/include \
+$(LIBCURL_CPPFLAGS)
+
+if !HAVE_W32
+PERF_GET_CONCURRENT=perf_get_concurrent
+TEST_CONCURRENT_STOP=test_concurrent_stop
+if HAVE_CURL_BINARY
+CURL_FORK_TEST = test_get_response_cleanup
+endif
+endif
+
+if HAVE_CURL
+check_PROGRAMS = \
+  test_start_stop \
+  test_get \
+  test_get_sendfile \
+  test_urlparse \
+  test_put \
+  $(TEST_CONCURRENT_STOP) \
+  test_process_headers \
+  test_process_arguments \
+  test_parse_cookies \
+  test_large_put \
+  test_get11 \
+  test_get_sendfile11 \
+  test_put11 \
+  test_large_put11 \
+  test_long_header \
+  test_long_header11 \
+  test_get_chunked \
+  test_put_chunked \
+  test_iplimit11 \
+  test_termination \
+  test_timeout \
+  test_callback \
+  $(CURL_FORK_TEST) \
+  perf_get $(PERF_GET_CONCURRENT)
+
+if HAVE_POSIX_THREADS
+check_PROGRAMS += \
+  test_quiesce
+endif
+
+if HAVE_POSTPROCESSOR
+ check_PROGRAMS += \
+  test_post \
+  test_postform \
+  test_post_loop \
+  test_post11 \
+  test_postform11 \
+  test_post_loop11
+endif
+
+noinst_PROGRAMS = \
+  test_options
+
+if ENABLE_DAUTH
+  check_PROGRAMS += \
+	test_digestauth test_digestauth_with_arguments
+endif
+
+TESTS = $(check_PROGRAMS)
+
+noinst_LIBRARIES = libcurl_version_check.a
+endif
+
+libcurl_version_check_a_SOURCES = \
+  curl_version_check.c
+
+test_start_stop_SOURCES = \
+  test_start_stop.c
+test_start_stop_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+test_concurrent_stop_SOURCES = \
+  test_concurrent_stop.c
+test_concurrent_stop_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+ @LIBCURL@
+
+test_options_SOURCES = \
+  test_options.c
+test_options_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+test_get_SOURCES = \
+  test_get.c
+test_get_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_quiesce_SOURCES = \
+  test_quiesce.c
+test_quiesce_CFLAGS = \
+  $(PTHREAD_CFLAGS) $(AM_CFLAGS)
+test_quiesce_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(PTHREAD_LIBS) @LIBCURL@
+
+test_callback_SOURCES = \
+  test_callback.c
+test_callback_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+perf_get_SOURCES = \
+  perf_get.c \
+  gauger.h
+perf_get_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+perf_get_concurrent_SOURCES = \
+  perf_get_concurrent.c \
+  gauger.h
+perf_get_concurrent_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_digestauth_SOURCES = \
+  test_digestauth.c
+test_digestauth_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_digestauth_with_arguments_SOURCES = \
+  test_digestauth_with_arguments.c
+test_digestauth_with_arguments_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_get_sendfile_SOURCES = \
+  test_get_sendfile.c
+test_get_sendfile_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+test_get_sendfile_DEPENDENCIES =
+
+if HAVE_W32
+test_get_sendfile_LDADD += \
+ $(top_builddir)/src/platform/libplatform_interface.la
+test_get_sendfile_DEPENDENCIES += \
+ $(top_builddir)/src/platform/libplatform_interface.la
+endif
+
+test_urlparse_SOURCES = \
+  test_urlparse.c
+test_urlparse_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_get_response_cleanup_SOURCES = \
+  test_get_response_cleanup.c
+test_get_response_cleanup_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+test_get_chunked_SOURCES = \
+  test_get_chunked.c
+test_get_chunked_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_post_SOURCES = \
+  test_post.c
+test_post_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_process_headers_SOURCES = \
+  test_process_headers.c
+test_process_headers_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_parse_cookies_SOURCES = \
+  test_parse_cookies.c
+test_parse_cookies_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_process_arguments_SOURCES = \
+  test_process_arguments.c
+test_process_arguments_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_postform_SOURCES = \
+  test_postform.c
+test_postform_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_post_loop_SOURCES = \
+  test_post_loop.c
+test_post_loop_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_put_SOURCES = \
+  test_put.c
+test_put_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_put_chunked_SOURCES = \
+  test_put_chunked.c
+test_put_chunked_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_get11_SOURCES = \
+  test_get.c
+test_get11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_get_sendfile11_SOURCES = \
+  test_get_sendfile.c
+test_get_sendfile11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+test_get_sendfile11_DEPENDENCIES =
+
+if HAVE_W32
+test_get_sendfile11_LDADD += \
+  $(top_builddir)/src/platform/libplatform_interface.la
+test_get_sendfile11_DEPENDENCIES += \
+  $(top_builddir)/src/platform/libplatform_interface.la
+endif
+
+test_post11_SOURCES = \
+  test_post.c
+test_post11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_postform11_SOURCES = \
+  test_postform.c
+test_postform11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_post_loop11_SOURCES = \
+  test_post_loop.c
+test_post_loop11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_put11_SOURCES = \
+  test_put.c
+test_put11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_large_put_SOURCES = \
+  test_large_put.c
+test_large_put_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_large_put11_SOURCES = \
+  test_large_put.c
+test_large_put11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_long_header_SOURCES = \
+  test_long_header.c
+test_long_header_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_long_header11_SOURCES = \
+  test_long_header.c
+test_long_header11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_iplimit11_SOURCES = \
+  test_iplimit.c
+test_iplimit11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_termination_SOURCES = \
+  test_termination.c
+test_termination_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_timeout_SOURCES = \
+  test_timeout.c
+test_timeout_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
diff --git a/src/testcurl/Makefile.in b/src/testcurl/Makefile.in
new file mode 100644
index 0000000..937bf26
--- /dev/null
+++ b/src/testcurl/Makefile.in
@@ -0,0 +1,2059 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 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 = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+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@
+@ENABLE_HTTPS_TRUE@am__append_1 = https
+@HAVE_CURL_TRUE@check_PROGRAMS = test_start_stop$(EXEEXT) \
+@HAVE_CURL_TRUE@	test_get$(EXEEXT) test_get_sendfile$(EXEEXT) \
+@HAVE_CURL_TRUE@	test_urlparse$(EXEEXT) test_put$(EXEEXT) \
+@HAVE_CURL_TRUE@	$(am__EXEEXT_1) test_process_headers$(EXEEXT) \
+@HAVE_CURL_TRUE@	test_process_arguments$(EXEEXT) \
+@HAVE_CURL_TRUE@	test_parse_cookies$(EXEEXT) \
+@HAVE_CURL_TRUE@	test_large_put$(EXEEXT) test_get11$(EXEEXT) \
+@HAVE_CURL_TRUE@	test_get_sendfile11$(EXEEXT) \
+@HAVE_CURL_TRUE@	test_put11$(EXEEXT) test_large_put11$(EXEEXT) \
+@HAVE_CURL_TRUE@	test_long_header$(EXEEXT) \
+@HAVE_CURL_TRUE@	test_long_header11$(EXEEXT) \
+@HAVE_CURL_TRUE@	test_get_chunked$(EXEEXT) \
+@HAVE_CURL_TRUE@	test_put_chunked$(EXEEXT) \
+@HAVE_CURL_TRUE@	test_iplimit11$(EXEEXT) \
+@HAVE_CURL_TRUE@	test_termination$(EXEEXT) \
+@HAVE_CURL_TRUE@	test_timeout$(EXEEXT) test_callback$(EXEEXT) \
+@HAVE_CURL_TRUE@	$(am__EXEEXT_2) perf_get$(EXEEXT) \
+@HAVE_CURL_TRUE@	$(am__EXEEXT_3) $(am__EXEEXT_4) \
+@HAVE_CURL_TRUE@	$(am__EXEEXT_5) $(am__EXEEXT_6)
+@HAVE_CURL_TRUE@@HAVE_POSIX_THREADS_TRUE@am__append_2 = \
+@HAVE_CURL_TRUE@@HAVE_POSIX_THREADS_TRUE@  test_quiesce
+
+@HAVE_CURL_TRUE@@HAVE_POSTPROCESSOR_TRUE@am__append_3 = \
+@HAVE_CURL_TRUE@@HAVE_POSTPROCESSOR_TRUE@  test_post \
+@HAVE_CURL_TRUE@@HAVE_POSTPROCESSOR_TRUE@  test_postform \
+@HAVE_CURL_TRUE@@HAVE_POSTPROCESSOR_TRUE@  test_post_loop \
+@HAVE_CURL_TRUE@@HAVE_POSTPROCESSOR_TRUE@  test_post11 \
+@HAVE_CURL_TRUE@@HAVE_POSTPROCESSOR_TRUE@  test_postform11 \
+@HAVE_CURL_TRUE@@HAVE_POSTPROCESSOR_TRUE@  test_post_loop11
+
+@HAVE_CURL_TRUE@noinst_PROGRAMS = test_options$(EXEEXT)
+@ENABLE_DAUTH_TRUE@@HAVE_CURL_TRUE@am__append_4 = \
+@ENABLE_DAUTH_TRUE@@HAVE_CURL_TRUE@	test_digestauth test_digestauth_with_arguments
+
+@HAVE_W32_TRUE@am__append_5 = \
+@HAVE_W32_TRUE@ $(top_builddir)/src/platform/libplatform_interface.la
+
+@HAVE_W32_TRUE@am__append_6 = \
+@HAVE_W32_TRUE@ $(top_builddir)/src/platform/libplatform_interface.la
+
+@HAVE_W32_TRUE@am__append_7 = \
+@HAVE_W32_TRUE@  $(top_builddir)/src/platform/libplatform_interface.la
+
+@HAVE_W32_TRUE@am__append_8 = \
+@HAVE_W32_TRUE@  $(top_builddir)/src/platform/libplatform_interface.la
+
+subdir = src/testcurl
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp $(top_srcdir)/test-driver
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_compile_flags.m4 \
+	$(top_srcdir)/m4/ax_append_flag.m4 \
+	$(top_srcdir)/m4/ax_check_compile_flag.m4 \
+	$(top_srcdir)/m4/ax_check_link_flag.m4 \
+	$(top_srcdir)/m4/ax_check_openssl.m4 \
+	$(top_srcdir)/m4/ax_count_cpus.m4 \
+	$(top_srcdir)/m4/ax_have_epoll.m4 \
+	$(top_srcdir)/m4/ax_pthread.m4 \
+	$(top_srcdir)/m4/ax_require_defined.m4 \
+	$(top_srcdir)/m4/libcurl.m4 $(top_srcdir)/m4/libgcrypt.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)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/MHD_config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_@AM_V@)
+am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
+am__v_AR_0 = @echo "  AR      " $@;
+am__v_AR_1 = 
+libcurl_version_check_a_AR = $(AR) $(ARFLAGS)
+libcurl_version_check_a_LIBADD =
+am_libcurl_version_check_a_OBJECTS = curl_version_check.$(OBJEXT)
+libcurl_version_check_a_OBJECTS =  \
+	$(am_libcurl_version_check_a_OBJECTS)
+@HAVE_W32_FALSE@am__EXEEXT_1 = test_concurrent_stop$(EXEEXT)
+@HAVE_CURL_BINARY_TRUE@@HAVE_W32_FALSE@am__EXEEXT_2 = test_get_response_cleanup$(EXEEXT)
+@HAVE_W32_FALSE@am__EXEEXT_3 = perf_get_concurrent$(EXEEXT)
+@HAVE_CURL_TRUE@@HAVE_POSIX_THREADS_TRUE@am__EXEEXT_4 = test_quiesce$(EXEEXT)
+@HAVE_CURL_TRUE@@HAVE_POSTPROCESSOR_TRUE@am__EXEEXT_5 =  \
+@HAVE_CURL_TRUE@@HAVE_POSTPROCESSOR_TRUE@	test_post$(EXEEXT) \
+@HAVE_CURL_TRUE@@HAVE_POSTPROCESSOR_TRUE@	test_postform$(EXEEXT) \
+@HAVE_CURL_TRUE@@HAVE_POSTPROCESSOR_TRUE@	test_post_loop$(EXEEXT) \
+@HAVE_CURL_TRUE@@HAVE_POSTPROCESSOR_TRUE@	test_post11$(EXEEXT) \
+@HAVE_CURL_TRUE@@HAVE_POSTPROCESSOR_TRUE@	test_postform11$(EXEEXT) \
+@HAVE_CURL_TRUE@@HAVE_POSTPROCESSOR_TRUE@	test_post_loop11$(EXEEXT)
+@ENABLE_DAUTH_TRUE@@HAVE_CURL_TRUE@am__EXEEXT_6 =  \
+@ENABLE_DAUTH_TRUE@@HAVE_CURL_TRUE@	test_digestauth$(EXEEXT) \
+@ENABLE_DAUTH_TRUE@@HAVE_CURL_TRUE@	test_digestauth_with_arguments$(EXEEXT)
+PROGRAMS = $(noinst_PROGRAMS)
+am_perf_get_OBJECTS = perf_get.$(OBJEXT)
+perf_get_OBJECTS = $(am_perf_get_OBJECTS)
+perf_get_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+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_perf_get_concurrent_OBJECTS = perf_get_concurrent.$(OBJEXT)
+perf_get_concurrent_OBJECTS = $(am_perf_get_concurrent_OBJECTS)
+perf_get_concurrent_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_callback_OBJECTS = test_callback.$(OBJEXT)
+test_callback_OBJECTS = $(am_test_callback_OBJECTS)
+test_callback_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_concurrent_stop_OBJECTS = test_concurrent_stop.$(OBJEXT)
+test_concurrent_stop_OBJECTS = $(am_test_concurrent_stop_OBJECTS)
+test_concurrent_stop_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_digestauth_OBJECTS = test_digestauth.$(OBJEXT)
+test_digestauth_OBJECTS = $(am_test_digestauth_OBJECTS)
+test_digestauth_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_digestauth_with_arguments_OBJECTS =  \
+	test_digestauth_with_arguments.$(OBJEXT)
+test_digestauth_with_arguments_OBJECTS =  \
+	$(am_test_digestauth_with_arguments_OBJECTS)
+test_digestauth_with_arguments_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_get_OBJECTS = test_get.$(OBJEXT)
+test_get_OBJECTS = $(am_test_get_OBJECTS)
+test_get_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_get11_OBJECTS = test_get.$(OBJEXT)
+test_get11_OBJECTS = $(am_test_get11_OBJECTS)
+test_get11_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_get_chunked_OBJECTS = test_get_chunked.$(OBJEXT)
+test_get_chunked_OBJECTS = $(am_test_get_chunked_OBJECTS)
+test_get_chunked_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_get_response_cleanup_OBJECTS =  \
+	test_get_response_cleanup.$(OBJEXT)
+test_get_response_cleanup_OBJECTS =  \
+	$(am_test_get_response_cleanup_OBJECTS)
+test_get_response_cleanup_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_get_sendfile_OBJECTS = test_get_sendfile.$(OBJEXT)
+test_get_sendfile_OBJECTS = $(am_test_get_sendfile_OBJECTS)
+am_test_get_sendfile11_OBJECTS = test_get_sendfile.$(OBJEXT)
+test_get_sendfile11_OBJECTS = $(am_test_get_sendfile11_OBJECTS)
+am_test_iplimit11_OBJECTS = test_iplimit.$(OBJEXT)
+test_iplimit11_OBJECTS = $(am_test_iplimit11_OBJECTS)
+test_iplimit11_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_large_put_OBJECTS = test_large_put.$(OBJEXT)
+test_large_put_OBJECTS = $(am_test_large_put_OBJECTS)
+test_large_put_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_large_put11_OBJECTS = test_large_put.$(OBJEXT)
+test_large_put11_OBJECTS = $(am_test_large_put11_OBJECTS)
+test_large_put11_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_long_header_OBJECTS = test_long_header.$(OBJEXT)
+test_long_header_OBJECTS = $(am_test_long_header_OBJECTS)
+test_long_header_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_long_header11_OBJECTS = test_long_header.$(OBJEXT)
+test_long_header11_OBJECTS = $(am_test_long_header11_OBJECTS)
+test_long_header11_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_options_OBJECTS = test_options.$(OBJEXT)
+test_options_OBJECTS = $(am_test_options_OBJECTS)
+test_options_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_parse_cookies_OBJECTS = test_parse_cookies.$(OBJEXT)
+test_parse_cookies_OBJECTS = $(am_test_parse_cookies_OBJECTS)
+test_parse_cookies_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_post_OBJECTS = test_post.$(OBJEXT)
+test_post_OBJECTS = $(am_test_post_OBJECTS)
+test_post_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_post11_OBJECTS = test_post.$(OBJEXT)
+test_post11_OBJECTS = $(am_test_post11_OBJECTS)
+test_post11_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_post_loop_OBJECTS = test_post_loop.$(OBJEXT)
+test_post_loop_OBJECTS = $(am_test_post_loop_OBJECTS)
+test_post_loop_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_post_loop11_OBJECTS = test_post_loop.$(OBJEXT)
+test_post_loop11_OBJECTS = $(am_test_post_loop11_OBJECTS)
+test_post_loop11_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_postform_OBJECTS = test_postform.$(OBJEXT)
+test_postform_OBJECTS = $(am_test_postform_OBJECTS)
+test_postform_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_postform11_OBJECTS = test_postform.$(OBJEXT)
+test_postform11_OBJECTS = $(am_test_postform11_OBJECTS)
+test_postform11_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_process_arguments_OBJECTS = test_process_arguments.$(OBJEXT)
+test_process_arguments_OBJECTS = $(am_test_process_arguments_OBJECTS)
+test_process_arguments_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_process_headers_OBJECTS = test_process_headers.$(OBJEXT)
+test_process_headers_OBJECTS = $(am_test_process_headers_OBJECTS)
+test_process_headers_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_put_OBJECTS = test_put.$(OBJEXT)
+test_put_OBJECTS = $(am_test_put_OBJECTS)
+test_put_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_put11_OBJECTS = test_put.$(OBJEXT)
+test_put11_OBJECTS = $(am_test_put11_OBJECTS)
+test_put11_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_put_chunked_OBJECTS = test_put_chunked.$(OBJEXT)
+test_put_chunked_OBJECTS = $(am_test_put_chunked_OBJECTS)
+test_put_chunked_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_quiesce_OBJECTS = test_quiesce-test_quiesce.$(OBJEXT)
+test_quiesce_OBJECTS = $(am_test_quiesce_OBJECTS)
+am__DEPENDENCIES_1 =
+test_quiesce_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la \
+	$(am__DEPENDENCIES_1)
+test_quiesce_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(test_quiesce_CFLAGS) \
+	$(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+am_test_start_stop_OBJECTS = test_start_stop.$(OBJEXT)
+test_start_stop_OBJECTS = $(am_test_start_stop_OBJECTS)
+test_start_stop_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_termination_OBJECTS = test_termination.$(OBJEXT)
+test_termination_OBJECTS = $(am_test_termination_OBJECTS)
+test_termination_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_timeout_OBJECTS = test_timeout.$(OBJEXT)
+test_timeout_OBJECTS = $(am_test_timeout_OBJECTS)
+test_timeout_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_urlparse_OBJECTS = test_urlparse.$(OBJEXT)
+test_urlparse_OBJECTS = $(am_test_urlparse_OBJECTS)
+test_urlparse_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+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__depfiles_maybe = depfiles
+am__mv = mv -f
+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 = $(libcurl_version_check_a_SOURCES) $(perf_get_SOURCES) \
+	$(perf_get_concurrent_SOURCES) $(test_callback_SOURCES) \
+	$(test_concurrent_stop_SOURCES) $(test_digestauth_SOURCES) \
+	$(test_digestauth_with_arguments_SOURCES) $(test_get_SOURCES) \
+	$(test_get11_SOURCES) $(test_get_chunked_SOURCES) \
+	$(test_get_response_cleanup_SOURCES) \
+	$(test_get_sendfile_SOURCES) $(test_get_sendfile11_SOURCES) \
+	$(test_iplimit11_SOURCES) $(test_large_put_SOURCES) \
+	$(test_large_put11_SOURCES) $(test_long_header_SOURCES) \
+	$(test_long_header11_SOURCES) $(test_options_SOURCES) \
+	$(test_parse_cookies_SOURCES) $(test_post_SOURCES) \
+	$(test_post11_SOURCES) $(test_post_loop_SOURCES) \
+	$(test_post_loop11_SOURCES) $(test_postform_SOURCES) \
+	$(test_postform11_SOURCES) $(test_process_arguments_SOURCES) \
+	$(test_process_headers_SOURCES) $(test_put_SOURCES) \
+	$(test_put11_SOURCES) $(test_put_chunked_SOURCES) \
+	$(test_quiesce_SOURCES) $(test_start_stop_SOURCES) \
+	$(test_termination_SOURCES) $(test_timeout_SOURCES) \
+	$(test_urlparse_SOURCES)
+DIST_SOURCES = $(libcurl_version_check_a_SOURCES) $(perf_get_SOURCES) \
+	$(perf_get_concurrent_SOURCES) $(test_callback_SOURCES) \
+	$(test_concurrent_stop_SOURCES) $(test_digestauth_SOURCES) \
+	$(test_digestauth_with_arguments_SOURCES) $(test_get_SOURCES) \
+	$(test_get11_SOURCES) $(test_get_chunked_SOURCES) \
+	$(test_get_response_cleanup_SOURCES) \
+	$(test_get_sendfile_SOURCES) $(test_get_sendfile11_SOURCES) \
+	$(test_iplimit11_SOURCES) $(test_large_put_SOURCES) \
+	$(test_large_put11_SOURCES) $(test_long_header_SOURCES) \
+	$(test_long_header11_SOURCES) $(test_options_SOURCES) \
+	$(test_parse_cookies_SOURCES) $(test_post_SOURCES) \
+	$(test_post11_SOURCES) $(test_post_loop_SOURCES) \
+	$(test_post_loop11_SOURCES) $(test_postform_SOURCES) \
+	$(test_postform11_SOURCES) $(test_process_arguments_SOURCES) \
+	$(test_process_headers_SOURCES) $(test_put_SOURCES) \
+	$(test_put11_SOURCES) $(test_put_chunked_SOURCES) \
+	$(test_quiesce_SOURCES) $(test_start_stop_SOURCES) \
+	$(test_termination_SOURCES) $(test_timeout_SOURCES) \
+	$(test_urlparse_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-recursive dvi-recursive html-recursive info-recursive \
+	install-data-recursive install-dvi-recursive \
+	install-exec-recursive install-html-recursive \
+	install-info-recursive install-pdf-recursive \
+	install-ps-recursive install-recursive installcheck-recursive \
+	installdirs-recursive pdf-recursive ps-recursive \
+	tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	check recheck distdir
+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__tty_colors_dummy = \
+  mgn= red= grn= lgn= blu= brg= std=; \
+  am__color_tests=no
+am__tty_colors = { \
+  $(am__tty_colors_dummy); \
+  if test "X$(AM_COLOR_TESTS)" = Xno; then \
+    am__color_tests=no; \
+  elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+    am__color_tests=yes; \
+  elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+    am__color_tests=yes; \
+  fi; \
+  if test $$am__color_tests = yes; then \
+    red=''; \
+    grn=''; \
+    lgn=''; \
+    blu=''; \
+    mgn=''; \
+    brg=''; \
+    std=''; \
+  fi; \
+}
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__recheck_rx = ^[ 	]*:recheck:[ 	]*
+am__global_test_result_rx = ^[ 	]*:global-test-result:[ 	]*
+am__copy_in_global_log_rx = ^[ 	]*:copy-in-global-log:[ 	]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+  recheck = 1; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+        { \
+          if ((getline line2 < ($$0 ".log")) < 0) \
+	    recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+        { \
+          recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+        { \
+          break; \
+        } \
+    }; \
+  if (recheck) \
+    print $$0; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+  print "fatal: making $@: " msg | "cat >&2"; \
+  exit 1; \
+} \
+function rst_section(header) \
+{ \
+  print header; \
+  len = length(header); \
+  for (i = 1; i <= len; i = i + 1) \
+    printf "="; \
+  printf "\n\n"; \
+} \
+{ \
+  copy_in_global_log = 1; \
+  global_test_result = "RUN"; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+         fatal("failed to read from " $$0 ".trs"); \
+      if (line ~ /$(am__global_test_result_rx)/) \
+        { \
+          sub("$(am__global_test_result_rx)", "", line); \
+          sub("[ 	]*$$", "", line); \
+          global_test_result = line; \
+        } \
+      else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+        copy_in_global_log = 0; \
+    }; \
+  if (copy_in_global_log) \
+    { \
+      rst_section(global_test_result ": " $$0); \
+      while ((rc = (getline line < ($$0 ".log"))) != 0) \
+      { \
+        if (rc < 0) \
+          fatal("failed to read from " $$0 ".log"); \
+        print line; \
+      }; \
+      printf "\n"; \
+    }; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/   &   /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this.  Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+  --color-tests "$$am__color_tests" \
+  --enable-hard-errors "$$am__enable_hard_errors" \
+  --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test.  Creates the
+# directory for the log if needed.  Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log.  Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT.  Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup);					\
+$(am__vpath_adj_setup) $(am__vpath_adj)			\
+$(am__tty_colors);					\
+srcdir=$(srcdir); export srcdir;			\
+case "$@" in						\
+  */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;;	\
+    *) am__odir=.;; 					\
+esac;							\
+test "x$$am__odir" = x"." || test -d "$$am__odir" 	\
+  || $(MKDIR_P) "$$am__odir" || exit $$?;		\
+if test -f "./$$f"; then dir=./;			\
+elif test -f "$$f"; then dir=;				\
+else dir="$(srcdir)/"; fi;				\
+tst=$$dir$$f; log='$@'; 				\
+if test -n '$(DISABLE_HARD_ERRORS)'; then		\
+  am__enable_hard_errors=no; 				\
+else							\
+  am__enable_hard_errors=yes; 				\
+fi; 							\
+case " $(XFAIL_TESTS) " in				\
+  *[\ \	]$$f[\ \	]* | *[\ \	]$$dir$$f[\ \	]*) \
+    am__expect_failure=yes;;				\
+  *)							\
+    am__expect_failure=no;;				\
+esac; 							\
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed).  The result is saved in the shell variable
+# '$bases'.  This honors runtime overriding of TESTS and TEST_LOGS.  Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+  bases='$(TEST_LOGS)'; \
+  bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+  bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+  case '$@' in \
+    */*) \
+      case '$*' in \
+        */*) b='$*';; \
+          *) b=`echo '$@' | sed 's/\.log$$//'`; \
+       esac;; \
+    *) \
+      b='$*';; \
+  esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+	$(TEST_LOG_FLAGS)
+DIST_SUBDIRS = . https
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPU_COUNT = @CPU_COUNT@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_CPPFLAGS = @GNUTLS_CPPFLAGS@
+GNUTLS_LDFLAGS = @GNUTLS_LDFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+HAVE_CURL_BINARY = @HAVE_CURL_BINARY@
+HAVE_MAKEINFO_BINARY = @HAVE_MAKEINFO_BINARY@
+HIDDEN_VISIBILITY_CFLAGS = @HIDDEN_VISIBILITY_CFLAGS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSPDY_VERSION_AGE = @LIBSPDY_VERSION_AGE@
+LIBSPDY_VERSION_CURRENT = @LIBSPDY_VERSION_CURRENT@
+LIBSPDY_VERSION_REVISION = @LIBSPDY_VERSION_REVISION@
+LIBTOOL = @LIBTOOL@
+LIB_VERSION_AGE = @LIB_VERSION_AGE@
+LIB_VERSION_CURRENT = @LIB_VERSION_CURRENT@
+LIB_VERSION_REVISION = @LIB_VERSION_REVISION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MHD_LIBDEPS = @MHD_LIBDEPS@
+MHD_LIBDEPS_PKGCFG = @MHD_LIBDEPS_PKGCFG@
+MHD_LIB_CFLAGS = @MHD_LIB_CFLAGS@
+MHD_LIB_CPPFLAGS = @MHD_LIB_CPPFLAGS@
+MHD_LIB_LDFLAGS = @MHD_LIB_LDFLAGS@
+MHD_REQ_PRIVATE = @MHD_REQ_PRIVATE@
+MKDIR_P = @MKDIR_P@
+MS_LIB_TOOL = @MS_LIB_TOOL@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_INCLUDES = @OPENSSL_INCLUDES@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@
+PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@
+PACKAGE_VERSION_SUBMINOR = @PACKAGE_VERSION_SUBMINOR@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPDY_LIBDEPS = @SPDY_LIBDEPS@
+SPDY_LIB_CFLAGS = @SPDY_LIB_CFLAGS@
+SPDY_LIB_CPPFLAGS = @SPDY_LIB_CPPFLAGS@
+SPDY_LIB_LDFLAGS = @SPDY_LIB_LDFLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+_libcurl_config = @_libcurl_config@
+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_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@
+ax_pthread_config = @ax_pthread_config@
+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@
+have_socat = @have_socat@
+have_zzuf = @have_zzuf@
+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@
+lt_cv_objdir = @lt_cv_objdir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# This Makefile.am is in the public domain
+SUBDIRS = . $(am__append_1)
+@USE_COVERAGE_TRUE@AM_CFLAGS = -fprofile-arcs -ftest-coverage
+AM_CPPFLAGS = \
+-DCPU_COUNT=$(CPU_COUNT) \
+-I$(top_srcdir) \
+-I$(top_srcdir)/src/microhttpd \
+-I$(top_srcdir)/src/include \
+$(LIBCURL_CPPFLAGS)
+
+@HAVE_W32_FALSE@PERF_GET_CONCURRENT = perf_get_concurrent
+@HAVE_W32_FALSE@TEST_CONCURRENT_STOP = test_concurrent_stop
+@HAVE_CURL_BINARY_TRUE@@HAVE_W32_FALSE@CURL_FORK_TEST = test_get_response_cleanup
+@HAVE_CURL_TRUE@TESTS = $(check_PROGRAMS)
+@HAVE_CURL_TRUE@noinst_LIBRARIES = libcurl_version_check.a
+libcurl_version_check_a_SOURCES = \
+  curl_version_check.c
+
+test_start_stop_SOURCES = \
+  test_start_stop.c
+
+test_start_stop_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+test_concurrent_stop_SOURCES = \
+  test_concurrent_stop.c
+
+test_concurrent_stop_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+ @LIBCURL@
+
+test_options_SOURCES = \
+  test_options.c
+
+test_options_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+test_get_SOURCES = \
+  test_get.c
+
+test_get_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_quiesce_SOURCES = \
+  test_quiesce.c
+
+test_quiesce_CFLAGS = \
+  $(PTHREAD_CFLAGS) $(AM_CFLAGS)
+
+test_quiesce_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(PTHREAD_LIBS) @LIBCURL@
+
+test_callback_SOURCES = \
+  test_callback.c
+
+test_callback_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+perf_get_SOURCES = \
+  perf_get.c \
+  gauger.h
+
+perf_get_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+perf_get_concurrent_SOURCES = \
+  perf_get_concurrent.c \
+  gauger.h
+
+perf_get_concurrent_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_digestauth_SOURCES = \
+  test_digestauth.c
+
+test_digestauth_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_digestauth_with_arguments_SOURCES = \
+  test_digestauth_with_arguments.c
+
+test_digestauth_with_arguments_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_get_sendfile_SOURCES = \
+  test_get_sendfile.c
+
+test_get_sendfile_LDADD =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la @LIBCURL@ \
+	$(am__append_5)
+test_get_sendfile_DEPENDENCIES = $(am__append_6)
+test_urlparse_SOURCES = \
+  test_urlparse.c
+
+test_urlparse_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_get_response_cleanup_SOURCES = \
+  test_get_response_cleanup.c
+
+test_get_response_cleanup_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+test_get_chunked_SOURCES = \
+  test_get_chunked.c
+
+test_get_chunked_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_post_SOURCES = \
+  test_post.c
+
+test_post_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_process_headers_SOURCES = \
+  test_process_headers.c
+
+test_process_headers_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_parse_cookies_SOURCES = \
+  test_parse_cookies.c
+
+test_parse_cookies_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_process_arguments_SOURCES = \
+  test_process_arguments.c
+
+test_process_arguments_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_postform_SOURCES = \
+  test_postform.c
+
+test_postform_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_post_loop_SOURCES = \
+  test_post_loop.c
+
+test_post_loop_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_put_SOURCES = \
+  test_put.c
+
+test_put_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_put_chunked_SOURCES = \
+  test_put_chunked.c
+
+test_put_chunked_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_get11_SOURCES = \
+  test_get.c
+
+test_get11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_get_sendfile11_SOURCES = \
+  test_get_sendfile.c
+
+test_get_sendfile11_LDADD =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la @LIBCURL@ \
+	$(am__append_7)
+test_get_sendfile11_DEPENDENCIES = $(am__append_8)
+test_post11_SOURCES = \
+  test_post.c
+
+test_post11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_postform11_SOURCES = \
+  test_postform.c
+
+test_postform11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_post_loop11_SOURCES = \
+  test_post_loop.c
+
+test_post_loop11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_put11_SOURCES = \
+  test_put.c
+
+test_put11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_large_put_SOURCES = \
+  test_large_put.c
+
+test_large_put_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_large_put11_SOURCES = \
+  test_large_put.c
+
+test_large_put11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_long_header_SOURCES = \
+  test_long_header.c
+
+test_long_header_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_long_header11_SOURCES = \
+  test_long_header.c
+
+test_long_header11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_iplimit11_SOURCES = \
+  test_iplimit.c
+
+test_iplimit11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_termination_SOURCES = \
+  test_termination.c
+
+test_termination_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+test_timeout_SOURCES = \
+  test_timeout.c
+
+test_timeout_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@
+
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
+$(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) --gnu src/testcurl/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/testcurl/Makefile
+.PRECIOUS: 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__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	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-noinstLIBRARIES:
+	-test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+
+libcurl_version_check.a: $(libcurl_version_check_a_OBJECTS) $(libcurl_version_check_a_DEPENDENCIES) $(EXTRA_libcurl_version_check_a_DEPENDENCIES) 
+	$(AM_V_at)-rm -f libcurl_version_check.a
+	$(AM_V_AR)$(libcurl_version_check_a_AR) libcurl_version_check.a $(libcurl_version_check_a_OBJECTS) $(libcurl_version_check_a_LIBADD)
+	$(AM_V_at)$(RANLIB) libcurl_version_check.a
+
+clean-checkPROGRAMS:
+	@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+
+clean-noinstPROGRAMS:
+	@list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+
+perf_get$(EXEEXT): $(perf_get_OBJECTS) $(perf_get_DEPENDENCIES) $(EXTRA_perf_get_DEPENDENCIES) 
+	@rm -f perf_get$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(perf_get_OBJECTS) $(perf_get_LDADD) $(LIBS)
+
+perf_get_concurrent$(EXEEXT): $(perf_get_concurrent_OBJECTS) $(perf_get_concurrent_DEPENDENCIES) $(EXTRA_perf_get_concurrent_DEPENDENCIES) 
+	@rm -f perf_get_concurrent$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(perf_get_concurrent_OBJECTS) $(perf_get_concurrent_LDADD) $(LIBS)
+
+test_callback$(EXEEXT): $(test_callback_OBJECTS) $(test_callback_DEPENDENCIES) $(EXTRA_test_callback_DEPENDENCIES) 
+	@rm -f test_callback$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_callback_OBJECTS) $(test_callback_LDADD) $(LIBS)
+
+test_concurrent_stop$(EXEEXT): $(test_concurrent_stop_OBJECTS) $(test_concurrent_stop_DEPENDENCIES) $(EXTRA_test_concurrent_stop_DEPENDENCIES) 
+	@rm -f test_concurrent_stop$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_concurrent_stop_OBJECTS) $(test_concurrent_stop_LDADD) $(LIBS)
+
+test_digestauth$(EXEEXT): $(test_digestauth_OBJECTS) $(test_digestauth_DEPENDENCIES) $(EXTRA_test_digestauth_DEPENDENCIES) 
+	@rm -f test_digestauth$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_digestauth_OBJECTS) $(test_digestauth_LDADD) $(LIBS)
+
+test_digestauth_with_arguments$(EXEEXT): $(test_digestauth_with_arguments_OBJECTS) $(test_digestauth_with_arguments_DEPENDENCIES) $(EXTRA_test_digestauth_with_arguments_DEPENDENCIES) 
+	@rm -f test_digestauth_with_arguments$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_digestauth_with_arguments_OBJECTS) $(test_digestauth_with_arguments_LDADD) $(LIBS)
+
+test_get$(EXEEXT): $(test_get_OBJECTS) $(test_get_DEPENDENCIES) $(EXTRA_test_get_DEPENDENCIES) 
+	@rm -f test_get$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_get_OBJECTS) $(test_get_LDADD) $(LIBS)
+
+test_get11$(EXEEXT): $(test_get11_OBJECTS) $(test_get11_DEPENDENCIES) $(EXTRA_test_get11_DEPENDENCIES) 
+	@rm -f test_get11$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_get11_OBJECTS) $(test_get11_LDADD) $(LIBS)
+
+test_get_chunked$(EXEEXT): $(test_get_chunked_OBJECTS) $(test_get_chunked_DEPENDENCIES) $(EXTRA_test_get_chunked_DEPENDENCIES) 
+	@rm -f test_get_chunked$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_get_chunked_OBJECTS) $(test_get_chunked_LDADD) $(LIBS)
+
+test_get_response_cleanup$(EXEEXT): $(test_get_response_cleanup_OBJECTS) $(test_get_response_cleanup_DEPENDENCIES) $(EXTRA_test_get_response_cleanup_DEPENDENCIES) 
+	@rm -f test_get_response_cleanup$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_get_response_cleanup_OBJECTS) $(test_get_response_cleanup_LDADD) $(LIBS)
+
+test_get_sendfile$(EXEEXT): $(test_get_sendfile_OBJECTS) $(test_get_sendfile_DEPENDENCIES) $(EXTRA_test_get_sendfile_DEPENDENCIES) 
+	@rm -f test_get_sendfile$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_get_sendfile_OBJECTS) $(test_get_sendfile_LDADD) $(LIBS)
+
+test_get_sendfile11$(EXEEXT): $(test_get_sendfile11_OBJECTS) $(test_get_sendfile11_DEPENDENCIES) $(EXTRA_test_get_sendfile11_DEPENDENCIES) 
+	@rm -f test_get_sendfile11$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_get_sendfile11_OBJECTS) $(test_get_sendfile11_LDADD) $(LIBS)
+
+test_iplimit11$(EXEEXT): $(test_iplimit11_OBJECTS) $(test_iplimit11_DEPENDENCIES) $(EXTRA_test_iplimit11_DEPENDENCIES) 
+	@rm -f test_iplimit11$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_iplimit11_OBJECTS) $(test_iplimit11_LDADD) $(LIBS)
+
+test_large_put$(EXEEXT): $(test_large_put_OBJECTS) $(test_large_put_DEPENDENCIES) $(EXTRA_test_large_put_DEPENDENCIES) 
+	@rm -f test_large_put$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_large_put_OBJECTS) $(test_large_put_LDADD) $(LIBS)
+
+test_large_put11$(EXEEXT): $(test_large_put11_OBJECTS) $(test_large_put11_DEPENDENCIES) $(EXTRA_test_large_put11_DEPENDENCIES) 
+	@rm -f test_large_put11$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_large_put11_OBJECTS) $(test_large_put11_LDADD) $(LIBS)
+
+test_long_header$(EXEEXT): $(test_long_header_OBJECTS) $(test_long_header_DEPENDENCIES) $(EXTRA_test_long_header_DEPENDENCIES) 
+	@rm -f test_long_header$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_long_header_OBJECTS) $(test_long_header_LDADD) $(LIBS)
+
+test_long_header11$(EXEEXT): $(test_long_header11_OBJECTS) $(test_long_header11_DEPENDENCIES) $(EXTRA_test_long_header11_DEPENDENCIES) 
+	@rm -f test_long_header11$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_long_header11_OBJECTS) $(test_long_header11_LDADD) $(LIBS)
+
+test_options$(EXEEXT): $(test_options_OBJECTS) $(test_options_DEPENDENCIES) $(EXTRA_test_options_DEPENDENCIES) 
+	@rm -f test_options$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_options_OBJECTS) $(test_options_LDADD) $(LIBS)
+
+test_parse_cookies$(EXEEXT): $(test_parse_cookies_OBJECTS) $(test_parse_cookies_DEPENDENCIES) $(EXTRA_test_parse_cookies_DEPENDENCIES) 
+	@rm -f test_parse_cookies$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_parse_cookies_OBJECTS) $(test_parse_cookies_LDADD) $(LIBS)
+
+test_post$(EXEEXT): $(test_post_OBJECTS) $(test_post_DEPENDENCIES) $(EXTRA_test_post_DEPENDENCIES) 
+	@rm -f test_post$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_post_OBJECTS) $(test_post_LDADD) $(LIBS)
+
+test_post11$(EXEEXT): $(test_post11_OBJECTS) $(test_post11_DEPENDENCIES) $(EXTRA_test_post11_DEPENDENCIES) 
+	@rm -f test_post11$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_post11_OBJECTS) $(test_post11_LDADD) $(LIBS)
+
+test_post_loop$(EXEEXT): $(test_post_loop_OBJECTS) $(test_post_loop_DEPENDENCIES) $(EXTRA_test_post_loop_DEPENDENCIES) 
+	@rm -f test_post_loop$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_post_loop_OBJECTS) $(test_post_loop_LDADD) $(LIBS)
+
+test_post_loop11$(EXEEXT): $(test_post_loop11_OBJECTS) $(test_post_loop11_DEPENDENCIES) $(EXTRA_test_post_loop11_DEPENDENCIES) 
+	@rm -f test_post_loop11$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_post_loop11_OBJECTS) $(test_post_loop11_LDADD) $(LIBS)
+
+test_postform$(EXEEXT): $(test_postform_OBJECTS) $(test_postform_DEPENDENCIES) $(EXTRA_test_postform_DEPENDENCIES) 
+	@rm -f test_postform$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_postform_OBJECTS) $(test_postform_LDADD) $(LIBS)
+
+test_postform11$(EXEEXT): $(test_postform11_OBJECTS) $(test_postform11_DEPENDENCIES) $(EXTRA_test_postform11_DEPENDENCIES) 
+	@rm -f test_postform11$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_postform11_OBJECTS) $(test_postform11_LDADD) $(LIBS)
+
+test_process_arguments$(EXEEXT): $(test_process_arguments_OBJECTS) $(test_process_arguments_DEPENDENCIES) $(EXTRA_test_process_arguments_DEPENDENCIES) 
+	@rm -f test_process_arguments$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_process_arguments_OBJECTS) $(test_process_arguments_LDADD) $(LIBS)
+
+test_process_headers$(EXEEXT): $(test_process_headers_OBJECTS) $(test_process_headers_DEPENDENCIES) $(EXTRA_test_process_headers_DEPENDENCIES) 
+	@rm -f test_process_headers$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_process_headers_OBJECTS) $(test_process_headers_LDADD) $(LIBS)
+
+test_put$(EXEEXT): $(test_put_OBJECTS) $(test_put_DEPENDENCIES) $(EXTRA_test_put_DEPENDENCIES) 
+	@rm -f test_put$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_put_OBJECTS) $(test_put_LDADD) $(LIBS)
+
+test_put11$(EXEEXT): $(test_put11_OBJECTS) $(test_put11_DEPENDENCIES) $(EXTRA_test_put11_DEPENDENCIES) 
+	@rm -f test_put11$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_put11_OBJECTS) $(test_put11_LDADD) $(LIBS)
+
+test_put_chunked$(EXEEXT): $(test_put_chunked_OBJECTS) $(test_put_chunked_DEPENDENCIES) $(EXTRA_test_put_chunked_DEPENDENCIES) 
+	@rm -f test_put_chunked$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_put_chunked_OBJECTS) $(test_put_chunked_LDADD) $(LIBS)
+
+test_quiesce$(EXEEXT): $(test_quiesce_OBJECTS) $(test_quiesce_DEPENDENCIES) $(EXTRA_test_quiesce_DEPENDENCIES) 
+	@rm -f test_quiesce$(EXEEXT)
+	$(AM_V_CCLD)$(test_quiesce_LINK) $(test_quiesce_OBJECTS) $(test_quiesce_LDADD) $(LIBS)
+
+test_start_stop$(EXEEXT): $(test_start_stop_OBJECTS) $(test_start_stop_DEPENDENCIES) $(EXTRA_test_start_stop_DEPENDENCIES) 
+	@rm -f test_start_stop$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_start_stop_OBJECTS) $(test_start_stop_LDADD) $(LIBS)
+
+test_termination$(EXEEXT): $(test_termination_OBJECTS) $(test_termination_DEPENDENCIES) $(EXTRA_test_termination_DEPENDENCIES) 
+	@rm -f test_termination$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_termination_OBJECTS) $(test_termination_LDADD) $(LIBS)
+
+test_timeout$(EXEEXT): $(test_timeout_OBJECTS) $(test_timeout_DEPENDENCIES) $(EXTRA_test_timeout_DEPENDENCIES) 
+	@rm -f test_timeout$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_timeout_OBJECTS) $(test_timeout_LDADD) $(LIBS)
+
+test_urlparse$(EXEEXT): $(test_urlparse_OBJECTS) $(test_urlparse_DEPENDENCIES) $(EXTRA_test_urlparse_DEPENDENCIES) 
+	@rm -f test_urlparse$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_urlparse_OBJECTS) $(test_urlparse_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/curl_version_check.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/perf_get.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/perf_get_concurrent.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_callback.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_concurrent_stop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_digestauth.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_digestauth_with_arguments.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_get.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_get_chunked.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_get_response_cleanup.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_get_sendfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_iplimit.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_large_put.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_long_header.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_options.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_parse_cookies.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_post.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_post_loop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_postform.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_process_arguments.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_process_headers.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_put.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_put_chunked.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_quiesce-test_quiesce.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_start_stop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_termination.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_timeout.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_urlparse.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+test_quiesce-test_quiesce.o: test_quiesce.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_quiesce_CFLAGS) $(CFLAGS) -MT test_quiesce-test_quiesce.o -MD -MP -MF $(DEPDIR)/test_quiesce-test_quiesce.Tpo -c -o test_quiesce-test_quiesce.o `test -f 'test_quiesce.c' || echo '$(srcdir)/'`test_quiesce.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_quiesce-test_quiesce.Tpo $(DEPDIR)/test_quiesce-test_quiesce.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_quiesce.c' object='test_quiesce-test_quiesce.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_quiesce_CFLAGS) $(CFLAGS) -c -o test_quiesce-test_quiesce.o `test -f 'test_quiesce.c' || echo '$(srcdir)/'`test_quiesce.c
+
+test_quiesce-test_quiesce.obj: test_quiesce.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_quiesce_CFLAGS) $(CFLAGS) -MT test_quiesce-test_quiesce.obj -MD -MP -MF $(DEPDIR)/test_quiesce-test_quiesce.Tpo -c -o test_quiesce-test_quiesce.obj `if test -f 'test_quiesce.c'; then $(CYGPATH_W) 'test_quiesce.c'; else $(CYGPATH_W) '$(srcdir)/test_quiesce.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_quiesce-test_quiesce.Tpo $(DEPDIR)/test_quiesce-test_quiesce.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_quiesce.c' object='test_quiesce-test_quiesce.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_quiesce_CFLAGS) $(CFLAGS) -c -o test_quiesce-test_quiesce.obj `if test -f 'test_quiesce.c'; then $(CYGPATH_W) 'test_quiesce.c'; else $(CYGPATH_W) '$(srcdir)/test_quiesce.c'; fi`
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+#     (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'.  Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+	rm -f $< $@
+	$(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+	@:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+	@$(am__set_TESTS_bases); \
+	am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+	redo_bases=`for i in $$bases; do \
+	              am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+	            done`; \
+	if test -n "$$redo_bases"; then \
+	  redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+	  redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+	  if $(am__make_dryrun); then :; else \
+	    rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
+	  fi; \
+	fi; \
+	if test -n "$$am__remaking_logs"; then \
+	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+	       "recursion detected" >&2; \
+	else \
+	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+	fi; \
+	if $(am__make_dryrun); then :; else \
+	  st=0;  \
+	  errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+	  for i in $$redo_bases; do \
+	    test -f $$i.trs && test -r $$i.trs \
+	      || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+	    test -f $$i.log && test -r $$i.log \
+	      || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+	  done; \
+	  test $$st -eq 0 || exit 1; \
+	fi
+	@$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+	ws='[ 	]'; \
+	results=`for b in $$bases; do echo $$b.trs; done`; \
+	test -n "$$results" || results=/dev/null; \
+	all=`  grep "^$$ws*:test-result:"           $$results | wc -l`; \
+	pass=` grep "^$$ws*:test-result:$$ws*PASS"  $$results | wc -l`; \
+	fail=` grep "^$$ws*:test-result:$$ws*FAIL"  $$results | wc -l`; \
+	skip=` grep "^$$ws*:test-result:$$ws*SKIP"  $$results | wc -l`; \
+	xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+	xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+	error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+	if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+	  success=true; \
+	else \
+	  success=false; \
+	fi; \
+	br='==================='; br=$$br$$br$$br$$br; \
+	result_count () \
+	{ \
+	    if test x"$$1" = x"--maybe-color"; then \
+	      maybe_colorize=yes; \
+	    elif test x"$$1" = x"--no-color"; then \
+	      maybe_colorize=no; \
+	    else \
+	      echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+	    fi; \
+	    shift; \
+	    desc=$$1 count=$$2; \
+	    if test $$maybe_colorize = yes && test $$count -gt 0; then \
+	      color_start=$$3 color_end=$$std; \
+	    else \
+	      color_start= color_end=; \
+	    fi; \
+	    echo "$${color_start}# $$desc $$count$${color_end}"; \
+	}; \
+	create_testsuite_report () \
+	{ \
+	  result_count $$1 "TOTAL:" $$all   "$$brg"; \
+	  result_count $$1 "PASS: " $$pass  "$$grn"; \
+	  result_count $$1 "SKIP: " $$skip  "$$blu"; \
+	  result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+	  result_count $$1 "FAIL: " $$fail  "$$red"; \
+	  result_count $$1 "XPASS:" $$xpass "$$red"; \
+	  result_count $$1 "ERROR:" $$error "$$mgn"; \
+	}; \
+	{								\
+	  echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" |	\
+	    $(am__rst_title);						\
+	  create_testsuite_report --no-color;				\
+	  echo;								\
+	  echo ".. contents:: :depth: 2";				\
+	  echo;								\
+	  for b in $$bases; do echo $$b; done				\
+	    | $(am__create_global_log);					\
+	} >$(TEST_SUITE_LOG).tmp || exit 1;				\
+	mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG);			\
+	if $$success; then						\
+	  col="$$grn";							\
+	 else								\
+	  col="$$red";							\
+	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
+	fi;								\
+	echo "$${col}$$br$${std}"; 					\
+	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
+	echo "$${col}$$br$${std}"; 					\
+	create_testsuite_report --maybe-color;				\
+	echo "$$col$$br$$std";						\
+	if $$success; then :; else					\
+	  echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}";		\
+	  if test -n "$(PACKAGE_BUGREPORT)"; then			\
+	    echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}";	\
+	  fi;								\
+	  echo "$$col$$br$$std";					\
+	fi;								\
+	$$success || exit 1
+
+check-TESTS:
+	@list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
+	@list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+	log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+	exit $$?;
+recheck: all $(check_PROGRAMS)
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	bases=`for i in $$bases; do echo $$i; done \
+	         | $(am__list_recheck_tests)` || exit 1; \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	log_list=`echo $$log_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+	        am__force_recheck=am--force-recheck \
+	        TEST_LOGS="$$log_list"; \
+	exit $$?
+test_start_stop.log: test_start_stop$(EXEEXT)
+	@p='test_start_stop$(EXEEXT)'; \
+	b='test_start_stop'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_get.log: test_get$(EXEEXT)
+	@p='test_get$(EXEEXT)'; \
+	b='test_get'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_get_sendfile.log: test_get_sendfile$(EXEEXT)
+	@p='test_get_sendfile$(EXEEXT)'; \
+	b='test_get_sendfile'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_urlparse.log: test_urlparse$(EXEEXT)
+	@p='test_urlparse$(EXEEXT)'; \
+	b='test_urlparse'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_put.log: test_put$(EXEEXT)
+	@p='test_put$(EXEEXT)'; \
+	b='test_put'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_concurrent_stop.log: test_concurrent_stop$(EXEEXT)
+	@p='test_concurrent_stop$(EXEEXT)'; \
+	b='test_concurrent_stop'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_process_headers.log: test_process_headers$(EXEEXT)
+	@p='test_process_headers$(EXEEXT)'; \
+	b='test_process_headers'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_process_arguments.log: test_process_arguments$(EXEEXT)
+	@p='test_process_arguments$(EXEEXT)'; \
+	b='test_process_arguments'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_parse_cookies.log: test_parse_cookies$(EXEEXT)
+	@p='test_parse_cookies$(EXEEXT)'; \
+	b='test_parse_cookies'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_large_put.log: test_large_put$(EXEEXT)
+	@p='test_large_put$(EXEEXT)'; \
+	b='test_large_put'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_get11.log: test_get11$(EXEEXT)
+	@p='test_get11$(EXEEXT)'; \
+	b='test_get11'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_get_sendfile11.log: test_get_sendfile11$(EXEEXT)
+	@p='test_get_sendfile11$(EXEEXT)'; \
+	b='test_get_sendfile11'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_put11.log: test_put11$(EXEEXT)
+	@p='test_put11$(EXEEXT)'; \
+	b='test_put11'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_large_put11.log: test_large_put11$(EXEEXT)
+	@p='test_large_put11$(EXEEXT)'; \
+	b='test_large_put11'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_long_header.log: test_long_header$(EXEEXT)
+	@p='test_long_header$(EXEEXT)'; \
+	b='test_long_header'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_long_header11.log: test_long_header11$(EXEEXT)
+	@p='test_long_header11$(EXEEXT)'; \
+	b='test_long_header11'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_get_chunked.log: test_get_chunked$(EXEEXT)
+	@p='test_get_chunked$(EXEEXT)'; \
+	b='test_get_chunked'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_put_chunked.log: test_put_chunked$(EXEEXT)
+	@p='test_put_chunked$(EXEEXT)'; \
+	b='test_put_chunked'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_iplimit11.log: test_iplimit11$(EXEEXT)
+	@p='test_iplimit11$(EXEEXT)'; \
+	b='test_iplimit11'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_termination.log: test_termination$(EXEEXT)
+	@p='test_termination$(EXEEXT)'; \
+	b='test_termination'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_timeout.log: test_timeout$(EXEEXT)
+	@p='test_timeout$(EXEEXT)'; \
+	b='test_timeout'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_callback.log: test_callback$(EXEEXT)
+	@p='test_callback$(EXEEXT)'; \
+	b='test_callback'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_get_response_cleanup.log: test_get_response_cleanup$(EXEEXT)
+	@p='test_get_response_cleanup$(EXEEXT)'; \
+	b='test_get_response_cleanup'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+perf_get.log: perf_get$(EXEEXT)
+	@p='perf_get$(EXEEXT)'; \
+	b='perf_get'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+perf_get_concurrent.log: perf_get_concurrent$(EXEEXT)
+	@p='perf_get_concurrent$(EXEEXT)'; \
+	b='perf_get_concurrent'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_quiesce.log: test_quiesce$(EXEEXT)
+	@p='test_quiesce$(EXEEXT)'; \
+	b='test_quiesce'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_post.log: test_post$(EXEEXT)
+	@p='test_post$(EXEEXT)'; \
+	b='test_post'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_postform.log: test_postform$(EXEEXT)
+	@p='test_postform$(EXEEXT)'; \
+	b='test_postform'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_post_loop.log: test_post_loop$(EXEEXT)
+	@p='test_post_loop$(EXEEXT)'; \
+	b='test_post_loop'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_post11.log: test_post11$(EXEEXT)
+	@p='test_post11$(EXEEXT)'; \
+	b='test_post11'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_postform11.log: test_postform11$(EXEEXT)
+	@p='test_postform11$(EXEEXT)'; \
+	b='test_postform11'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_post_loop11.log: test_post_loop11$(EXEEXT)
+	@p='test_post_loop11$(EXEEXT)'; \
+	b='test_post_loop11'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_digestauth.log: test_digestauth$(EXEEXT)
+	@p='test_digestauth$(EXEEXT)'; \
+	b='test_digestauth'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_digestauth_with_arguments.log: test_digestauth_with_arguments$(EXEEXT)
+	@p='test_digestauth_with_arguments$(EXEEXT)'; \
+	b='test_digestauth_with_arguments'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+	@p='$<'; \
+	$(am__set_b); \
+	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@	@p='$<'; \
+@am__EXEEXT_TRUE@	$(am__set_b); \
+@am__EXEEXT_TRUE@	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@	--log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@	"$$tst" $(AM_TESTS_FD_REDIRECT)
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    $(am__make_dryrun) \
+	      || test -d "$(distdir)/$$subdir" \
+	      || $(MKDIR_P) "$(distdir)/$$subdir" \
+	      || exit 1; \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-recursive
+all-am: Makefile $(LIBRARIES) $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+	-test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+	-test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+	-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-checkPROGRAMS clean-generic clean-libtool \
+	clean-noinstLIBRARIES clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) check-am install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+	check-TESTS check-am clean clean-checkPROGRAMS clean-generic \
+	clean-libtool clean-noinstLIBRARIES clean-noinstPROGRAMS \
+	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 installdirs-am \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+	pdf pdf-am ps ps-am recheck tags tags-am uninstall \
+	uninstall-am
+
+
+# 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/src/testcurl/curl_version_check.c b/src/testcurl/curl_version_check.c
new file mode 100644
index 0000000..518cb86
--- /dev/null
+++ b/src/testcurl/curl_version_check.c
@@ -0,0 +1,176 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file curl_version_check.c
+ * @brief  verify required cURL version is available to run tests
+ * @author Sagie Amir
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+static int
+parse_version_number (const char **s)
+{
+  int i = 0;
+  char num[17];
+
+  while (i < 16 && ((**s >= '0') & (**s <= '9')))
+    {
+      num[i] = **s;
+      (*s)++;
+      i++;
+    }
+
+  num[i] = '\0';
+
+  return atoi (num);
+}
+
+const char *
+parse_version_string (const char *s, int *major, int *minor, int *micro)
+{
+  if (!s)
+    return NULL;
+  *major = parse_version_number (&s);
+  if (*s != '.')
+    return NULL;
+  s++;
+  *minor = parse_version_number (&s);
+  if (*s != '.')
+    return NULL;
+  s++;
+  *micro = parse_version_number (&s);
+  return s;
+}
+
+#if HTTPS_SUPPORT
+int
+curl_uses_nss_ssl()
+{
+  return (strstr(curl_version(), " NSS/") != NULL) ? 0 : -1;
+}
+#endif
+
+/*
+ * check local libcurl version matches required version
+ */
+int
+curl_check_version (const char *req_version)
+{
+  const char *ver;
+  const char *curl_ver;
+#if HTTPS_SUPPORT
+  const char *ssl_ver;
+  const char *req_ssl_ver;
+#endif
+
+  int loc_major, loc_minor, loc_micro;
+  int rq_major, rq_minor, rq_micro;
+
+  ver = curl_version ();
+#if HAVE_MESSAGES
+  fprintf (stderr, "curl version: %s\n", ver);
+#endif
+  /*
+   * this call relies on the cURL string to be of the exact following format :
+   * 'libcurl/7.16.4 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/0.6.5' OR
+   * 'libcurl/7.18.2 GnuTLS/2.4.0 zlib/1.2.3.3 libidn/0.6.5'
+   */
+  curl_ver = strchr (ver, '/');
+  if (curl_ver == NULL)
+    return -1;
+  curl_ver++;
+  /* Parse version numbers */
+  if ( (NULL == parse_version_string (req_version, &rq_major, &rq_minor, &rq_micro)) ||
+       (NULL == parse_version_string (curl_ver, &loc_major, &loc_minor, &loc_micro)) )
+    return -1;
+
+  /* Compare version numbers.  */
+  if ((loc_major > rq_major
+       || (loc_major == rq_major && loc_minor > rq_minor)
+       || (loc_major == rq_major && loc_minor == rq_minor
+           && loc_micro > rq_micro) || (loc_major == rq_major
+                                        && loc_minor == rq_minor
+                                        && loc_micro == rq_micro)) == 0)
+    {
+      fprintf (stderr,
+               "Error: running curl test depends on local libcurl version > %s\n",
+               req_version);
+      return -1;
+    }
+
+  /*
+   * enforce required gnutls/openssl version.
+   * TODO use curl version string to assert use of gnutls
+   */
+#if HTTPS_SUPPORT
+  ssl_ver = strchr (curl_ver, ' ');
+  if (ssl_ver == NULL)
+    return -1;
+  ssl_ver++;
+  if (strncmp ("GnuTLS", ssl_ver, strlen ("GNUtls")) == 0)
+    {
+      ssl_ver = strchr (ssl_ver, '/');
+      req_ssl_ver = MHD_REQ_CURL_GNUTLS_VERSION;
+    }
+  else if (strncmp ("OpenSSL", ssl_ver, strlen ("OpenSSL")) == 0)
+    {
+      ssl_ver = strchr (ssl_ver, '/');
+      req_ssl_ver = MHD_REQ_CURL_OPENSSL_VERSION;
+    }
+  else if (strncmp ("NSS", ssl_ver, strlen ("NSS")) == 0)
+    {
+      ssl_ver = strchr (ssl_ver, '/');
+      req_ssl_ver = MHD_REQ_CURL_NSS_VERSION;
+    }
+  else
+    {
+      fprintf (stderr, "Error: unrecognized curl ssl library\n");
+      return -1;
+    }
+  if (ssl_ver == NULL)
+    return -1;
+  ssl_ver++;
+  if ( (NULL == parse_version_string (req_ssl_ver, &rq_major, &rq_minor, &rq_micro)) ||
+       (NULL == parse_version_string (ssl_ver, &loc_major, &loc_minor, &loc_micro)) )
+    return -1;
+
+  if ((loc_major > rq_major
+       || (loc_major == rq_major && loc_minor > rq_minor)
+       || (loc_major == rq_major && loc_minor == rq_minor
+           && loc_micro > rq_micro) || (loc_major == rq_major
+                                        && loc_minor == rq_minor
+                                        && loc_micro == rq_micro)) == 0)
+    {
+      fprintf (stderr,
+               "Error: running curl test depends on local libcurl SSL version > %s\n",
+               req_ssl_ver);
+      return -1;
+    }
+#endif
+  return 0;
+}
diff --git a/src/testcurl/gauger.h b/src/testcurl/gauger.h
new file mode 100644
index 0000000..3a0dd22
--- /dev/null
+++ b/src/testcurl/gauger.h
@@ -0,0 +1,86 @@
+/** ---------------------------------------------------------------------------
+ * This software is in the public domain, furnished "as is", without technical
+ * support, and with no warranty, express or implied, as to its usefulness for
+ * any purpose.
+ *
+ * gauger.h
+ * Interface for C programs to log remotely to a gauger server
+ *
+ * Author: Bartlomiej Polot
+ * -------------------------------------------------------------------------*/
+#ifndef __GAUGER_H__
+#define __GAUGER_H__
+
+#ifndef WINDOWS
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/wait.h>
+
+#define GAUGER(category, counter, value, unit)\
+{\
+    const char * __gauger_v[10];			\
+    char __gauger_s[32];\
+    pid_t __gauger_p;\
+    if(!(__gauger_p=fork())){\
+      if(!fork()){ \
+            sprintf(__gauger_s,"%Lf", (long double) (value));\
+            __gauger_v[0] = "gauger";\
+            __gauger_v[1] = "-n";\
+            __gauger_v[2] = counter;	\
+            __gauger_v[3] = "-d";\
+            __gauger_v[4] = __gauger_s;\
+            __gauger_v[5] = "-u";\
+            __gauger_v[6] = unit;	\
+            __gauger_v[7] = "-c";\
+            __gauger_v[8] = category;	\
+            __gauger_v[9] = (char *)NULL;\
+            execvp("gauger", (char*const*) __gauger_v);	\
+            _exit(1);\
+        }else{\
+            _exit(0);\
+        }\
+    }else{\
+        waitpid(__gauger_p,NULL,0);\
+    }\
+}
+
+#define GAUGER_ID(category, counter, value, unit, id)\
+{\
+    char* __gauger_v[12];\
+    char __gauger_s[32];\
+    pid_t __gauger_p;\
+    if(!(__gauger_p=fork())){\
+        if(!fork()){\
+            sprintf(__gauger_s,"%Lf", (long double) (value));\
+            __gauger_v[0] = "gauger";\
+            __gauger_v[1] = "-n";\
+            __gauger_v[2] = counter;\
+            __gauger_v[3] = "-d";\
+            __gauger_v[4] = __gauger_s;\
+            __gauger_v[5] = "-u";\
+            __gauger_v[6] = unit;\
+            __gauger_v[7] = "-i";\
+            __gauger_v[8] = id;\
+            __gauger_v[9] = "-c";\
+            __gauger_v[10] = category;\
+            __gauger_v[11] = (char *)NULL;\
+            execvp("gauger",__gauger_v);\
+            perror("gauger");\
+            _exit(1);\
+        }else{\
+            _exit(0);\
+        }\
+    }else{\
+        waitpid(__gauger_p,NULL,0);\
+    }\
+}
+
+#else
+
+#define GAUGER_ID(category, counter, value, unit, id) {}
+#define GAUGER(category, counter, value, unit) {}
+
+#endif // WINDOWS
+
+#endif
diff --git a/src/testcurl/https/Makefile.am b/src/testcurl/https/Makefile.am
new file mode 100644
index 0000000..47deb70
--- /dev/null
+++ b/src/testcurl/https/Makefile.am
@@ -0,0 +1,153 @@
+# This Makefile.am is in the public domain
+SUBDIRS = .
+
+if USE_COVERAGE
+  AM_CFLAGS = --coverage
+endif
+
+if HAVE_GNUTLS_SNI
+  TEST_HTTPS_SNI = test_https_sni
+endif
+
+if HAVE_POSIX_THREADS
+  HTTPS_PARALLEL_TESTS = test_https_get_parallel \
+  test_https_get_parallel_threads
+endif
+
+CPU_COUNT_DEF = -DCPU_COUNT=$(CPU_COUNT)
+
+AM_CPPFLAGS = \
+  -I$(top_srcdir)/src/include \
+  -I$(top_srcdir)/src/microhttpd \
+  -I$(top_srcdir)/src/platform \
+  $(LIBCURL_CPPFLAGS) $(GNUTLS_CPPFLAGS)
+
+check_PROGRAMS = \
+  test_tls_options \
+  test_tls_authentication \
+  test_https_multi_daemon \
+  test_https_get \
+  $(TEST_HTTPS_SNI) \
+  test_https_get_select \
+  $(HTTPS_PARALLEL_TESTS) \
+  test_https_session_info \
+  test_https_time_out \
+  test_empty_response
+
+EXTRA_DIST = cert.pem key.pem tls_test_keys.h tls_test_common.h \
+  host1.crt host1.key host2.crt host2.key
+
+TESTS = \
+  test_tls_options \
+  test_https_multi_daemon \
+  test_https_get \
+  $(TEST_HTTPS_SNI) \
+  test_https_get_select \
+  $(HTTPS_PARALLEL_TESTS) \
+  test_https_session_info \
+  test_https_time_out \
+  test_tls_authentication \
+  test_empty_response
+
+
+test_https_time_out_SOURCES = \
+  test_https_time_out.c \
+  tls_test_common.c
+test_https_time_out_LDADD  = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_tls_options_SOURCES = \
+  test_tls_options.c \
+  tls_test_common.c
+test_tls_options_LDADD = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_https_get_parallel_SOURCES = \
+  test_https_get_parallel.c \
+  tls_test_common.c
+test_https_get_parallel_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(CPU_COUNT_DEF)
+test_https_get_parallel_CFLAGS = \
+  $(PTHREAD_CFLAGS) $(AM_CFLAGS)
+test_https_get_parallel_LDADD = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(PTHREAD_LIBS) $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_empty_response_SOURCES = \
+  test_empty_response.c \
+  tls_test_common.c
+test_empty_response_LDADD = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_https_get_parallel_threads_SOURCES = \
+  test_https_get_parallel_threads.c \
+  tls_test_common.c
+test_https_get_parallel_threads_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(CPU_COUNT_DEF)
+test_https_get_parallel_threads_CFLAGS = \
+  $(PTHREAD_CFLAGS) $(AM_CFLAGS)
+test_https_get_parallel_threads_LDADD = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(PTHREAD_LIBS) $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_tls_authentication_SOURCES = \
+  test_tls_authentication.c \
+  tls_test_common.c
+test_tls_authentication_LDADD  = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_https_session_info_SOURCES = \
+  test_https_session_info.c \
+  tls_test_common.c
+test_https_session_info_LDADD  = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_https_multi_daemon_SOURCES = \
+  test_https_multi_daemon.c \
+  tls_test_common.c
+test_https_multi_daemon_LDADD  = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_https_get_SOURCES = \
+  test_https_get.c \
+  tls_test_common.c
+test_https_get_LDADD  = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+if HAVE_GNUTLS_SNI
+test_https_sni_SOURCES = \
+  test_https_sni.c \
+  tls_test_common.c
+test_https_sni_CPPFLAGS = \
+  $(AM_CPPFLAGS) \
+  -DABS_SRCDIR=\"$(abs_srcdir)\"
+test_https_sni_LDADD  = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+endif
+
+test_https_get_select_SOURCES = \
+  test_https_get_select.c \
+  tls_test_common.c
+test_https_get_select_LDADD  = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
diff --git a/src/testcurl/https/Makefile.in b/src/testcurl/https/Makefile.in
new file mode 100644
index 0000000..8815b21
--- /dev/null
+++ b/src/testcurl/https/Makefile.in
@@ -0,0 +1,1578 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 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 = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+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@
+check_PROGRAMS = test_tls_options$(EXEEXT) \
+	test_tls_authentication$(EXEEXT) \
+	test_https_multi_daemon$(EXEEXT) test_https_get$(EXEEXT) \
+	$(am__EXEEXT_1) test_https_get_select$(EXEEXT) $(am__EXEEXT_2) \
+	test_https_session_info$(EXEEXT) test_https_time_out$(EXEEXT) \
+	test_empty_response$(EXEEXT)
+TESTS = test_tls_options$(EXEEXT) test_https_multi_daemon$(EXEEXT) \
+	test_https_get$(EXEEXT) $(am__EXEEXT_1) \
+	test_https_get_select$(EXEEXT) $(am__EXEEXT_2) \
+	test_https_session_info$(EXEEXT) test_https_time_out$(EXEEXT) \
+	test_tls_authentication$(EXEEXT) test_empty_response$(EXEEXT)
+subdir = src/testcurl/https
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp $(top_srcdir)/test-driver
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_compile_flags.m4 \
+	$(top_srcdir)/m4/ax_append_flag.m4 \
+	$(top_srcdir)/m4/ax_check_compile_flag.m4 \
+	$(top_srcdir)/m4/ax_check_link_flag.m4 \
+	$(top_srcdir)/m4/ax_check_openssl.m4 \
+	$(top_srcdir)/m4/ax_count_cpus.m4 \
+	$(top_srcdir)/m4/ax_have_epoll.m4 \
+	$(top_srcdir)/m4/ax_pthread.m4 \
+	$(top_srcdir)/m4/ax_require_defined.m4 \
+	$(top_srcdir)/m4/libcurl.m4 $(top_srcdir)/m4/libgcrypt.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)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/MHD_config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+@HAVE_GNUTLS_SNI_TRUE@am__EXEEXT_1 = test_https_sni$(EXEEXT)
+@HAVE_POSIX_THREADS_TRUE@am__EXEEXT_2 =  \
+@HAVE_POSIX_THREADS_TRUE@	test_https_get_parallel$(EXEEXT) \
+@HAVE_POSIX_THREADS_TRUE@	test_https_get_parallel_threads$(EXEEXT)
+am_test_empty_response_OBJECTS = test_empty_response.$(OBJEXT) \
+	tls_test_common.$(OBJEXT)
+test_empty_response_OBJECTS = $(am_test_empty_response_OBJECTS)
+am__DEPENDENCIES_1 =
+test_empty_response_DEPENDENCIES =  \
+	$(top_builddir)/src/testcurl/libcurl_version_check.a \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+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_test_https_get_OBJECTS = test_https_get.$(OBJEXT) \
+	tls_test_common.$(OBJEXT)
+test_https_get_OBJECTS = $(am_test_https_get_OBJECTS)
+test_https_get_DEPENDENCIES =  \
+	$(top_builddir)/src/testcurl/libcurl_version_check.a \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_test_https_get_parallel_OBJECTS =  \
+	test_https_get_parallel-test_https_get_parallel.$(OBJEXT) \
+	test_https_get_parallel-tls_test_common.$(OBJEXT)
+test_https_get_parallel_OBJECTS =  \
+	$(am_test_https_get_parallel_OBJECTS)
+test_https_get_parallel_DEPENDENCIES =  \
+	$(top_builddir)/src/testcurl/libcurl_version_check.a \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1)
+test_https_get_parallel_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(test_https_get_parallel_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
+am_test_https_get_parallel_threads_OBJECTS = test_https_get_parallel_threads-test_https_get_parallel_threads.$(OBJEXT) \
+	test_https_get_parallel_threads-tls_test_common.$(OBJEXT)
+test_https_get_parallel_threads_OBJECTS =  \
+	$(am_test_https_get_parallel_threads_OBJECTS)
+test_https_get_parallel_threads_DEPENDENCIES =  \
+	$(top_builddir)/src/testcurl/libcurl_version_check.a \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1)
+test_https_get_parallel_threads_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(test_https_get_parallel_threads_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+am_test_https_get_select_OBJECTS = test_https_get_select.$(OBJEXT) \
+	tls_test_common.$(OBJEXT)
+test_https_get_select_OBJECTS = $(am_test_https_get_select_OBJECTS)
+test_https_get_select_DEPENDENCIES =  \
+	$(top_builddir)/src/testcurl/libcurl_version_check.a \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_test_https_multi_daemon_OBJECTS =  \
+	test_https_multi_daemon.$(OBJEXT) tls_test_common.$(OBJEXT)
+test_https_multi_daemon_OBJECTS =  \
+	$(am_test_https_multi_daemon_OBJECTS)
+test_https_multi_daemon_DEPENDENCIES =  \
+	$(top_builddir)/src/testcurl/libcurl_version_check.a \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_test_https_session_info_OBJECTS =  \
+	test_https_session_info.$(OBJEXT) tls_test_common.$(OBJEXT)
+test_https_session_info_OBJECTS =  \
+	$(am_test_https_session_info_OBJECTS)
+test_https_session_info_DEPENDENCIES =  \
+	$(top_builddir)/src/testcurl/libcurl_version_check.a \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am__test_https_sni_SOURCES_DIST = test_https_sni.c tls_test_common.c
+@HAVE_GNUTLS_SNI_TRUE@am_test_https_sni_OBJECTS =  \
+@HAVE_GNUTLS_SNI_TRUE@	test_https_sni-test_https_sni.$(OBJEXT) \
+@HAVE_GNUTLS_SNI_TRUE@	test_https_sni-tls_test_common.$(OBJEXT)
+test_https_sni_OBJECTS = $(am_test_https_sni_OBJECTS)
+@HAVE_GNUTLS_SNI_TRUE@test_https_sni_DEPENDENCIES = $(top_builddir)/src/testcurl/libcurl_version_check.a \
+@HAVE_GNUTLS_SNI_TRUE@	$(top_builddir)/src/microhttpd/libmicrohttpd.la \
+@HAVE_GNUTLS_SNI_TRUE@	$(am__DEPENDENCIES_1) \
+@HAVE_GNUTLS_SNI_TRUE@	$(am__DEPENDENCIES_1)
+am_test_https_time_out_OBJECTS = test_https_time_out.$(OBJEXT) \
+	tls_test_common.$(OBJEXT)
+test_https_time_out_OBJECTS = $(am_test_https_time_out_OBJECTS)
+test_https_time_out_DEPENDENCIES =  \
+	$(top_builddir)/src/testcurl/libcurl_version_check.a \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_test_tls_authentication_OBJECTS =  \
+	test_tls_authentication.$(OBJEXT) tls_test_common.$(OBJEXT)
+test_tls_authentication_OBJECTS =  \
+	$(am_test_tls_authentication_OBJECTS)
+test_tls_authentication_DEPENDENCIES =  \
+	$(top_builddir)/src/testcurl/libcurl_version_check.a \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_test_tls_options_OBJECTS = test_tls_options.$(OBJEXT) \
+	tls_test_common.$(OBJEXT)
+test_tls_options_OBJECTS = $(am_test_tls_options_OBJECTS)
+test_tls_options_DEPENDENCIES =  \
+	$(top_builddir)/src/testcurl/libcurl_version_check.a \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_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__depfiles_maybe = depfiles
+am__mv = mv -f
+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 = $(test_empty_response_SOURCES) $(test_https_get_SOURCES) \
+	$(test_https_get_parallel_SOURCES) \
+	$(test_https_get_parallel_threads_SOURCES) \
+	$(test_https_get_select_SOURCES) \
+	$(test_https_multi_daemon_SOURCES) \
+	$(test_https_session_info_SOURCES) $(test_https_sni_SOURCES) \
+	$(test_https_time_out_SOURCES) \
+	$(test_tls_authentication_SOURCES) $(test_tls_options_SOURCES)
+DIST_SOURCES = $(test_empty_response_SOURCES) \
+	$(test_https_get_SOURCES) $(test_https_get_parallel_SOURCES) \
+	$(test_https_get_parallel_threads_SOURCES) \
+	$(test_https_get_select_SOURCES) \
+	$(test_https_multi_daemon_SOURCES) \
+	$(test_https_session_info_SOURCES) \
+	$(am__test_https_sni_SOURCES_DIST) \
+	$(test_https_time_out_SOURCES) \
+	$(test_tls_authentication_SOURCES) $(test_tls_options_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-recursive dvi-recursive html-recursive info-recursive \
+	install-data-recursive install-dvi-recursive \
+	install-exec-recursive install-html-recursive \
+	install-info-recursive install-pdf-recursive \
+	install-ps-recursive install-recursive installcheck-recursive \
+	installdirs-recursive pdf-recursive ps-recursive \
+	tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	check recheck distdir
+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__tty_colors_dummy = \
+  mgn= red= grn= lgn= blu= brg= std=; \
+  am__color_tests=no
+am__tty_colors = { \
+  $(am__tty_colors_dummy); \
+  if test "X$(AM_COLOR_TESTS)" = Xno; then \
+    am__color_tests=no; \
+  elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+    am__color_tests=yes; \
+  elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+    am__color_tests=yes; \
+  fi; \
+  if test $$am__color_tests = yes; then \
+    red=''; \
+    grn=''; \
+    lgn=''; \
+    blu=''; \
+    mgn=''; \
+    brg=''; \
+    std=''; \
+  fi; \
+}
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__recheck_rx = ^[ 	]*:recheck:[ 	]*
+am__global_test_result_rx = ^[ 	]*:global-test-result:[ 	]*
+am__copy_in_global_log_rx = ^[ 	]*:copy-in-global-log:[ 	]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+  recheck = 1; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+        { \
+          if ((getline line2 < ($$0 ".log")) < 0) \
+	    recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+        { \
+          recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+        { \
+          break; \
+        } \
+    }; \
+  if (recheck) \
+    print $$0; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+  print "fatal: making $@: " msg | "cat >&2"; \
+  exit 1; \
+} \
+function rst_section(header) \
+{ \
+  print header; \
+  len = length(header); \
+  for (i = 1; i <= len; i = i + 1) \
+    printf "="; \
+  printf "\n\n"; \
+} \
+{ \
+  copy_in_global_log = 1; \
+  global_test_result = "RUN"; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+         fatal("failed to read from " $$0 ".trs"); \
+      if (line ~ /$(am__global_test_result_rx)/) \
+        { \
+          sub("$(am__global_test_result_rx)", "", line); \
+          sub("[ 	]*$$", "", line); \
+          global_test_result = line; \
+        } \
+      else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+        copy_in_global_log = 0; \
+    }; \
+  if (copy_in_global_log) \
+    { \
+      rst_section(global_test_result ": " $$0); \
+      while ((rc = (getline line < ($$0 ".log"))) != 0) \
+      { \
+        if (rc < 0) \
+          fatal("failed to read from " $$0 ".log"); \
+        print line; \
+      }; \
+      printf "\n"; \
+    }; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/   &   /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this.  Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+  --color-tests "$$am__color_tests" \
+  --enable-hard-errors "$$am__enable_hard_errors" \
+  --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test.  Creates the
+# directory for the log if needed.  Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log.  Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT.  Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup);					\
+$(am__vpath_adj_setup) $(am__vpath_adj)			\
+$(am__tty_colors);					\
+srcdir=$(srcdir); export srcdir;			\
+case "$@" in						\
+  */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;;	\
+    *) am__odir=.;; 					\
+esac;							\
+test "x$$am__odir" = x"." || test -d "$$am__odir" 	\
+  || $(MKDIR_P) "$$am__odir" || exit $$?;		\
+if test -f "./$$f"; then dir=./;			\
+elif test -f "$$f"; then dir=;				\
+else dir="$(srcdir)/"; fi;				\
+tst=$$dir$$f; log='$@'; 				\
+if test -n '$(DISABLE_HARD_ERRORS)'; then		\
+  am__enable_hard_errors=no; 				\
+else							\
+  am__enable_hard_errors=yes; 				\
+fi; 							\
+case " $(XFAIL_TESTS) " in				\
+  *[\ \	]$$f[\ \	]* | *[\ \	]$$dir$$f[\ \	]*) \
+    am__expect_failure=yes;;				\
+  *)							\
+    am__expect_failure=no;;				\
+esac; 							\
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed).  The result is saved in the shell variable
+# '$bases'.  This honors runtime overriding of TESTS and TEST_LOGS.  Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+  bases='$(TEST_LOGS)'; \
+  bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+  bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+  case '$@' in \
+    */*) \
+      case '$*' in \
+        */*) b='$*';; \
+          *) b=`echo '$@' | sed 's/\.log$$//'`; \
+       esac;; \
+    *) \
+      b='$*';; \
+  esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+	$(TEST_LOG_FLAGS)
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPU_COUNT = @CPU_COUNT@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_CPPFLAGS = @GNUTLS_CPPFLAGS@
+GNUTLS_LDFLAGS = @GNUTLS_LDFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+HAVE_CURL_BINARY = @HAVE_CURL_BINARY@
+HAVE_MAKEINFO_BINARY = @HAVE_MAKEINFO_BINARY@
+HIDDEN_VISIBILITY_CFLAGS = @HIDDEN_VISIBILITY_CFLAGS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSPDY_VERSION_AGE = @LIBSPDY_VERSION_AGE@
+LIBSPDY_VERSION_CURRENT = @LIBSPDY_VERSION_CURRENT@
+LIBSPDY_VERSION_REVISION = @LIBSPDY_VERSION_REVISION@
+LIBTOOL = @LIBTOOL@
+LIB_VERSION_AGE = @LIB_VERSION_AGE@
+LIB_VERSION_CURRENT = @LIB_VERSION_CURRENT@
+LIB_VERSION_REVISION = @LIB_VERSION_REVISION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MHD_LIBDEPS = @MHD_LIBDEPS@
+MHD_LIBDEPS_PKGCFG = @MHD_LIBDEPS_PKGCFG@
+MHD_LIB_CFLAGS = @MHD_LIB_CFLAGS@
+MHD_LIB_CPPFLAGS = @MHD_LIB_CPPFLAGS@
+MHD_LIB_LDFLAGS = @MHD_LIB_LDFLAGS@
+MHD_REQ_PRIVATE = @MHD_REQ_PRIVATE@
+MKDIR_P = @MKDIR_P@
+MS_LIB_TOOL = @MS_LIB_TOOL@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_INCLUDES = @OPENSSL_INCLUDES@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@
+PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@
+PACKAGE_VERSION_SUBMINOR = @PACKAGE_VERSION_SUBMINOR@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPDY_LIBDEPS = @SPDY_LIBDEPS@
+SPDY_LIB_CFLAGS = @SPDY_LIB_CFLAGS@
+SPDY_LIB_CPPFLAGS = @SPDY_LIB_CPPFLAGS@
+SPDY_LIB_LDFLAGS = @SPDY_LIB_LDFLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+_libcurl_config = @_libcurl_config@
+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_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@
+ax_pthread_config = @ax_pthread_config@
+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@
+have_socat = @have_socat@
+have_zzuf = @have_zzuf@
+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@
+lt_cv_objdir = @lt_cv_objdir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# This Makefile.am is in the public domain
+SUBDIRS = .
+@USE_COVERAGE_TRUE@AM_CFLAGS = --coverage
+@HAVE_GNUTLS_SNI_TRUE@TEST_HTTPS_SNI = test_https_sni
+@HAVE_POSIX_THREADS_TRUE@HTTPS_PARALLEL_TESTS = test_https_get_parallel \
+@HAVE_POSIX_THREADS_TRUE@  test_https_get_parallel_threads
+
+CPU_COUNT_DEF = -DCPU_COUNT=$(CPU_COUNT)
+AM_CPPFLAGS = \
+  -I$(top_srcdir)/src/include \
+  -I$(top_srcdir)/src/microhttpd \
+  -I$(top_srcdir)/src/platform \
+  $(LIBCURL_CPPFLAGS) $(GNUTLS_CPPFLAGS)
+
+EXTRA_DIST = cert.pem key.pem tls_test_keys.h tls_test_common.h \
+  host1.crt host1.key host2.crt host2.key
+
+test_https_time_out_SOURCES = \
+  test_https_time_out.c \
+  tls_test_common.c
+
+test_https_time_out_LDADD = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_tls_options_SOURCES = \
+  test_tls_options.c \
+  tls_test_common.c
+
+test_tls_options_LDADD = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_https_get_parallel_SOURCES = \
+  test_https_get_parallel.c \
+  tls_test_common.c
+
+test_https_get_parallel_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(CPU_COUNT_DEF)
+
+test_https_get_parallel_CFLAGS = \
+  $(PTHREAD_CFLAGS) $(AM_CFLAGS)
+
+test_https_get_parallel_LDADD = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(PTHREAD_LIBS) $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_empty_response_SOURCES = \
+  test_empty_response.c \
+  tls_test_common.c
+
+test_empty_response_LDADD = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_https_get_parallel_threads_SOURCES = \
+  test_https_get_parallel_threads.c \
+  tls_test_common.c
+
+test_https_get_parallel_threads_CPPFLAGS = \
+  $(AM_CPPFLAGS) $(CPU_COUNT_DEF)
+
+test_https_get_parallel_threads_CFLAGS = \
+  $(PTHREAD_CFLAGS) $(AM_CFLAGS)
+
+test_https_get_parallel_threads_LDADD = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(PTHREAD_LIBS) $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_tls_authentication_SOURCES = \
+  test_tls_authentication.c \
+  tls_test_common.c
+
+test_tls_authentication_LDADD = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_https_session_info_SOURCES = \
+  test_https_session_info.c \
+  tls_test_common.c
+
+test_https_session_info_LDADD = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_https_multi_daemon_SOURCES = \
+  test_https_multi_daemon.c \
+  tls_test_common.c
+
+test_https_multi_daemon_LDADD = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_https_get_SOURCES = \
+  test_https_get.c \
+  tls_test_common.c
+
+test_https_get_LDADD = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+@HAVE_GNUTLS_SNI_TRUE@test_https_sni_SOURCES = \
+@HAVE_GNUTLS_SNI_TRUE@  test_https_sni.c \
+@HAVE_GNUTLS_SNI_TRUE@  tls_test_common.c
+
+@HAVE_GNUTLS_SNI_TRUE@test_https_sni_CPPFLAGS = \
+@HAVE_GNUTLS_SNI_TRUE@  $(AM_CPPFLAGS) \
+@HAVE_GNUTLS_SNI_TRUE@  -DABS_SRCDIR=\"$(abs_srcdir)\"
+
+@HAVE_GNUTLS_SNI_TRUE@test_https_sni_LDADD = \
+@HAVE_GNUTLS_SNI_TRUE@  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+@HAVE_GNUTLS_SNI_TRUE@  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+@HAVE_GNUTLS_SNI_TRUE@  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+test_https_get_select_SOURCES = \
+  test_https_get_select.c \
+  tls_test_common.c
+
+test_https_get_select_LDADD = \
+  $(top_builddir)/src/testcurl/libcurl_version_check.a \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  $(GNUTLS_LDFLAGS) $(GNUTLS_LIBS) @LIBGCRYPT_LIBS@ @LIBCURL@
+
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
+$(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) --gnu src/testcurl/https/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/testcurl/https/Makefile
+.PRECIOUS: 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__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	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-checkPROGRAMS:
+	@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+
+test_empty_response$(EXEEXT): $(test_empty_response_OBJECTS) $(test_empty_response_DEPENDENCIES) $(EXTRA_test_empty_response_DEPENDENCIES) 
+	@rm -f test_empty_response$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_empty_response_OBJECTS) $(test_empty_response_LDADD) $(LIBS)
+
+test_https_get$(EXEEXT): $(test_https_get_OBJECTS) $(test_https_get_DEPENDENCIES) $(EXTRA_test_https_get_DEPENDENCIES) 
+	@rm -f test_https_get$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_https_get_OBJECTS) $(test_https_get_LDADD) $(LIBS)
+
+test_https_get_parallel$(EXEEXT): $(test_https_get_parallel_OBJECTS) $(test_https_get_parallel_DEPENDENCIES) $(EXTRA_test_https_get_parallel_DEPENDENCIES) 
+	@rm -f test_https_get_parallel$(EXEEXT)
+	$(AM_V_CCLD)$(test_https_get_parallel_LINK) $(test_https_get_parallel_OBJECTS) $(test_https_get_parallel_LDADD) $(LIBS)
+
+test_https_get_parallel_threads$(EXEEXT): $(test_https_get_parallel_threads_OBJECTS) $(test_https_get_parallel_threads_DEPENDENCIES) $(EXTRA_test_https_get_parallel_threads_DEPENDENCIES) 
+	@rm -f test_https_get_parallel_threads$(EXEEXT)
+	$(AM_V_CCLD)$(test_https_get_parallel_threads_LINK) $(test_https_get_parallel_threads_OBJECTS) $(test_https_get_parallel_threads_LDADD) $(LIBS)
+
+test_https_get_select$(EXEEXT): $(test_https_get_select_OBJECTS) $(test_https_get_select_DEPENDENCIES) $(EXTRA_test_https_get_select_DEPENDENCIES) 
+	@rm -f test_https_get_select$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_https_get_select_OBJECTS) $(test_https_get_select_LDADD) $(LIBS)
+
+test_https_multi_daemon$(EXEEXT): $(test_https_multi_daemon_OBJECTS) $(test_https_multi_daemon_DEPENDENCIES) $(EXTRA_test_https_multi_daemon_DEPENDENCIES) 
+	@rm -f test_https_multi_daemon$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_https_multi_daemon_OBJECTS) $(test_https_multi_daemon_LDADD) $(LIBS)
+
+test_https_session_info$(EXEEXT): $(test_https_session_info_OBJECTS) $(test_https_session_info_DEPENDENCIES) $(EXTRA_test_https_session_info_DEPENDENCIES) 
+	@rm -f test_https_session_info$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_https_session_info_OBJECTS) $(test_https_session_info_LDADD) $(LIBS)
+
+test_https_sni$(EXEEXT): $(test_https_sni_OBJECTS) $(test_https_sni_DEPENDENCIES) $(EXTRA_test_https_sni_DEPENDENCIES) 
+	@rm -f test_https_sni$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_https_sni_OBJECTS) $(test_https_sni_LDADD) $(LIBS)
+
+test_https_time_out$(EXEEXT): $(test_https_time_out_OBJECTS) $(test_https_time_out_DEPENDENCIES) $(EXTRA_test_https_time_out_DEPENDENCIES) 
+	@rm -f test_https_time_out$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_https_time_out_OBJECTS) $(test_https_time_out_LDADD) $(LIBS)
+
+test_tls_authentication$(EXEEXT): $(test_tls_authentication_OBJECTS) $(test_tls_authentication_DEPENDENCIES) $(EXTRA_test_tls_authentication_DEPENDENCIES) 
+	@rm -f test_tls_authentication$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_tls_authentication_OBJECTS) $(test_tls_authentication_LDADD) $(LIBS)
+
+test_tls_options$(EXEEXT): $(test_tls_options_OBJECTS) $(test_tls_options_DEPENDENCIES) $(EXTRA_test_tls_options_DEPENDENCIES) 
+	@rm -f test_tls_options$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_tls_options_OBJECTS) $(test_tls_options_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_empty_response.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_https_get.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_https_get_parallel-test_https_get_parallel.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_https_get_parallel-tls_test_common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_https_get_parallel_threads-test_https_get_parallel_threads.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_https_get_parallel_threads-tls_test_common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_https_get_select.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_https_multi_daemon.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_https_session_info.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_https_sni-test_https_sni.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_https_sni-tls_test_common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_https_time_out.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_tls_authentication.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_tls_options.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_test_common.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+test_https_get_parallel-test_https_get_parallel.o: test_https_get_parallel.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_CFLAGS) $(CFLAGS) -MT test_https_get_parallel-test_https_get_parallel.o -MD -MP -MF $(DEPDIR)/test_https_get_parallel-test_https_get_parallel.Tpo -c -o test_https_get_parallel-test_https_get_parallel.o `test -f 'test_https_get_parallel.c' || echo '$(srcdir)/'`test_https_get_parallel.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_https_get_parallel-test_https_get_parallel.Tpo $(DEPDIR)/test_https_get_parallel-test_https_get_parallel.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_https_get_parallel.c' object='test_https_get_parallel-test_https_get_parallel.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_CFLAGS) $(CFLAGS) -c -o test_https_get_parallel-test_https_get_parallel.o `test -f 'test_https_get_parallel.c' || echo '$(srcdir)/'`test_https_get_parallel.c
+
+test_https_get_parallel-test_https_get_parallel.obj: test_https_get_parallel.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_CFLAGS) $(CFLAGS) -MT test_https_get_parallel-test_https_get_parallel.obj -MD -MP -MF $(DEPDIR)/test_https_get_parallel-test_https_get_parallel.Tpo -c -o test_https_get_parallel-test_https_get_parallel.obj `if test -f 'test_https_get_parallel.c'; then $(CYGPATH_W) 'test_https_get_parallel.c'; else $(CYGPATH_W) '$(srcdir)/test_https_get_parallel.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_https_get_parallel-test_https_get_parallel.Tpo $(DEPDIR)/test_https_get_parallel-test_https_get_parallel.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_https_get_parallel.c' object='test_https_get_parallel-test_https_get_parallel.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_CFLAGS) $(CFLAGS) -c -o test_https_get_parallel-test_https_get_parallel.obj `if test -f 'test_https_get_parallel.c'; then $(CYGPATH_W) 'test_https_get_parallel.c'; else $(CYGPATH_W) '$(srcdir)/test_https_get_parallel.c'; fi`
+
+test_https_get_parallel-tls_test_common.o: tls_test_common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_CFLAGS) $(CFLAGS) -MT test_https_get_parallel-tls_test_common.o -MD -MP -MF $(DEPDIR)/test_https_get_parallel-tls_test_common.Tpo -c -o test_https_get_parallel-tls_test_common.o `test -f 'tls_test_common.c' || echo '$(srcdir)/'`tls_test_common.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_https_get_parallel-tls_test_common.Tpo $(DEPDIR)/test_https_get_parallel-tls_test_common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tls_test_common.c' object='test_https_get_parallel-tls_test_common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_CFLAGS) $(CFLAGS) -c -o test_https_get_parallel-tls_test_common.o `test -f 'tls_test_common.c' || echo '$(srcdir)/'`tls_test_common.c
+
+test_https_get_parallel-tls_test_common.obj: tls_test_common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_CFLAGS) $(CFLAGS) -MT test_https_get_parallel-tls_test_common.obj -MD -MP -MF $(DEPDIR)/test_https_get_parallel-tls_test_common.Tpo -c -o test_https_get_parallel-tls_test_common.obj `if test -f 'tls_test_common.c'; then $(CYGPATH_W) 'tls_test_common.c'; else $(CYGPATH_W) '$(srcdir)/tls_test_common.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_https_get_parallel-tls_test_common.Tpo $(DEPDIR)/test_https_get_parallel-tls_test_common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tls_test_common.c' object='test_https_get_parallel-tls_test_common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_CFLAGS) $(CFLAGS) -c -o test_https_get_parallel-tls_test_common.obj `if test -f 'tls_test_common.c'; then $(CYGPATH_W) 'tls_test_common.c'; else $(CYGPATH_W) '$(srcdir)/tls_test_common.c'; fi`
+
+test_https_get_parallel_threads-test_https_get_parallel_threads.o: test_https_get_parallel_threads.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_threads_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_threads_CFLAGS) $(CFLAGS) -MT test_https_get_parallel_threads-test_https_get_parallel_threads.o -MD -MP -MF $(DEPDIR)/test_https_get_parallel_threads-test_https_get_parallel_threads.Tpo -c -o test_https_get_parallel_threads-test_https_get_parallel_threads.o `test -f 'test_https_get_parallel_threads.c' || echo '$(srcdir)/'`test_https_get_parallel_threads.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_https_get_parallel_threads-test_https_get_parallel_threads.Tpo $(DEPDIR)/test_https_get_parallel_threads-test_https_get_parallel_threads.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_https_get_parallel_threads.c' object='test_https_get_parallel_threads-test_https_get_parallel_threads.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_threads_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_threads_CFLAGS) $(CFLAGS) -c -o test_https_get_parallel_threads-test_https_get_parallel_threads.o `test -f 'test_https_get_parallel_threads.c' || echo '$(srcdir)/'`test_https_get_parallel_threads.c
+
+test_https_get_parallel_threads-test_https_get_parallel_threads.obj: test_https_get_parallel_threads.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_threads_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_threads_CFLAGS) $(CFLAGS) -MT test_https_get_parallel_threads-test_https_get_parallel_threads.obj -MD -MP -MF $(DEPDIR)/test_https_get_parallel_threads-test_https_get_parallel_threads.Tpo -c -o test_https_get_parallel_threads-test_https_get_parallel_threads.obj `if test -f 'test_https_get_parallel_threads.c'; then $(CYGPATH_W) 'test_https_get_parallel_threads.c'; else $(CYGPATH_W) '$(srcdir)/test_https_get_parallel_threads.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_https_get_parallel_threads-test_https_get_parallel_threads.Tpo $(DEPDIR)/test_https_get_parallel_threads-test_https_get_parallel_threads.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_https_get_parallel_threads.c' object='test_https_get_parallel_threads-test_https_get_parallel_threads.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_threads_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_threads_CFLAGS) $(CFLAGS) -c -o test_https_get_parallel_threads-test_https_get_parallel_threads.obj `if test -f 'test_https_get_parallel_threads.c'; then $(CYGPATH_W) 'test_https_get_parallel_threads.c'; else $(CYGPATH_W) '$(srcdir)/test_https_get_parallel_threads.c'; fi`
+
+test_https_get_parallel_threads-tls_test_common.o: tls_test_common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_threads_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_threads_CFLAGS) $(CFLAGS) -MT test_https_get_parallel_threads-tls_test_common.o -MD -MP -MF $(DEPDIR)/test_https_get_parallel_threads-tls_test_common.Tpo -c -o test_https_get_parallel_threads-tls_test_common.o `test -f 'tls_test_common.c' || echo '$(srcdir)/'`tls_test_common.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_https_get_parallel_threads-tls_test_common.Tpo $(DEPDIR)/test_https_get_parallel_threads-tls_test_common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tls_test_common.c' object='test_https_get_parallel_threads-tls_test_common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_threads_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_threads_CFLAGS) $(CFLAGS) -c -o test_https_get_parallel_threads-tls_test_common.o `test -f 'tls_test_common.c' || echo '$(srcdir)/'`tls_test_common.c
+
+test_https_get_parallel_threads-tls_test_common.obj: tls_test_common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_threads_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_threads_CFLAGS) $(CFLAGS) -MT test_https_get_parallel_threads-tls_test_common.obj -MD -MP -MF $(DEPDIR)/test_https_get_parallel_threads-tls_test_common.Tpo -c -o test_https_get_parallel_threads-tls_test_common.obj `if test -f 'tls_test_common.c'; then $(CYGPATH_W) 'tls_test_common.c'; else $(CYGPATH_W) '$(srcdir)/tls_test_common.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_https_get_parallel_threads-tls_test_common.Tpo $(DEPDIR)/test_https_get_parallel_threads-tls_test_common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tls_test_common.c' object='test_https_get_parallel_threads-tls_test_common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_get_parallel_threads_CPPFLAGS) $(CPPFLAGS) $(test_https_get_parallel_threads_CFLAGS) $(CFLAGS) -c -o test_https_get_parallel_threads-tls_test_common.obj `if test -f 'tls_test_common.c'; then $(CYGPATH_W) 'tls_test_common.c'; else $(CYGPATH_W) '$(srcdir)/tls_test_common.c'; fi`
+
+test_https_sni-test_https_sni.o: test_https_sni.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_sni_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_https_sni-test_https_sni.o -MD -MP -MF $(DEPDIR)/test_https_sni-test_https_sni.Tpo -c -o test_https_sni-test_https_sni.o `test -f 'test_https_sni.c' || echo '$(srcdir)/'`test_https_sni.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_https_sni-test_https_sni.Tpo $(DEPDIR)/test_https_sni-test_https_sni.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_https_sni.c' object='test_https_sni-test_https_sni.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_sni_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_https_sni-test_https_sni.o `test -f 'test_https_sni.c' || echo '$(srcdir)/'`test_https_sni.c
+
+test_https_sni-test_https_sni.obj: test_https_sni.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_sni_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_https_sni-test_https_sni.obj -MD -MP -MF $(DEPDIR)/test_https_sni-test_https_sni.Tpo -c -o test_https_sni-test_https_sni.obj `if test -f 'test_https_sni.c'; then $(CYGPATH_W) 'test_https_sni.c'; else $(CYGPATH_W) '$(srcdir)/test_https_sni.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_https_sni-test_https_sni.Tpo $(DEPDIR)/test_https_sni-test_https_sni.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='test_https_sni.c' object='test_https_sni-test_https_sni.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_sni_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_https_sni-test_https_sni.obj `if test -f 'test_https_sni.c'; then $(CYGPATH_W) 'test_https_sni.c'; else $(CYGPATH_W) '$(srcdir)/test_https_sni.c'; fi`
+
+test_https_sni-tls_test_common.o: tls_test_common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_sni_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_https_sni-tls_test_common.o -MD -MP -MF $(DEPDIR)/test_https_sni-tls_test_common.Tpo -c -o test_https_sni-tls_test_common.o `test -f 'tls_test_common.c' || echo '$(srcdir)/'`tls_test_common.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_https_sni-tls_test_common.Tpo $(DEPDIR)/test_https_sni-tls_test_common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tls_test_common.c' object='test_https_sni-tls_test_common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_sni_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_https_sni-tls_test_common.o `test -f 'tls_test_common.c' || echo '$(srcdir)/'`tls_test_common.c
+
+test_https_sni-tls_test_common.obj: tls_test_common.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_sni_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_https_sni-tls_test_common.obj -MD -MP -MF $(DEPDIR)/test_https_sni-tls_test_common.Tpo -c -o test_https_sni-tls_test_common.obj `if test -f 'tls_test_common.c'; then $(CYGPATH_W) 'tls_test_common.c'; else $(CYGPATH_W) '$(srcdir)/tls_test_common.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/test_https_sni-tls_test_common.Tpo $(DEPDIR)/test_https_sni-tls_test_common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tls_test_common.c' object='test_https_sni-tls_test_common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_https_sni_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_https_sni-tls_test_common.obj `if test -f 'tls_test_common.c'; then $(CYGPATH_W) 'tls_test_common.c'; else $(CYGPATH_W) '$(srcdir)/tls_test_common.c'; fi`
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+#     (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'.  Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+	rm -f $< $@
+	$(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+	@:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+	@$(am__set_TESTS_bases); \
+	am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+	redo_bases=`for i in $$bases; do \
+	              am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+	            done`; \
+	if test -n "$$redo_bases"; then \
+	  redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+	  redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+	  if $(am__make_dryrun); then :; else \
+	    rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
+	  fi; \
+	fi; \
+	if test -n "$$am__remaking_logs"; then \
+	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+	       "recursion detected" >&2; \
+	else \
+	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+	fi; \
+	if $(am__make_dryrun); then :; else \
+	  st=0;  \
+	  errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+	  for i in $$redo_bases; do \
+	    test -f $$i.trs && test -r $$i.trs \
+	      || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+	    test -f $$i.log && test -r $$i.log \
+	      || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+	  done; \
+	  test $$st -eq 0 || exit 1; \
+	fi
+	@$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+	ws='[ 	]'; \
+	results=`for b in $$bases; do echo $$b.trs; done`; \
+	test -n "$$results" || results=/dev/null; \
+	all=`  grep "^$$ws*:test-result:"           $$results | wc -l`; \
+	pass=` grep "^$$ws*:test-result:$$ws*PASS"  $$results | wc -l`; \
+	fail=` grep "^$$ws*:test-result:$$ws*FAIL"  $$results | wc -l`; \
+	skip=` grep "^$$ws*:test-result:$$ws*SKIP"  $$results | wc -l`; \
+	xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+	xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+	error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+	if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+	  success=true; \
+	else \
+	  success=false; \
+	fi; \
+	br='==================='; br=$$br$$br$$br$$br; \
+	result_count () \
+	{ \
+	    if test x"$$1" = x"--maybe-color"; then \
+	      maybe_colorize=yes; \
+	    elif test x"$$1" = x"--no-color"; then \
+	      maybe_colorize=no; \
+	    else \
+	      echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+	    fi; \
+	    shift; \
+	    desc=$$1 count=$$2; \
+	    if test $$maybe_colorize = yes && test $$count -gt 0; then \
+	      color_start=$$3 color_end=$$std; \
+	    else \
+	      color_start= color_end=; \
+	    fi; \
+	    echo "$${color_start}# $$desc $$count$${color_end}"; \
+	}; \
+	create_testsuite_report () \
+	{ \
+	  result_count $$1 "TOTAL:" $$all   "$$brg"; \
+	  result_count $$1 "PASS: " $$pass  "$$grn"; \
+	  result_count $$1 "SKIP: " $$skip  "$$blu"; \
+	  result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+	  result_count $$1 "FAIL: " $$fail  "$$red"; \
+	  result_count $$1 "XPASS:" $$xpass "$$red"; \
+	  result_count $$1 "ERROR:" $$error "$$mgn"; \
+	}; \
+	{								\
+	  echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" |	\
+	    $(am__rst_title);						\
+	  create_testsuite_report --no-color;				\
+	  echo;								\
+	  echo ".. contents:: :depth: 2";				\
+	  echo;								\
+	  for b in $$bases; do echo $$b; done				\
+	    | $(am__create_global_log);					\
+	} >$(TEST_SUITE_LOG).tmp || exit 1;				\
+	mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG);			\
+	if $$success; then						\
+	  col="$$grn";							\
+	 else								\
+	  col="$$red";							\
+	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
+	fi;								\
+	echo "$${col}$$br$${std}"; 					\
+	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
+	echo "$${col}$$br$${std}"; 					\
+	create_testsuite_report --maybe-color;				\
+	echo "$$col$$br$$std";						\
+	if $$success; then :; else					\
+	  echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}";		\
+	  if test -n "$(PACKAGE_BUGREPORT)"; then			\
+	    echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}";	\
+	  fi;								\
+	  echo "$$col$$br$$std";					\
+	fi;								\
+	$$success || exit 1
+
+check-TESTS:
+	@list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
+	@list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+	log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+	exit $$?;
+recheck: all $(check_PROGRAMS)
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	bases=`for i in $$bases; do echo $$i; done \
+	         | $(am__list_recheck_tests)` || exit 1; \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	log_list=`echo $$log_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+	        am__force_recheck=am--force-recheck \
+	        TEST_LOGS="$$log_list"; \
+	exit $$?
+test_tls_options.log: test_tls_options$(EXEEXT)
+	@p='test_tls_options$(EXEEXT)'; \
+	b='test_tls_options'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_https_multi_daemon.log: test_https_multi_daemon$(EXEEXT)
+	@p='test_https_multi_daemon$(EXEEXT)'; \
+	b='test_https_multi_daemon'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_https_get.log: test_https_get$(EXEEXT)
+	@p='test_https_get$(EXEEXT)'; \
+	b='test_https_get'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_https_sni.log: test_https_sni$(EXEEXT)
+	@p='test_https_sni$(EXEEXT)'; \
+	b='test_https_sni'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_https_get_select.log: test_https_get_select$(EXEEXT)
+	@p='test_https_get_select$(EXEEXT)'; \
+	b='test_https_get_select'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_https_get_parallel.log: test_https_get_parallel$(EXEEXT)
+	@p='test_https_get_parallel$(EXEEXT)'; \
+	b='test_https_get_parallel'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_https_get_parallel_threads.log: test_https_get_parallel_threads$(EXEEXT)
+	@p='test_https_get_parallel_threads$(EXEEXT)'; \
+	b='test_https_get_parallel_threads'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_https_session_info.log: test_https_session_info$(EXEEXT)
+	@p='test_https_session_info$(EXEEXT)'; \
+	b='test_https_session_info'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_https_time_out.log: test_https_time_out$(EXEEXT)
+	@p='test_https_time_out$(EXEEXT)'; \
+	b='test_https_time_out'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_tls_authentication.log: test_tls_authentication$(EXEEXT)
+	@p='test_tls_authentication$(EXEEXT)'; \
+	b='test_tls_authentication'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_empty_response.log: test_empty_response$(EXEEXT)
+	@p='test_empty_response$(EXEEXT)'; \
+	b='test_empty_response'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+	@p='$<'; \
+	$(am__set_b); \
+	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@	@p='$<'; \
+@am__EXEEXT_TRUE@	$(am__set_b); \
+@am__EXEEXT_TRUE@	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@	--log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@	"$$tst" $(AM_TESTS_FD_REDIRECT)
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    $(am__make_dryrun) \
+	      || test -d "$(distdir)/$$subdir" \
+	      || $(MKDIR_P) "$(distdir)/$$subdir" \
+	      || exit 1; \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+	-test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+	-test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+	-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-checkPROGRAMS clean-generic clean-libtool \
+	mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) check-am install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+	check-TESTS check-am clean clean-checkPROGRAMS clean-generic \
+	clean-libtool 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 \
+	installdirs-am maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \
+	uninstall uninstall-am
+
+
+# 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/src/testcurl/https/cert.pem b/src/testcurl/https/cert.pem
new file mode 100644
index 0000000..2c766df
--- /dev/null
+++ b/src/testcurl/https/cert.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICpjCCAZCgAwIBAgIESEPtjjALBgkqhkiG9w0BAQUwADAeFw0wODA2MDIxMjU0
+MzhaFw0wOTA2MDIxMjU0NDZaMAAwggEfMAsGCSqGSIb3DQEBAQOCAQ4AMIIBCQKC
+AQC03TyUvK5HmUAirRp067taIEO4bibh5nqolUoUdo/LeblMQV+qnrv/RNAMTx5X
+fNLZ45/kbM9geF8qY0vsPyQvP4jumzK0LOJYuIwmHaUm9vbXnYieILiwCuTgjaud
+3VkZDoQ9fteIo+6we9UTpVqZpxpbLulBMh/VsvX0cPJ1VFC7rT59o9hAUlFf9jX/
+GmKdYI79MtgVx0OPBjmmSD6kicBBfmfgkO7bIGwlRtsIyMznxbHu6VuoX/eVxrTv
+rmCwgEXLWRZ6ru8MQl5YfqeGXXRVwMeXU961KefbuvmEPccgCxm8FZ1C1cnDHFXh
+siSgAzMBjC/b6KVhNQ4KnUdZAgMBAAGjLzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0O
+BBYEFJcUvpjvE5fF/yzUshkWDpdYiQh/MAsGCSqGSIb3DQEBBQOCAQEARP7eKSB2
+RNd6XjEjK0SrxtoTnxS3nw9sfcS7/qD1+XHdObtDFqGNSjGYFB3Gpx8fpQhCXdoN
+8QUs3/5ZVa5yjZMQewWBgz8kNbnbH40F2y81MHITxxCe1Y+qqHWwVaYLsiOTqj2/
+0S3QjEJ9tvklmg7JX09HC4m5QRYfWBeQLD1u8ZjA1Sf1xJriomFVyRLI2VPO2bNe
+JDMXWuP+8kMC7gEvUnJ7A92Y2yrhu3QI3bjPk8uSpHea19Q77tul1UVBJ5g+zpH3
+OsF5p0MyaVf09GTzcLds5nE/osTdXGUyHJapWReVmPm3Zn6gqYlnzD99z+DPIgIV
+RhZvQx74NQnS6g==
+-----END CERTIFICATE-----
diff --git a/src/testcurl/https/host1.crt b/src/testcurl/https/host1.crt
new file mode 100644
index 0000000..d9b8b99
--- /dev/null
+++ b/src/testcurl/https/host1.crt
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICWTCCAcICCQDc4McLp7j56DANBgkqhkiG9w0BAQUFADBwMQswCQYDVQQGEwJa
+WjETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMQ4wDAYDVQQDDAVob3N0MTEZMBcGCSqGSIb3DQEJARYKdGVzdEBo
+b3N0MTAgFw0xMzExMTcxNTE2MzdaGA8yMTEzMTAyNDE1MTYzN1owcDELMAkGA1UE
+BhMCWloxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp
+ZGdpdHMgUHR5IEx0ZDEOMAwGA1UEAwwFaG9zdDExGTAXBgkqhkiG9w0BCQEWCnRl
+c3RAaG9zdDEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKxYiRUzfQnekQn3
+6e+hP/mt/JEkiFzX5TV+E19ue2v4tc7lf+SoLEk2dVt5tGQkHjIGeFFNwCLrgXoi
+h3KfP4R1IYe7NFbM+lFVwPceF3inJ75dZD80BxaXQANeh0yC/DhaVJUFNaof2S4+
+7xd8zTL6M11gME+XmR8uaDvW7EBtAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAf62m
+Nstj9p9u8T5A5fRnJWfoglH/zfm7IHzht0Wi047O3NFZJ0pOPqV97HuErUA5oBGg
+qswnyRGyGMcvL08Bki7Q6NkY7K0ON3lq+ofTkIAHlOKMF+Y/otbjuIDHBfo63tmE
+uOcr8XDQGu9R0cfh+qLgicJQd/8cFBhxsL0ls6I=
+-----END CERTIFICATE-----
diff --git a/src/testcurl/https/host1.key b/src/testcurl/https/host1.key
new file mode 100644
index 0000000..f549a4d
--- /dev/null
+++ b/src/testcurl/https/host1.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCsWIkVM30J3pEJ9+nvoT/5rfyRJIhc1+U1fhNfbntr+LXO5X/k
+qCxJNnVbebRkJB4yBnhRTcAi64F6Iodynz+EdSGHuzRWzPpRVcD3Hhd4pye+XWQ/
+NAcWl0ADXodMgvw4WlSVBTWqH9kuPu8XfM0y+jNdYDBPl5kfLmg71uxAbQIDAQAB
+AoGBAJvq9QmjLSnymtCj4pYSEai2iNpebKdiAlEkoC4j67DArupgohWhN398ryt0
+rYgzTMYBKHSVnI969AYkmtlNzM1yNckRQb/G/tWrkl9re28y2nbAExtHbvLoTk2C
+a/EEl1Op+JZNzLoSje7IQMVZoArD3d4aUbfux4XzlO2eRNmZAkEA2pV49QgcOTOJ
+PrR5cgekonNdeMtkbZm9dhxgDk9IsYkC0iOxjn/IbeCQN3wuTQ5/yLoiiQ/CQ8w5
+JndF/XpICwJBAMnY37BSRb+XKZeJWP0yjqyFJwzHXkh6IsoSF2OOXSixdiMpthLh
+IPzvo6Qxsnha4VvwuDxljHzQFPgMT//CTGcCQQDMs9S+LKU50JDEX4Goj43X8RBl
+cp0Poz3yYap3XDqowLYalADRgcvzUq3cuHgoA98Z3W9ASrjUg2o2ItcyBhV3AkAK
+bCBgwl7Hnc6P/I+Tw2CKl/WEO2cq5uOU+4opodg9maw39JdqMiW56cXRXJ+Sh17L
+mIpq0/OFHll21WvsEORRAkAnDDn/vmW25PSxPVY7tKKJCCkmtBeLQpySfpDgBF+O
+QvvokKs2COivc50rmOYNvD1WSsAOspdaSoZUgFw5ikti
+-----END RSA PRIVATE KEY-----
diff --git a/src/testcurl/https/host2.crt b/src/testcurl/https/host2.crt
new file mode 100644
index 0000000..ac28b0e
--- /dev/null
+++ b/src/testcurl/https/host2.crt
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICWTCCAcICCQCJ9nhDYTUBKjANBgkqhkiG9w0BAQUFADBwMQswCQYDVQQGEwJa
+WjETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMQ4wDAYDVQQDDAVob3N0MjEZMBcGCSqGSIb3DQEJARYKdGVzdEBo
+b3N0MjAgFw0xMzExMTcxNTE2NDNaGA8yMTEzMTAyNDE1MTY0M1owcDELMAkGA1UE
+BhMCWloxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp
+ZGdpdHMgUHR5IEx0ZDEOMAwGA1UEAwwFaG9zdDIxGTAXBgkqhkiG9w0BCQEWCnRl
+c3RAaG9zdDIwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALVK8QKMvU96iNL2
+66PKm6xXw9NPHDn+o1TLF1CQRxXMrBYUrObk0961+3n3Z3BXOFHKfSV4E55CpVyz
+D1Wcadlt3B9z3ke3HOi0lEa1xNJTMQK/QT3Fx/NURmNg5s9HAsqY4ocb9KHaF5Ex
+0TgC0L0aRP0cK1x2TgPEHBNcgGl9AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAEXOi
+9rSmVrTN5olIdowctr1vWbGwRCjCnAFXDsqakcDASNthr15LB5kr/mrA3olJjbZh
+o+JDvWMY6FN8r1QXW0RL9/obbHxtJpwvAmYVMY9jrR8Rpo38p4RfXlN85g3q9PVx
+5IGLaOqLf4hSnKArFL/fzXwxX9b5HBCKlXfiuqM=
+-----END CERTIFICATE-----
diff --git a/src/testcurl/https/host2.key b/src/testcurl/https/host2.key
new file mode 100644
index 0000000..4bbd617
--- /dev/null
+++ b/src/testcurl/https/host2.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQC1SvECjL1PeojS9uujypusV8PTTxw5/qNUyxdQkEcVzKwWFKzm
+5NPetft592dwVzhRyn0leBOeQqVcsw9VnGnZbdwfc95HtxzotJRGtcTSUzECv0E9
+xcfzVEZjYObPRwLKmOKHG/Sh2heRMdE4AtC9GkT9HCtcdk4DxBwTXIBpfQIDAQAB
+AoGAR5Do6TfDt69IefdNeCAQKg2PWUg+fUpfEacGciAyX5GnUSQiSReF58HxHumi
+ZL+ZlPgZRQRMwknO23Q4FnSjd66A3E9iHLqkWxRFJWME6E7zgtBrIjctnNu9uYM9
+cw4R6qmXOL7C5sK00KXF2ep8+s+JjrZz61o85QnGGRYA94ECQQDbG6f1B8NKY9T1
+1GDR/++rJbdTVQlZQcKSXMumpU6V3mEV0O9GkYaZzoYvWa3kx6c0np4karrm3QWa
+u5E0q1YdAkEA09FPcmzVvIR0+sMWca8QJ/tJUxD6qYo8vLOpO4wt4iTPhGBEU+Q5
+cgXmde3/plVsp0vYxK/NG5XZkoC1fbuC4QJATRGxRlLwsl3jLoUBeVxY5Q5jKYCj
+xS2ITwss5vUGa1jJNW9EesH9YmRudoFI1UwU2EFixtRz4Xik3ARV0vzhUQJAfabT
+50ASxqMYtczW2peMEPurMqCG4d4ES7iUMqPkcBuAErn8rntbbH19igWmOyi/rLp8
+m6jiFnQdPiAmCbEbYQJAFAKiQl2ZOe3gkSh8MaQilD8Ppog6rod4SQiSmRNsDWPi
+IxqXneaGDWhzynC9xr4SwuJ9D5VxW1phNyiveDuYXw==
+-----END RSA PRIVATE KEY-----
diff --git a/src/testcurl/https/key.pem b/src/testcurl/https/key.pem
new file mode 100644
index 0000000..a5848ee
--- /dev/null
+++ b/src/testcurl/https/key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAtN08lLyuR5lAIq0adOu7WiBDuG4m4eZ6qJVKFHaPy3m5TEFf
+qp67/0TQDE8eV3zS2eOf5GzPYHhfKmNL7D8kLz+I7psytCziWLiMJh2lJvb2152I
+niC4sArk4I2rnd1ZGQ6EPX7XiKPusHvVE6VamacaWy7pQTIf1bL19HDydVRQu60+
+faPYQFJRX/Y1/xpinWCO/TLYFcdDjwY5pkg+pInAQX5n4JDu2yBsJUbbCMjM58Wx
+7ulbqF/3lca0765gsIBFy1kWeq7vDEJeWH6nhl10VcDHl1PetSnn27r5hD3HIAsZ
+vBWdQtXJwxxV4bIkoAMzAYwv2+ilYTUOCp1HWQIDAQABAoIBAArOQv3R7gmqDspj
+lDaTFOz0C4e70QfjGMX0sWnakYnDGn6DU19iv3GnX1S072ejtgc9kcJ4e8VUO79R
+EmqpdRR7k8dJr3RTUCyjzf/C+qiCzcmhCFYGN3KRHA6MeEnkvRuBogX4i5EG1k5l
+/5t+YBTZBnqXKWlzQLKoUAiMLPg0eRWh+6q7H4N7kdWWBmTpako7TEqpIwuEnPGx
+u3EPuTR+LN6lF55WBePbCHccUHUQaXuav18NuDkcJmCiMArK9SKb+h0RqLD6oMI/
+dKD6n8cZXeMBkK+C8U/K0sN2hFHACsu30b9XfdnljgP9v+BP8GhnB0nCB6tNBCPo
+32srOwECgYEAxWh3iBT4lWqL6bZavVbnhmvtif4nHv2t2/hOs/CAq8iLAw0oWGZc
++JEZTUDMvFRlulr0kcaWra+4fN3OmJnjeuFXZq52lfMgXBIKBmoSaZpIh2aDY1Rd
+RbEse7nQl9hTEPmYspiXLGtnAXW7HuWqVfFFP3ya8rUS3t4d07Hig8ECgYEA6ou6
+OHiBRTbtDqLIv8NghARc/AqwNWgEc9PelCPe5bdCOLBEyFjqKiT2MttnSSUc2Zob
+XhYkHC6zN1Mlq30N0e3Q61YK9LxMdU1vsluXxNq2rfK1Scb1oOlOOtlbV3zA3VRF
+hV3t1nOA9tFmUrwZi0CUMWJE/zbPAyhwWotKyZkCgYEAh0kFicPdbABdrCglXVae
+SnfSjVwYkVuGd5Ze0WADvjYsVkYBHTvhgRNnRJMg+/vWz3Sf4Ps4rgUbqK8Vc20b
+AU5G6H6tlCvPRGm0ZxrwTWDHTcuKRVs+pJE8C/qWoklE/AAhjluWVoGwUMbPGuiH
+6Gf1bgHF6oj/Sq7rv/VLZ8ECgYBeq7ml05YyLuJutuwa4yzQ/MXfghzv4aVyb0F3
+QCdXR6o2IYgR6jnSewrZKlA9aPqFJrwHNR6sNXlnSmt5Fcf/RWO/qgJQGLUv3+rG
+7kuLTNDR05azSdiZc7J89ID3Bkb+z2YkV+6JUiPq/Ei1+nDBEXb/m+/HqALU/nyj
+P3gXeQKBgBusb8Rbd+KgxSA0hwY6aoRTPRt8LNvXdsB9vRcKKHUFQvxUWiUSS+L9
+/Qu1sJbrUquKOHqksV5wCnWnAKyJNJlhHuBToqQTgKXjuNmVdYSe631saiI7PHyC
+eRJ6DxULPxABytJrYCRrNqmXi5TCiqR2mtfalEMOPxz8rUU8dYyx
+-----END RSA PRIVATE KEY-----
diff --git a/src/testcurl/https/test_empty_response.c b/src/testcurl/https/test_empty_response.c
new file mode 100644
index 0000000..f9f8001
--- /dev/null
+++ b/src/testcurl/https/test_empty_response.c
@@ -0,0 +1,201 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2013 Christian Grothoff
+
+ libmicrohttpd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ libmicrohttpd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libmicrohttpd; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file test_empty_response.c
+ * @brief  Testcase for libmicrohttpd HTTPS GET operations with emtpy reply
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "microhttpd.h"
+#include <limits.h>
+#include <sys/stat.h>
+#include <curl/curl.h>
+#include <gcrypt.h>
+#include "tls_test_common.h"
+
+extern const char srv_key_pem[];
+extern const char srv_self_signed_cert_pem[];
+extern const char srv_signed_cert_pem[];
+extern const char srv_signed_key_pem[];
+
+static int oneone;
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  struct MHD_Response *response;
+  int ret;
+
+  response = MHD_create_response_from_buffer (0, NULL,
+					      MHD_RESPMEM_PERSISTENT);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+static int
+testInternalSelectGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_USE_SELECT_INTERNALLY,
+                        1082, NULL, NULL, &ahc_echo, "GET", 
+                        MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem,
+                        MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem,
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+
+  char *aes256_sha = "AES256-SHA";
+  if (curl_uses_nss_ssl() == 0)
+    {
+      aes256_sha = "rsa_aes_256_sha";
+    }
+
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "https://127.0.0.1:1082/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  /* TLS options */
+  curl_easy_setopt (c, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
+  curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST, aes256_sha);
+  curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0);
+  curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  mret = curl_multi_add_handle (multi, c);
+  if (mret != CURLM_OK)
+    {
+      curl_multi_cleanup (multi);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 1024;
+    }
+  start = time (NULL);
+  while ((time (NULL) - start < 5) && (multi != NULL))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 2048;
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select (max + 1, &rs, &ws, &es, &tv);
+      curl_multi_perform (multi, &running);
+      if (running == 0)
+        {
+          msg = curl_multi_info_read (multi, &running);
+          if (msg == NULL)
+            break;
+          if (msg->msg == CURLMSG_DONE)
+            {
+              if (msg->data.result != CURLE_OK)
+                printf ("%s failed at %s:%d: `%s'\n",
+                        "curl_multi_perform",
+                        __FILE__,
+                        __LINE__, curl_easy_strerror (msg->data.result));
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              c = NULL;
+              multi = NULL;
+            }
+        }
+    }
+  if (multi != NULL)
+    {
+      curl_multi_remove_handle (multi, c);
+      curl_easy_cleanup (c);
+      curl_multi_cleanup (multi);
+    }
+  MHD_stop_daemon (d);
+  if (cbc.pos != 0)
+    return 8192;
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  if (0 != curl_global_init (CURL_GLOBAL_ALL))
+    {
+      fprintf (stderr, "Error: %s\n", strerror (errno));
+      return -1;
+    }
+  if (0 != (errorCount = testInternalSelectGet ()))
+    fprintf (stderr, "Fail: %d\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;
+}
diff --git a/src/testcurl/https/test_https_get.c b/src/testcurl/https/test_https_get.c
new file mode 100644
index 0000000..f7957c3
--- /dev/null
+++ b/src/testcurl/https/test_https_get.c
@@ -0,0 +1,130 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2007 Christian Grothoff
+
+  libmicrohttpd is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  libmicrohttpd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with libmicrohttpd; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_https_get.c
+ * @brief  Testcase for libmicrohttpd HTTPS GET operations
+ * @author Sagie Amir
+ */
+
+#include "platform.h"
+#include "microhttpd.h"
+#include <limits.h>
+#include <sys/stat.h>
+#include <curl/curl.h>
+#include <gcrypt.h>
+#include "tls_test_common.h"
+
+extern const char srv_key_pem[];
+extern const char srv_self_signed_cert_pem[];
+extern const char srv_signed_cert_pem[];
+extern const char srv_signed_key_pem[];
+
+
+static int
+test_cipher_option (FILE * test_fd,
+		    const char *cipher_suite,
+		    int proto_version)
+{
+
+  int ret;
+  struct MHD_Daemon *d;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL |
+                        MHD_USE_DEBUG, 4233,
+                        NULL, NULL, &http_ahc, NULL,
+                        MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem,
+                        MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem,
+                        MHD_OPTION_END);
+
+  if (d == NULL)
+    {
+      fprintf (stderr, MHD_E_SERVER_INIT);
+      return -1;
+    }
+
+  ret = test_https_transfer (test_fd, cipher_suite, proto_version);
+
+  MHD_stop_daemon (d);
+  return ret;
+}
+
+
+/* perform a HTTP GET request via SSL/TLS */
+static int
+test_secure_get (FILE * test_fd,
+		 const char *cipher_suite,
+		 int proto_version)
+{
+  int ret;
+  struct MHD_Daemon *d;
+
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL |
+                        MHD_USE_DEBUG, 4233,
+                        NULL, NULL, &http_ahc, NULL,
+                        MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
+                        MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
+                        MHD_OPTION_END);
+
+  if (d == NULL)
+    {
+      fprintf (stderr, MHD_E_SERVER_INIT);
+      return -1;
+    }
+
+  ret = test_https_transfer (test_fd, cipher_suite, proto_version);
+
+  MHD_stop_daemon (d);
+  return ret;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+  const char *aes256_sha_tlsv1   = "AES256-SHA";
+  const char *des_cbc3_sha_tlsv1 = "DES-CBC3-SHA";
+
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+  if (0 != curl_global_init (CURL_GLOBAL_ALL))
+    {
+      fprintf (stderr, "Error: %s\n", strerror (errno));
+      return -1;
+    }
+
+  if (curl_uses_nss_ssl() == 0)
+    {
+      aes256_sha_tlsv1 = "rsa_aes_256_sha";
+      des_cbc3_sha_tlsv1 = "rsa_aes_128_sha";
+    }
+
+  errorCount +=
+    test_secure_get (NULL, aes256_sha_tlsv1, CURL_SSLVERSION_TLSv1);
+  errorCount +=
+    test_cipher_option (NULL, des_cbc3_sha_tlsv1, CURL_SSLVERSION_TLSv1);
+  print_test_result (errorCount, argv[0]);
+
+  curl_global_cleanup ();
+
+  return errorCount != 0;
+}
diff --git a/src/testcurl/https/test_https_get_parallel.c b/src/testcurl/https/test_https_get_parallel.c
new file mode 100644
index 0000000..166e400
--- /dev/null
+++ b/src/testcurl/https/test_https_get_parallel.c
@@ -0,0 +1,185 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2007 Christian Grothoff
+
+  libmicrohttpd is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 2, or (at your
+  option) any later version.
+
+  libmicrohttpd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with libmicrohttpd; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_https_get_parallel.c
+ * @brief  Testcase for libmicrohttpd HTTPS GET operations
+ * @author Sagie Amir
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "microhttpd.h"
+#include <sys/stat.h>
+#include <limits.h>
+#include <curl/curl.h>
+#include <pthread.h>
+#include <gcrypt.h>
+#include "tls_test_common.h"
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 4
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 4
+#endif
+
+extern const char srv_key_pem[];
+extern const char srv_self_signed_cert_pem[];
+
+int curl_check_version (const char *req_version, ...);
+
+
+/**
+ * used when spawning multiple threads executing curl server requests
+ *
+ */
+static void *
+https_transfer_thread_adapter (void *args)
+{
+  static int nonnull;
+  struct https_test_data *cargs = args;
+  int ret;
+
+  /* time spread incomming requests */
+  usleep ((useconds_t) 10.0 * ((double) rand ()) / ((double) RAND_MAX));
+  ret = test_https_transfer (NULL,
+                             cargs->cipher_suite, cargs->proto_version);
+  if (ret == 0)
+    return NULL;
+  return &nonnull;
+}
+
+
+/**
+ * Test non-parallel requests.
+ *
+ * @return: 0 upon all client requests returning '0', -1 otherwise.
+ *
+ * TODO : make client_count a parameter - number of curl client threads to spawn
+ */
+static int
+test_single_client (void *cls, const char *cipher_suite,
+                    int curl_proto_version)
+{
+  void *client_thread_ret;
+  struct https_test_data client_args =
+    { NULL, cipher_suite, curl_proto_version };
+
+  client_thread_ret = https_transfer_thread_adapter (&client_args);
+  if (client_thread_ret != NULL)
+    return -1;
+  return 0;
+}
+
+
+/**
+ * Test parallel request handling.
+ *
+ * @return: 0 upon all client requests returning '0', -1 otherwise.
+ *
+ * TODO : make client_count a parameter - numver of curl client threads to spawn
+ */
+static int
+test_parallel_clients (void * cls, const char *cipher_suite,
+                       int curl_proto_version)
+{
+  int i;
+  int client_count = (CPU_COUNT - 1);
+  void *client_thread_ret;
+  pthread_t client_arr[client_count];
+  struct https_test_data client_args =
+    { NULL, cipher_suite, curl_proto_version };
+
+  for (i = 0; i < client_count; ++i)
+    {
+      if (pthread_create (&client_arr[i], NULL,
+                          &https_transfer_thread_adapter, &client_args) != 0)
+        {
+          fprintf (stderr, "Error: failed to spawn test client threads.\n");
+          return -1;
+        }
+    }
+
+  /* check all client requests fulfilled correctly */
+  for (i = 0; i < client_count; ++i)
+    {
+      if ((pthread_join (client_arr[i], &client_thread_ret) != 0) ||
+          (client_thread_ret != NULL))
+        return -1;
+    }
+
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{  
+  unsigned int errorCount = 0;
+  const char *aes256_sha = "AES256-SHA";
+
+  /* initialize random seed used by curl clients */
+  unsigned int iseed = (unsigned int) time (NULL);
+  srand (iseed);
+  if (0 != curl_global_init (CURL_GLOBAL_ALL))
+    {
+      fprintf (stderr, "Error: %s\n", strerror (errno));
+      return -1;
+    }
+
+  if (curl_uses_nss_ssl() == 0)
+    aes256_sha = "rsa_aes_256_sha";    
+#if EPOLL_SUPPORT
+  errorCount +=
+    test_wrap ("single threaded daemon, single client, epoll", &test_single_client,
+               NULL,
+               MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL | MHD_USE_DEBUG | MHD_USE_EPOLL_LINUX_ONLY,
+               aes256_sha, CURL_SSLVERSION_TLSv1, MHD_OPTION_HTTPS_MEM_KEY,
+               srv_key_pem, MHD_OPTION_HTTPS_MEM_CERT,
+               srv_self_signed_cert_pem, MHD_OPTION_END);
+#endif
+  errorCount +=
+    test_wrap ("single threaded daemon, single client", &test_single_client,
+               NULL,
+               MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL | MHD_USE_DEBUG,
+               aes256_sha, CURL_SSLVERSION_TLSv1, MHD_OPTION_HTTPS_MEM_KEY,
+               srv_key_pem, MHD_OPTION_HTTPS_MEM_CERT,
+               srv_self_signed_cert_pem, MHD_OPTION_END);
+#if EPOLL_SUPPORT
+  errorCount +=
+    test_wrap ("single threaded daemon, parallel clients, epoll",
+               &test_parallel_clients, NULL,
+               MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL | MHD_USE_DEBUG | MHD_USE_EPOLL_LINUX_ONLY,
+               aes256_sha, CURL_SSLVERSION_TLSv1, MHD_OPTION_HTTPS_MEM_KEY,
+               srv_key_pem, MHD_OPTION_HTTPS_MEM_CERT,
+               srv_self_signed_cert_pem, MHD_OPTION_END);
+#endif
+  errorCount +=
+    test_wrap ("single threaded daemon, parallel clients",
+               &test_parallel_clients, NULL,
+               MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL | MHD_USE_DEBUG,
+               aes256_sha, CURL_SSLVERSION_TLSv1, MHD_OPTION_HTTPS_MEM_KEY,
+               srv_key_pem, MHD_OPTION_HTTPS_MEM_CERT,
+               srv_self_signed_cert_pem, MHD_OPTION_END);
+
+  curl_global_cleanup ();
+  return errorCount != 0;
+}
diff --git a/src/testcurl/https/test_https_get_parallel_threads.c b/src/testcurl/https/test_https_get_parallel_threads.c
new file mode 100644
index 0000000..4cb2128
--- /dev/null
+++ b/src/testcurl/https/test_https_get_parallel_threads.c
@@ -0,0 +1,191 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2007 Christian Grothoff
+
+  libmicrohttpd is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 2, or (at your
+  option) any later version.
+
+  libmicrohttpd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with libmicrohttpd; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file tls_thread_mode_test.c
+ * @brief  Testcase for libmicrohttpd HTTPS GET operations
+ * @author Sagie Amir
+ * @author Christian Grothoff
+ *
+ * TODO: add test for external select!
+ */
+
+#include "platform.h"
+#include "microhttpd.h"
+#include <sys/stat.h>
+#include <limits.h>
+#include <curl/curl.h>
+#include <pthread.h>
+#include <gcrypt.h>
+#include "tls_test_common.h"
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 4
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 4
+#endif
+
+extern const char srv_key_pem[];
+extern const char srv_self_signed_cert_pem[];
+
+int curl_check_version (const char *req_version, ...);
+
+/**
+ * used when spawning multiple threads executing curl server requests
+ *
+ */
+static void *
+https_transfer_thread_adapter (void *args)
+{
+  static int nonnull;
+  struct https_test_data *cargs = args;
+  int ret;
+
+  /* time spread incomming requests */
+  usleep ((useconds_t) 10.0 * ((double) rand ()) / ((double) RAND_MAX));
+  ret = test_https_transfer (cargs->cls,
+                             cargs->cipher_suite, cargs->proto_version);
+  if (ret == 0)
+    return NULL;
+  return &nonnull;
+}
+
+/**
+ * Test non-parallel requests.
+ *
+ * @return: 0 upon all client requests returning '0', -1 otherwise.
+ *
+ * TODO : make client_count a parameter - numver of curl client threads to spawn
+ */
+static int
+test_single_client (void *cls, const char *cipher_suite,
+                    int curl_proto_version)
+{
+  void *client_thread_ret;
+  struct https_test_data client_args =
+    { NULL, cipher_suite, curl_proto_version };
+
+  client_thread_ret = https_transfer_thread_adapter (&client_args);
+  if (client_thread_ret != NULL)
+    return -1;
+  return 0;
+}
+
+
+/**
+ * Test parallel request handling.
+ *
+ * @return: 0 upon all client requests returning '0', -1 otherwise.
+ *
+ * TODO : make client_count a parameter - numver of curl client threads to spawn
+ */
+static int
+test_parallel_clients (void *cls, const char *cipher_suite,
+                       int curl_proto_version)
+{
+  int i;
+  int client_count = (CPU_COUNT - 1);
+  void *client_thread_ret;
+  pthread_t client_arr[client_count];
+  struct https_test_data client_args =
+    { NULL, cipher_suite, curl_proto_version };
+
+  for (i = 0; i < client_count; ++i)
+    {
+      if (pthread_create (&client_arr[i], NULL,
+                          &https_transfer_thread_adapter, &client_args) != 0)
+        {
+          fprintf (stderr, "Error: failed to spawn test client threads.\n");
+
+          return -1;
+        }
+    }
+
+  /* check all client requests fulfilled correctly */
+  for (i = 0; i < client_count; ++i)
+    {
+      if ((pthread_join (client_arr[i], &client_thread_ret) != 0) ||
+          (client_thread_ret != NULL))
+        return -1;
+    }
+
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+  const char *ssl_version;
+
+  /* initialize random seed used by curl clients */
+  unsigned int iseed = (unsigned int) time (NULL);
+
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+  srand (iseed);
+  ssl_version = curl_version_info (CURLVERSION_NOW)->ssl_version;
+  if (NULL == ssl_version)
+  {
+    fprintf (stderr, "Curl does not support SSL.  Cannot run the test.\n");
+    return 0;
+  }
+  if (0 != strncmp (ssl_version, "GnuTLS", 6))
+  {
+    fprintf (stderr, "This test can be run only with libcurl-gnutls.\n");
+    return 0;
+  }
+  if (0 != curl_global_init (CURL_GLOBAL_ALL))
+    {
+      fprintf (stderr, "Error: %s\n", strerror (errno));
+      return -1;
+    }
+
+  char *aes256_sha = "AES256-SHA";
+  if (curl_uses_nss_ssl() == 0)
+    {
+      aes256_sha = "rsa_aes_256_sha";
+    }
+
+  errorCount +=
+    test_wrap ("multi threaded daemon, single client", &test_single_client,
+               NULL,
+               MHD_USE_SSL | MHD_USE_DEBUG | MHD_USE_THREAD_PER_CONNECTION,
+               aes256_sha, CURL_SSLVERSION_TLSv1, MHD_OPTION_HTTPS_MEM_KEY,
+               srv_key_pem, MHD_OPTION_HTTPS_MEM_CERT,
+               srv_self_signed_cert_pem, MHD_OPTION_END);
+
+  errorCount +=
+    test_wrap ("multi threaded daemon, parallel client",
+               &test_parallel_clients, NULL,
+               MHD_USE_SSL | MHD_USE_DEBUG | MHD_USE_THREAD_PER_CONNECTION,
+               aes256_sha, CURL_SSLVERSION_TLSv1, MHD_OPTION_HTTPS_MEM_KEY,
+               srv_key_pem, MHD_OPTION_HTTPS_MEM_CERT,
+               srv_self_signed_cert_pem, MHD_OPTION_END);
+
+  if (errorCount != 0)
+    fprintf (stderr, "Failed test: %s.\n", argv[0]);
+
+  curl_global_cleanup ();
+  return errorCount != 0;
+}
diff --git a/src/testcurl/https/test_https_get_select.c b/src/testcurl/https/test_https_get_select.c
new file mode 100644
index 0000000..9f9ba99
--- /dev/null
+++ b/src/testcurl/https/test_https_get_select.c
@@ -0,0 +1,228 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007 Christian Grothoff
+
+ libmicrohttpd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ libmicrohttpd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libmicrohttpd; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file test_https_get_select.c
+ * @brief  Testcase for libmicrohttpd HTTPS GET operations
+ * @author Sagie Amir
+ */
+
+#include "platform.h"
+#include "microhttpd.h"
+#include <limits.h>
+#include <sys/stat.h>
+#include <curl/curl.h>
+#include <gcrypt.h>
+#include "tls_test_common.h"
+
+extern const char srv_key_pem[];
+extern const char srv_self_signed_cert_pem[];
+extern const char srv_signed_cert_pem[];
+extern const char srv_signed_key_pem[];
+
+static int oneone;
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  *unused = NULL;
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url,
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+
+static int
+testExternalGet (int flags)
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+  const char *aes256_sha = "AES256-SHA";
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | flags,
+                        1082, NULL, NULL, &ahc_echo, "GET", 
+                        MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem,
+                        MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem,
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+
+  if (curl_uses_nss_ssl() == 0)
+    aes256_sha = "rsa_aes_256_sha";   
+
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "https://127.0.0.1:1082/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  /* TLS options */
+  curl_easy_setopt (c, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
+  curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST, aes256_sha);
+  curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0);
+  curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  mret = curl_multi_add_handle (multi, c);
+  if (mret != CURLM_OK)
+    {
+      curl_multi_cleanup (multi);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 1024;
+    }
+  start = time (NULL);
+  while ((time (NULL) - start < 5) && (multi != NULL))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 2048;
+        }
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 4096;
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select (max + 1, &rs, &ws, &es, &tv);
+      curl_multi_perform (multi, &running);
+      if (running == 0)
+        {
+          msg = curl_multi_info_read (multi, &running);
+          if (msg == NULL)
+            break;
+          if (msg->msg == CURLMSG_DONE)
+            {
+              if (msg->data.result != CURLE_OK)
+                printf ("%s failed at %s:%d: `%s'\n",
+                        "curl_multi_perform",
+                        __FILE__,
+                        __LINE__, curl_easy_strerror (msg->data.result));
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              c = NULL;
+              multi = NULL;
+            }
+        }
+      MHD_run (d);
+    }
+  if (multi != NULL)
+    {
+      curl_multi_remove_handle (multi, c);
+      curl_easy_cleanup (c);
+      curl_multi_cleanup (multi);
+    }
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 8192;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 16384;
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  if (0 != curl_global_init (CURL_GLOBAL_ALL))
+    {
+      fprintf (stderr, "Error: %s\n", strerror (errno));
+      return -1;
+    }
+#if EPOLL_SUPPORT
+  if (0 != (errorCount = testExternalGet (MHD_USE_EPOLL_LINUX_ONLY)))
+    fprintf (stderr, "Fail: %d\n", errorCount);
+#endif
+  if (0 != (errorCount = testExternalGet (0)))
+    fprintf (stderr, "Fail: %d\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;
+}
diff --git a/src/testcurl/https/test_https_multi_daemon.c b/src/testcurl/https/test_https_multi_daemon.c
new file mode 100644
index 0000000..293aff4
--- /dev/null
+++ b/src/testcurl/https/test_https_multi_daemon.c
@@ -0,0 +1,134 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007 Christian Grothoff
+
+ libmicrohttpd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ libmicrohttpd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libmicrohttpd; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file mhds_multi_daemon_test.c
+ * @brief  Testcase for libmicrohttpd multiple HTTPS daemon scenario
+ * @author Sagie Amir
+ */
+
+#include "platform.h"
+#include "microhttpd.h"
+#include <curl/curl.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <gcrypt.h>
+#include "tls_test_common.h"
+
+extern int curl_check_version (const char *req_version, ...);
+extern const char srv_key_pem[];
+extern const char srv_self_signed_cert_pem[];
+
+/*
+ * assert initiating two separate daemons and having one shut down
+ * doesn't affect the other
+ */
+static int
+test_concurent_daemon_pair (void *cls, 
+			    const char *cipher_suite,
+                            int proto_version)
+{
+
+  int ret;
+  struct MHD_Daemon *d1;
+  struct MHD_Daemon *d2;
+
+  d1 = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL |
+                         MHD_USE_DEBUG, DEAMON_TEST_PORT,
+                         NULL, NULL, &http_ahc, NULL,
+                         MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem,
+                         MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem,
+                         MHD_OPTION_END);
+
+  if (d1 == NULL)
+    {
+      fprintf (stderr, MHD_E_SERVER_INIT);
+      return -1;
+    }
+
+  d2 = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL |
+                         MHD_USE_DEBUG, DEAMON_TEST_PORT + 1,
+                         NULL, NULL, &http_ahc, NULL,
+                         MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem,
+                         MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem,
+                         MHD_OPTION_END);
+
+  if (d2 == NULL)
+    {
+      MHD_stop_daemon (d1);
+      fprintf (stderr, MHD_E_SERVER_INIT);
+      return -1;
+    }
+
+  ret =
+    test_daemon_get (NULL, cipher_suite, proto_version, DEAMON_TEST_PORT, 0);
+  ret +=
+    test_daemon_get (NULL, cipher_suite, proto_version,
+                     DEAMON_TEST_PORT + 1, 0);
+
+  MHD_stop_daemon (d2);
+  ret +=
+    test_daemon_get (NULL, cipher_suite, proto_version, DEAMON_TEST_PORT, 0);
+  MHD_stop_daemon (d1);
+  return ret;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+  FILE *cert;
+
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+  if (0 != curl_global_init (CURL_GLOBAL_ALL))
+    {
+      fprintf (stderr, "Error (code: %u). l:%d f:%s\n", errorCount, __LINE__,
+               __FUNCTION__);
+      return -1;
+    }
+  if ((cert = setup_ca_cert ()) == NULL)
+    {
+      fprintf (stderr, MHD_E_TEST_FILE_CREAT);
+      return -1;
+    }
+
+  const char *aes256_sha = "AES256-SHA";
+  if (curl_uses_nss_ssl() == 0)
+    {
+      aes256_sha = "rsa_aes_256_sha";
+    }
+  
+  errorCount +=
+    test_concurent_daemon_pair (NULL, aes256_sha, CURL_SSLVERSION_TLSv1);
+
+  print_test_result (errorCount, "concurent_daemon_pair");
+
+  curl_global_cleanup ();
+  fclose (cert);
+  if (0 != remove (ca_cert_file_name))
+    fprintf (stderr,
+	     "Failed to remove `%s'\n",
+	     ca_cert_file_name);
+  return errorCount != 0;
+}
diff --git a/src/testcurl/https/test_https_session_info.c b/src/testcurl/https/test_https_session_info.c
new file mode 100644
index 0000000..8dac253
--- /dev/null
+++ b/src/testcurl/https/test_https_session_info.c
@@ -0,0 +1,186 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007 Christian Grothoff
+
+ libmicrohttpd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ libmicrohttpd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libmicrohttpd; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file mhds_session_info_test.c
+ * @brief  Testcase for libmicrohttpd HTTPS connection querying operations
+ * @author Sagie Amir
+ */
+
+#include "platform.h"
+#include "microhttpd.h"
+#include <curl/curl.h>
+#include <gcrypt.h>
+#include "tls_test_common.h"
+
+extern int curl_check_version (const char *req_version, ...);
+extern const char srv_key_pem[];
+extern const char srv_self_signed_cert_pem[];
+
+struct MHD_Daemon *d;
+
+/*
+ * HTTP access handler call back
+ * used to query negotiated security parameters
+ */
+static int
+query_session_ahc (void *cls, struct MHD_Connection *connection,
+                   const char *url, const char *method,
+                   const char *upload_data, const char *version,
+                   size_t *upload_data_size, void **ptr)
+{
+  struct MHD_Response *response;
+  int ret;
+
+  if (NULL == *ptr)
+    {
+      *ptr = &query_session_ahc;
+      return MHD_YES;
+    }
+
+  if (GNUTLS_TLS1_1 !=
+      (ret = MHD_get_connection_info
+       (connection,
+	MHD_CONNECTION_INFO_PROTOCOL)->protocol))
+    {
+      if (GNUTLS_TLS1_2 == ret)
+      {
+        /* as usual, TLS implementations sometimes don't
+           quite do what was asked, just mildly complain... */
+        fprintf (stderr,
+                 "Warning: requested TLS 1.1, got TLS 1.2\n");
+      }
+      else
+      {
+        /* really different version... */
+        fprintf (stderr,
+                 "Error: requested protocol mismatch (wanted %d, got %d)\n",
+                 GNUTLS_TLS1_1,
+                 ret);
+        return -1;
+      }
+    }
+
+  response = MHD_create_response_from_buffer (strlen (EMPTY_PAGE),
+					      (void *) EMPTY_PAGE,
+					      MHD_RESPMEM_PERSISTENT);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+/**
+ * negotiate a secure connection with server & query negotiated security parameters
+ */
+static int
+test_query_session ()
+{
+  CURL *c;
+  struct CBC cbc;
+  CURLcode errornum;
+  char url[256];
+
+  if (NULL == (cbc.buf = malloc (sizeof (char) * 255)))
+    return 16;
+  cbc.size = 255;
+  cbc.pos = 0;
+
+  gen_test_file_url (url, DEAMON_TEST_PORT);
+
+  /* setup test */
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL |
+                        MHD_USE_DEBUG, DEAMON_TEST_PORT,
+                        NULL, NULL, &query_session_ahc, NULL,
+			MHD_OPTION_HTTPS_PRIORITIES, "NORMAL:+ARCFOUR-128",
+                        MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem,
+                        MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem,
+                        MHD_OPTION_END);
+
+  if (d == NULL)
+    return 2;
+
+  const char *aes256_sha = "AES256-SHA";
+  if (curl_uses_nss_ssl() == 0)
+    {
+      aes256_sha = "rsa_aes_256_sha";
+    }
+
+  c = curl_easy_init ();
+#if DEBUG_HTTPS_TEST
+  curl_easy_setopt (c, CURLOPT_VERBOSE, 1);
+#endif
+  curl_easy_setopt (c, CURLOPT_URL, url);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 10L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 10L);
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_FILE, &cbc);
+  /* TLS options */
+  curl_easy_setopt (c, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_1);
+  curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST, aes256_sha);
+  /* currently skip any peer authentication */
+  curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0);
+  curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr, "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+
+      MHD_stop_daemon (d);
+      curl_easy_cleanup (c);
+      free (cbc.buf);
+      return -1;
+    }
+
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  free (cbc.buf);
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+  if (0 != curl_global_init (CURL_GLOBAL_ALL))
+    {
+      fprintf (stderr, "Error (code: %u)\n", errorCount);
+      return -1;
+    }
+  errorCount += test_query_session ();
+  print_test_result (errorCount, argv[0]);
+  curl_global_cleanup ();
+  if (errorCount > 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  return errorCount;
+}
diff --git a/src/testcurl/https/test_https_sni.c b/src/testcurl/https/test_https_sni.c
new file mode 100644
index 0000000..8c4dea6
--- /dev/null
+++ b/src/testcurl/https/test_https_sni.c
@@ -0,0 +1,291 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2013 Christian Grothoff
+
+  libmicrohttpd is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  libmicrohttpd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with libmicrohttpd; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_https_sni.c
+ * @brief  Testcase for libmicrohttpd HTTPS with SNI operations
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "microhttpd.h"
+#include <limits.h>
+#include <sys/stat.h>
+#include <curl/curl.h>
+#include <gcrypt.h>
+#include "tls_test_common.h"
+#include <gnutls/gnutls.h>
+
+/* This test only works with GnuTLS >= 3.0 */
+#if GNUTLS_VERSION_MAJOR >= 3
+
+#include <gnutls/abstract.h>
+
+/**
+ * A hostname, server key and certificate.
+ */
+struct Hosts
+{
+  struct Hosts *next;
+  const char *hostname;
+  gnutls_pcert_st pcrt;
+  gnutls_privkey_t key;
+};
+
+
+/**
+ * Linked list of supported TLDs and respective certificates.
+ */
+static struct Hosts *hosts;
+
+/* Load the certificate and the private key.
+ * (This code is largely taken from GnuTLS).
+ */
+static void
+load_keys(const char *hostname,
+          const char *CERT_FILE,
+          const char *KEY_FILE)
+{
+  int ret;
+  gnutls_datum_t data;
+  struct Hosts *host;
+
+  host = malloc (sizeof (struct Hosts));
+  if (NULL == host)
+    abort ();
+  host->hostname = hostname;
+  host->next = hosts;
+  hosts = host;
+
+  ret = gnutls_load_file (CERT_FILE, &data);
+  if (ret < 0)
+    {
+      fprintf (stderr,
+               "*** Error loading certificate file %s.\n",
+               CERT_FILE);
+      exit (1);
+    }
+  ret =
+    gnutls_pcert_import_x509_raw (&host->pcrt, &data, GNUTLS_X509_FMT_PEM,
+                                  0);
+  if (ret < 0)
+    {
+      fprintf (stderr,
+               "*** Error loading certificate file: %s\n",
+               gnutls_strerror (ret));
+      exit (1);
+    }
+  gnutls_free (data.data);
+
+  ret = gnutls_load_file (KEY_FILE, &data);
+  if (ret < 0)
+    {
+      fprintf (stderr,
+               "*** Error loading key file %s.\n",
+               KEY_FILE);
+      exit (1);
+    }
+
+  gnutls_privkey_init (&host->key);
+  ret =
+    gnutls_privkey_import_x509_raw (host->key,
+                                    &data, GNUTLS_X509_FMT_PEM,
+                                    NULL, 0);
+  if (ret < 0)
+    {
+      fprintf (stderr,
+               "*** Error loading key file: %s\n",
+               gnutls_strerror (ret));
+      exit (1);
+    }
+  gnutls_free (data.data);
+}
+
+
+
+/**
+ * @param session the session we are giving a cert for
+ * @param req_ca_dn NULL on server side
+ * @param nreqs length of req_ca_dn, and thus 0 on server side
+ * @param pk_algos NULL on server side
+ * @param pk_algos_length 0 on server side
+ * @param pcert list of certificates (to be set)
+ * @param pcert_length length of pcert (to be set)
+ * @param pkey the private key (to be set)
+ */
+static int
+sni_callback (gnutls_session_t session,
+              const gnutls_datum_t* req_ca_dn,
+              int nreqs,
+              const gnutls_pk_algorithm_t* pk_algos,
+              int pk_algos_length,
+              gnutls_pcert_st** pcert,
+              unsigned int *pcert_length,
+              gnutls_privkey_t * pkey)
+{
+  char name[256];
+  size_t name_len;
+  struct Hosts *host;
+  unsigned int type;
+
+  name_len = sizeof (name);
+  if (GNUTLS_E_SUCCESS !=
+      gnutls_server_name_get (session,
+                              name,
+                              &name_len,
+                              &type,
+                              0 /* index */))
+    return -1;
+  for (host = hosts; NULL != host; host = host->next)
+    if (0 == strncmp (name, host->hostname, name_len))
+      break;
+  if (NULL == host)
+    {
+      fprintf (stderr,
+               "Need certificate for %.*s\n",
+               (int) name_len,
+               name);
+      return -1;
+    }
+#if 0
+  fprintf (stderr,
+           "Returning certificate for %.*s\n",
+           (int) name_len,
+           name);
+#endif
+  *pkey = host->key;
+  *pcert_length = 1;
+  *pcert = &host->pcrt;
+  return 0;
+}
+
+
+/* perform a HTTP GET request via SSL/TLS */
+static int
+do_get (const char *url)
+{
+  CURL *c;
+  struct CBC cbc;
+  CURLcode errornum;
+  size_t len;
+  struct curl_slist *dns_info;
+
+  len = strlen (test_data);
+  if (NULL == (cbc.buf = malloc (sizeof (char) * len)))
+    {
+      fprintf (stderr, MHD_E_MEM);
+      return -1;
+    }
+  cbc.size = len;
+  cbc.pos = 0;
+
+  c = curl_easy_init ();
+#if DEBUG_HTTPS_TEST
+  curl_easy_setopt (c, CURLOPT_VERBOSE, 1);
+#endif
+  curl_easy_setopt (c, CURLOPT_URL, url);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 10L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 10L);
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_FILE, &cbc);
+
+  /* perform peer authentication */
+  /* TODO merge into send_curl_req */
+  curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0);
+  curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 2);
+  dns_info = curl_slist_append (NULL, "host1:4233:127.0.0.1");
+  dns_info = curl_slist_append (dns_info, "host2:4233:127.0.0.1");
+  curl_easy_setopt (c, CURLOPT_RESOLVE, dns_info);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr, "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      free (cbc.buf);
+      curl_slist_free_all (dns_info);
+      return errornum;
+    }
+
+  curl_easy_cleanup (c);
+  curl_slist_free_all (dns_info);
+  if (memcmp (cbc.buf, test_data, len) != 0)
+    {
+      fprintf (stderr, "Error: local file & received file differ.\n");
+      free (cbc.buf);
+      return -1;
+    }
+
+  free (cbc.buf);
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int error_count = 0;
+  struct MHD_Daemon *d;
+
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+  if (0 != curl_global_init (CURL_GLOBAL_ALL))
+    {
+      fprintf (stderr, "Error: %s\n", strerror (errno));
+      return -1;
+    }
+  load_keys ("host1", ABS_SRCDIR "/host1.crt", ABS_SRCDIR "/host1.key");
+  load_keys ("host2", ABS_SRCDIR "/host2.crt", ABS_SRCDIR "/host2.key");
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL | MHD_USE_DEBUG,
+                        4233,
+                        NULL, NULL,
+                        &http_ahc, NULL,
+                        MHD_OPTION_HTTPS_CERT_CALLBACK, &sni_callback,
+                        MHD_OPTION_END);
+  if (d == NULL)
+    {
+      fprintf (stderr, MHD_E_SERVER_INIT);
+      return -1;
+    }
+  error_count += do_get ("https://host1:4233/");
+  error_count += do_get ("https://host2:4233/");
+
+  MHD_stop_daemon (d);
+  curl_global_cleanup ();
+  return error_count != 0;
+}
+
+
+#else
+
+int main ()
+{
+  fprintf (stderr,
+           "SNI not supported by GnuTLS < 3.0\n");
+  return 0;
+}
+#endif
diff --git a/src/testcurl/https/test_https_time_out.c b/src/testcurl/https/test_https_time_out.c
new file mode 100644
index 0000000..124a280
--- /dev/null
+++ b/src/testcurl/https/test_https_time_out.c
@@ -0,0 +1,146 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007 Christian Grothoff
+
+ libmicrohttpd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ libmicrohttpd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libmicrohttpd; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file mhds_get_test.c
+ * @brief: daemon TLS alert response test-case
+ *
+ * @author Sagie Amir
+ */
+
+#include "platform.h"
+#include "microhttpd.h"
+#include "internal.h"
+#include "tls_test_common.h"
+#include <gcrypt.h>
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif
+
+extern const char srv_key_pem[];
+extern const char srv_self_signed_cert_pem[];
+
+static const int TIME_OUT = 3;
+
+static int
+test_tls_session_time_out (gnutls_session_t session)
+{
+  int ret;
+  MHD_socket sd;
+  struct sockaddr_in sa;
+
+  sd = socket (AF_INET, SOCK_STREAM, 0);
+  if (sd == -1)
+    {
+      fprintf (stderr, "Failed to create socket: %s\n", strerror (errno));
+      return -1;
+    }
+
+  memset (&sa, '\0', sizeof (struct sockaddr_in));
+  sa.sin_family = AF_INET;
+  sa.sin_port = htons (DEAMON_TEST_PORT);
+  sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+  gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) (intptr_t) sd);
+
+  ret = connect (sd, &sa, sizeof (struct sockaddr_in));
+
+  if (ret < 0)
+    {
+      fprintf (stderr, "Error: %s\n", MHD_E_FAILED_TO_CONNECT);
+      close (sd);
+      return -1;
+    }
+
+  ret = gnutls_handshake (session);
+  if (ret < 0)
+    {
+      fprintf (stderr, "Handshake failed\n");
+      close (sd);
+      return -1;
+    }
+
+  sleep (TIME_OUT + 1);
+
+  /* check that server has closed the connection */
+  /* TODO better RST trigger */
+  if (send (sd, "", 1, 0) == 0)
+    {
+      fprintf (stderr, "Connection failed to time-out\n");
+      close (sd);
+      return -1;
+    }
+
+  close (sd);
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  int errorCount = 0;;
+  struct MHD_Daemon *d;
+  gnutls_session_t session;
+  gnutls_datum_t key;
+  gnutls_datum_t cert;
+  gnutls_certificate_credentials_t xcred;
+
+
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+  gnutls_global_init ();
+  gnutls_global_set_log_level (11);
+
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL |
+                        MHD_USE_DEBUG, DEAMON_TEST_PORT,
+                        NULL, NULL, &http_dummy_ahc, NULL,
+                        MHD_OPTION_CONNECTION_TIMEOUT, TIME_OUT,
+                        MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem,
+                        MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem,
+                        MHD_OPTION_END);
+
+  if (d == NULL)
+    {
+      fprintf (stderr, MHD_E_SERVER_INIT);
+      return -1;
+    }
+
+  if (0 != setup_session (&session, &key, &cert, &xcred))
+    {
+      fprintf (stderr, "failed to setup session\n");
+      return 1;
+    }
+  errorCount += test_tls_session_time_out (session);
+  teardown_session (session, &key, &cert, xcred);
+
+  print_test_result (errorCount, argv[0]);
+
+  MHD_stop_daemon (d);
+  gnutls_global_deinit ();
+
+  return errorCount != 0;
+}
diff --git a/src/testcurl/https/test_tls_authentication.c b/src/testcurl/https/test_tls_authentication.c
new file mode 100644
index 0000000..cc9c76d
--- /dev/null
+++ b/src/testcurl/https/test_tls_authentication.c
@@ -0,0 +1,110 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007 Christian Grothoff
+
+ libmicrohttpd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ libmicrohttpd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libmicrohttpd; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file tls_authentication_test.c
+ * @brief  Testcase for libmicrohttpd HTTPS GET operations
+ * @author Sagie Amir
+ */
+
+#include "platform.h"
+#include "microhttpd.h"
+#include <curl/curl.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <gcrypt.h>
+#include "tls_test_common.h"
+
+extern int curl_check_version (const char *req_version, ...);
+extern const char test_file_data[];
+
+extern const char ca_key_pem[];
+extern const char ca_cert_pem[];
+extern const char srv_signed_cert_pem[];
+extern const char srv_signed_key_pem[];
+
+
+
+/* perform a HTTP GET request via SSL/TLS */
+static int
+test_secure_get (void * cls, char *cipher_suite, int proto_version)
+{
+  int ret;
+  struct MHD_Daemon *d;
+
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL |
+                        MHD_USE_DEBUG, DEAMON_TEST_PORT,
+                        NULL, NULL, &http_ahc, NULL,
+                        MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
+                        MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
+                        MHD_OPTION_END);
+
+  if (d == NULL)
+    {
+      fprintf (stderr, MHD_E_SERVER_INIT);
+      return -1;
+    }
+
+  ret = test_daemon_get (NULL, cipher_suite, proto_version, DEAMON_TEST_PORT, 0);
+
+  MHD_stop_daemon (d);
+  return ret;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+  if (setup_ca_cert () == NULL)
+    {
+      fprintf (stderr, MHD_E_TEST_FILE_CREAT);
+      return -1;
+    }
+
+  if (0 != curl_global_init (CURL_GLOBAL_ALL))
+    {
+      fprintf (stderr, "Error (code: %u)\n", errorCount);
+      return -1;
+    }
+
+  char *aes256_sha = "AES256-SHA";
+  if (curl_uses_nss_ssl() == 0)
+    {
+      aes256_sha = "rsa_aes_256_sha";
+    }
+
+  errorCount +=
+    test_secure_get (NULL, aes256_sha, CURL_SSLVERSION_TLSv1);
+
+  print_test_result (errorCount, argv[0]);
+
+  curl_global_cleanup ();
+  if (0 != remove (ca_cert_file_name))
+    fprintf (stderr,
+	     "Failed to remove `%s'\n",
+	     ca_cert_file_name);
+  return errorCount != 0;
+}
diff --git a/src/testcurl/https/test_tls_options.c b/src/testcurl/https/test_tls_options.c
new file mode 100644
index 0000000..7dd01a7
--- /dev/null
+++ b/src/testcurl/https/test_tls_options.c
@@ -0,0 +1,155 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2007, 2010 Christian Grothoff
+  
+  libmicrohttpd is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 2, or (at your
+  option) any later version.
+  
+  libmicrohttpd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+  
+  You should have received a copy of the GNU General Public License
+  along with libmicrohttpd; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file tls_daemon_options_test.c
+ * @brief  Testcase for libmicrohttpd HTTPS GET operations
+ * @author Sagie Amir
+ */
+
+#include "platform.h"
+#include "microhttpd.h"
+#include <sys/stat.h>
+#include <limits.h>
+#include <gcrypt.h>
+#include "tls_test_common.h"
+
+extern const char srv_key_pem[];
+extern const char srv_self_signed_cert_pem[];
+
+int curl_check_version (const char *req_version, ...);
+
+/**
+ * test server refuses to negotiate connections with unsupported protocol versions
+ *
+ */
+static int
+test_unmatching_ssl_version (void * cls, const char *cipher_suite,
+                             int curl_req_ssl_version)
+{
+  struct CBC cbc;
+  if (NULL == (cbc.buf = malloc (sizeof (char) * 256)))
+    {
+      fprintf (stderr, "Error: failed to allocate: %s\n",
+               strerror (errno));
+      return -1;
+    }
+  cbc.size = 256;
+  cbc.pos = 0;
+
+  char url[255];
+  if (gen_test_file_url (url, DEAMON_TEST_PORT))
+    {
+      free (cbc.buf);
+      fprintf (stderr, "Internal error in gen_test_file_url\n");
+      return -1;
+    }
+
+  /* assert daemon *rejected* request */
+  if (CURLE_OK ==
+      send_curl_req (url, &cbc, cipher_suite, curl_req_ssl_version))
+    {
+      free (cbc.buf);
+      fprintf (stderr, "cURL failed to reject request despite SSL version missmatch!\n");
+      return -1;
+    }
+
+  free (cbc.buf);
+  return 0;
+}
+
+
+/* setup a temporary transfer test file */
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+  const char *ssl_version;
+  int daemon_flags =
+    MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL | MHD_USE_DEBUG;
+
+  gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+ if (curl_check_version (MHD_REQ_CURL_VERSION))
+    {
+      return 0;
+    }
+  ssl_version = curl_version_info (CURLVERSION_NOW)->ssl_version;
+  if (NULL == ssl_version)
+  {
+    fprintf (stderr, "Curl does not support SSL.  Cannot run the test.\n");
+    return 0;
+  }
+  if (0 != strncmp (ssl_version, "GnuTLS", 6))
+  {
+    fprintf (stderr, "This test can be run only with libcurl-gnutls.\n");
+    return 0;
+  }
+
+  if (0 != curl_global_init (CURL_GLOBAL_ALL))
+    {
+      fprintf (stderr, "Error: %s\n", strerror (errno));
+      return 0; 
+    }
+
+  const char *aes128_sha = "AES128-SHA";
+  const char *aes256_sha = "AES256-SHA";
+  if (curl_uses_nss_ssl() == 0)
+    {
+      aes128_sha = "rsa_aes_128_sha";
+      aes256_sha = "rsa_aes_256_sha";
+    }
+  
+
+  if (0 != 
+    test_wrap ("TLS1.0-AES-SHA1",
+	       &test_https_transfer, NULL, daemon_flags,
+	       aes128_sha,
+	       CURL_SSLVERSION_TLSv1,
+	       MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem,
+	       MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem,
+	       MHD_OPTION_HTTPS_PRIORITIES, "NONE:+VERS-TLS1.0:+AES-128-CBC:+SHA1:+RSA:+COMP-NULL",
+	       MHD_OPTION_END))
+    {
+      fprintf (stderr, "TLS1.0-AES-SHA1 test failed\n");
+      errorCount++;
+    }
+  fprintf (stderr,
+	   "The following handshake should fail (and print an error message)...\n");
+  if (0 !=
+    test_wrap ("TLS1.0 vs SSL3",
+	       &test_unmatching_ssl_version, NULL, daemon_flags,
+	       aes256_sha,
+	       CURL_SSLVERSION_SSLv3,
+	       MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem,
+	       MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem,
+	       MHD_OPTION_HTTPS_PRIORITIES, "NONE:+VERS-TLS1.0:+AES-256-CBC:+SHA1:+RSA:+COMP-NULL",
+	       MHD_OPTION_END))
+    {
+      fprintf (stderr, "TLS1.0 vs SSL3 test failed\n");
+      errorCount++;
+    }
+  curl_global_cleanup ();
+
+  return errorCount != 0;
+}
diff --git a/src/testcurl/https/tls_test_common.c b/src/testcurl/https/tls_test_common.c
new file mode 100644
index 0000000..f917582
--- /dev/null
+++ b/src/testcurl/https/tls_test_common.c
@@ -0,0 +1,484 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007 Christian Grothoff
+
+ libmicrohttpd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ libmicrohttpd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libmicrohttpd; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file tls_test_common.c
+ * @brief  Common tls test functions
+ * @author Sagie Amir
+ */
+#include "tls_test_common.h"
+#include "tls_test_keys.h"
+
+
+int curl_check_version (const char *req_version, ...);
+
+FILE *
+setup_ca_cert ()
+{
+  FILE *cert_fd;
+
+  if (NULL == (cert_fd = fopen (ca_cert_file_name, "wb+")))
+    {
+      fprintf (stderr, "Error: failed to open `%s': %s\n",
+               ca_cert_file_name, strerror (errno));
+      return NULL;
+    }
+  if (fwrite (ca_cert_pem, sizeof (char), strlen (ca_cert_pem) + 1, cert_fd)
+      != strlen (ca_cert_pem) + 1)
+    {
+      fprintf (stderr, "Error: failed to write `%s. %s'\n",
+               ca_cert_file_name, strerror (errno));
+      fclose (cert_fd);
+      return NULL;
+    }
+  if (fflush (cert_fd))
+    {
+      fprintf (stderr, "Error: failed to flush ca cert file stream. %s\n",
+               strerror (errno));
+      fclose (cert_fd);
+      return NULL;
+    }
+  return cert_fd;
+}
+
+
+/*
+ * test HTTPS transfer
+ */
+int
+test_daemon_get (void *cls,
+		 const char *cipher_suite, int proto_version,
+		 int port,
+		 int ver_peer)
+{
+  CURL *c;
+  struct CBC cbc;
+  CURLcode errornum;
+  char url[255];
+  size_t len;
+
+  len = strlen (test_data);
+  if (NULL == (cbc.buf = malloc (sizeof (char) * len)))
+    {
+      fprintf (stderr, MHD_E_MEM);
+      return -1;
+    }
+  cbc.size = len;
+  cbc.pos = 0;
+
+  /* construct url - this might use doc_path */
+  gen_test_file_url (url, port);
+
+  c = curl_easy_init ();
+#if DEBUG_HTTPS_TEST
+  curl_easy_setopt (c, CURLOPT_VERBOSE, 1);
+#endif
+  curl_easy_setopt (c, CURLOPT_URL, url);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 10L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 10L);
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_FILE, &cbc);
+
+  /* TLS options */
+  curl_easy_setopt (c, CURLOPT_SSLVERSION, proto_version);
+  curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST, cipher_suite);
+
+  /* perform peer authentication */
+  /* TODO merge into send_curl_req */
+  curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, ver_peer);
+  curl_easy_setopt (c, CURLOPT_CAINFO, ca_cert_file_name);
+  curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr, "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      free (cbc.buf);
+      return errornum;
+    }
+
+  curl_easy_cleanup (c);
+
+  if (memcmp (cbc.buf, test_data, len) != 0)
+    {
+      fprintf (stderr, "Error: local file & received file differ.\n");
+      free (cbc.buf);
+      return -1;
+    }
+
+  free (cbc.buf);
+  return 0;
+}
+
+
+void
+print_test_result (int test_outcome, char *test_name)
+{
+#if 0
+  if (test_outcome != 0)
+    fprintf (stderr, "running test: %s [fail]\n", test_name);
+  else
+    fprintf (stdout, "running test: %s [pass]\n", test_name);
+#endif
+}
+
+size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+/**
+ *  HTTP access handler call back
+ */
+int
+http_ahc (void *cls, struct MHD_Connection *connection,
+          const char *url, const char *method, const char *upload_data,
+          const char *version, size_t *upload_data_size, void **ptr)
+{
+  static int aptr;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+    {
+      /* do never respond on first call */
+      *ptr = &aptr;
+      return MHD_YES;
+    }
+  *ptr = NULL;                  /* reset when done */
+  response = MHD_create_response_from_buffer (strlen (test_data),
+					      (void *) test_data,
+					      MHD_RESPMEM_PERSISTENT);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+/* HTTP access handler call back */
+int
+http_dummy_ahc (void *cls, struct MHD_Connection *connection,
+                const char *url, const char *method, const char *upload_data,
+                const char *version, size_t *upload_data_size,
+                void **ptr)
+{
+  return 0;
+}
+
+/**
+ * send a test http request to the daemon
+ * @param url
+ * @param cbc - may be null
+ * @param cipher_suite
+ * @param proto_version
+ * @return
+ */
+/* TODO have test wrap consider a NULL cbc */
+int
+send_curl_req (char *url, struct CBC * cbc, const char *cipher_suite,
+               int proto_version)
+{
+  CURL *c;
+  CURLcode errornum;
+  c = curl_easy_init ();
+#if DEBUG_HTTPS_TEST
+  curl_easy_setopt (c, CURLOPT_VERBOSE, CURL_VERBOS_LEVEL);
+#endif
+  curl_easy_setopt (c, CURLOPT_URL, url);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 60L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 60L);
+
+  if (cbc != NULL)
+    {
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_FILE, cbc);
+    }
+
+  /* TLS options */
+  curl_easy_setopt (c, CURLOPT_SSLVERSION, proto_version);
+  curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST, cipher_suite);
+
+  /* currently skip any peer authentication */
+  curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0);
+  curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0);
+
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr, "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      return errornum;
+    }
+  curl_easy_cleanup (c);
+
+  return CURLE_OK;
+}
+
+
+/**
+ * compile test file url pointing to the current running directory path
+ *
+ * @param url - char buffer into which the url is compiled
+ * @param port port to use for the test
+ * @return -1 on error
+ */
+int
+gen_test_file_url (char *url, int port)
+{
+  int ret = 0;
+  char *doc_path;
+  size_t doc_path_len;
+  /* setup test file path, url */
+  doc_path_len = PATH_MAX > 4096 ? 4096 : PATH_MAX;
+  if (NULL == (doc_path = malloc (doc_path_len)))
+    {
+      fprintf (stderr, MHD_E_MEM);
+      return -1;
+    }
+  if (getcwd (doc_path, doc_path_len) == NULL)
+    {
+      fprintf (stderr, "Error: failed to get working directory. %s\n",
+               strerror (errno));
+      ret = -1;
+    }
+#ifdef WINDOWS
+  {
+    int i;
+    for (i = 0; i < doc_path_len; i++)
+    {
+      if (doc_path[i] == 0)
+        break;
+      if (doc_path[i] == '\\')
+      {
+        doc_path[i] = '/';
+      }
+      if (doc_path[i] != ':')
+        continue;
+      if (i == 0)
+        break;
+      doc_path[i] = doc_path[i - 1];
+      doc_path[i - 1] = '/';
+    }
+  }
+#endif
+  /* construct url - this might use doc_path */
+  if (sprintf (url, "%s:%d%s/%s", "https://127.0.0.1", port,
+               doc_path, "urlpath") < 0)
+    ret = -1;
+
+  free (doc_path);
+  return ret;
+}
+
+/**
+ * test HTTPS file transfer
+ */
+int
+test_https_transfer (void *cls, const char *cipher_suite, int proto_version)
+{
+  int len;
+  int ret = 0;
+  struct CBC cbc;
+  char url[255];
+
+  len = strlen (test_data);
+  if (NULL == (cbc.buf = malloc (sizeof (char) * len)))
+    {
+      fprintf (stderr, MHD_E_MEM);
+      return -1;
+    }
+  cbc.size = len;
+  cbc.pos = 0;
+
+  if (gen_test_file_url (url, DEAMON_TEST_PORT))
+    {
+      ret = -1;
+      goto cleanup;
+    }
+
+  if (CURLE_OK != send_curl_req (url, &cbc, cipher_suite, proto_version))
+    {
+      ret = -1;
+      goto cleanup;
+    }
+
+  /* compare test file & daemon responce */
+  if ( (len != strlen (test_data)) ||
+       (memcmp (cbc.buf, 
+		test_data, 
+		len) != 0) )
+    {
+      fprintf (stderr, "Error: local file & received file differ.\n");
+      ret = -1;
+    }
+cleanup:
+  free (cbc.buf);
+  return ret;
+}
+
+/**
+ * setup test case
+ *
+ * @param d
+ * @param daemon_flags
+ * @param arg_list
+ * @return
+ */
+int
+setup_testcase (struct MHD_Daemon **d, int daemon_flags, va_list arg_list)
+{
+  *d = MHD_start_daemon_va (daemon_flags, DEAMON_TEST_PORT,
+                            NULL, NULL, &http_ahc, NULL, arg_list);
+
+  if (*d == NULL)
+    {
+      fprintf (stderr, MHD_E_SERVER_INIT);
+      return -1;
+    }
+
+  return 0;
+}
+
+void
+teardown_testcase (struct MHD_Daemon *d)
+{
+  MHD_stop_daemon (d);
+}
+
+int
+setup_session (gnutls_session_t * session,
+               gnutls_datum_t * key,
+               gnutls_datum_t * cert, 
+	       gnutls_certificate_credentials_t * xcred)
+{
+  int ret;
+  const char *err_pos;
+
+  gnutls_certificate_allocate_credentials (xcred);
+  key->size = strlen (srv_key_pem) + 1;
+  key->data = malloc (key->size);
+  if (NULL == key->data) 
+     {
+       gnutls_certificate_free_credentials (*xcred);
+	return -1;
+     }
+  memcpy (key->data, srv_key_pem, key->size);
+  cert->size = strlen (srv_self_signed_cert_pem) + 1;
+  cert->data = malloc (cert->size);
+  if (NULL == cert->data)
+    {
+        gnutls_certificate_free_credentials (*xcred);
+	free (key->data); 
+	return -1;
+    }
+  memcpy (cert->data, srv_self_signed_cert_pem, cert->size);
+  gnutls_certificate_set_x509_key_mem (*xcred, cert, key,
+				       GNUTLS_X509_FMT_PEM);
+  gnutls_init (session, GNUTLS_CLIENT);
+  ret = gnutls_priority_set_direct (*session,
+				    "NORMAL", &err_pos);
+  if (ret < 0)
+    {
+       gnutls_deinit (*session);
+       gnutls_certificate_free_credentials (*xcred);
+       free (key->data);
+       return -1;
+    }
+  gnutls_credentials_set (*session, 
+			  GNUTLS_CRD_CERTIFICATE, 
+			  *xcred);
+  return 0;
+}
+
+int
+teardown_session (gnutls_session_t session,
+                  gnutls_datum_t * key,
+                  gnutls_datum_t * cert,
+                  gnutls_certificate_credentials_t xcred)
+{
+  free (key->data);
+  key->data = NULL;
+  key->size = 0;
+  free (cert->data);
+  cert->data = NULL;
+  cert->size = 0;
+  gnutls_deinit (session);
+  gnutls_certificate_free_credentials (xcred);
+  return 0;
+}
+
+/* TODO test_wrap: change sig to (setup_func, test, va_list test_arg) */
+int
+test_wrap (const char *test_name, int
+           (*test_function) (void * cls, const char *cipher_suite,
+                             int proto_version), void * cls,
+           int daemon_flags, const char *cipher_suite, int proto_version, ...)
+{
+  int ret;
+  va_list arg_list;
+  struct MHD_Daemon *d;
+
+  va_start (arg_list, proto_version);
+  if (setup_testcase (&d, daemon_flags, arg_list) != 0)
+    {
+      va_end (arg_list);
+      fprintf (stderr, "Failed to setup testcase %s\n", test_name);
+      return -1;
+    }
+#if 0
+  fprintf (stdout, "running test: %s ", test_name);
+#endif
+  ret = test_function (NULL, cipher_suite, proto_version);
+#if 0
+  if (ret == 0)
+    {
+      fprintf (stdout, "[pass]\n");
+    }
+  else
+    {
+      fprintf (stdout, "[fail]\n");
+    }
+#endif
+  teardown_testcase (d);
+  va_end (arg_list);
+  return ret;
+}
diff --git a/src/testcurl/https/tls_test_common.h b/src/testcurl/https/tls_test_common.h
new file mode 100644
index 0000000..20198f7
--- /dev/null
+++ b/src/testcurl/https/tls_test_common.h
@@ -0,0 +1,141 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007 Christian Grothoff
+
+ libmicrohttpd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ libmicrohttpd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libmicrohttpd; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+#ifndef TLS_TEST_COMMON_H_
+#define TLS_TEST_COMMON_H_
+
+#include "platform.h"
+#include "microhttpd.h"
+#include <curl/curl.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <gnutls/gnutls.h>
+
+/* this enables verbos CURL version checking */
+#define DEBUG_HTTPS_TEST 0
+#define CURL_VERBOS_LEVEL 0
+
+#define DEAMON_TEST_PORT 4233
+
+#define test_data "Hello World\n"
+#define ca_cert_file_name "tmp_ca_cert.pem"
+
+#define EMPTY_PAGE "<html><head><title>Empty page</title></head><body>Empty page</body></html>"
+#define PAGE_NOT_FOUND "<html><head><title>File not found</title></head><body>File not found</body></html>"
+
+#define MHD_E_MEM "Error: memory error\n"
+#define MHD_E_SERVER_INIT "Error: failed to start server\n"
+#define MHD_E_TEST_FILE_CREAT "Error: failed to setup test file\n"
+#define MHD_E_CERT_FILE_CREAT "Error: failed to setup test certificate\n"
+#define MHD_E_KEY_FILE_CREAT "Error: failed to setup test certificate\n"
+#define MHD_E_FAILED_TO_CONNECT "Error: server connection could not be established\n"
+
+/* TODO rm if unused */
+struct https_test_data
+{
+  void *cls;
+  const char *cipher_suite;
+  int proto_version;
+};
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+struct CipherDef
+{
+  int options[2];
+  char *curlname;
+};
+
+
+int curl_check_version (const char *req_version, ...);
+int curl_uses_nss_ssl ();
+
+
+FILE *
+setup_ca_cert ();
+
+/**
+ * perform cURL request for file
+ */
+int
+test_daemon_get (void * cls,
+		 const char *cipher_suite, int proto_version,
+                 int port, int ver_peer);
+
+void print_test_result (int test_outcome, char *test_name);
+
+size_t copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx);
+
+int
+http_ahc (void *cls, struct MHD_Connection *connection,
+          const char *url, const char *method, const char *upload_data,
+          const char *version, size_t *upload_data_size, void **ptr);
+
+int
+http_dummy_ahc (void *cls, struct MHD_Connection *connection,
+                const char *url, const char *method, const char *upload_data,
+                const char *version, size_t *upload_data_size,
+                void **ptr);
+
+
+/**
+ * compile test file url pointing to the current running directory path
+ *
+ * @param url - char buffer into which the url is compiled
+ * @param port port to use for the test
+ * @return -1 on error
+ */
+int gen_test_file_url (char *url, int port);
+
+int
+send_curl_req (char *url, struct CBC *cbc, const char *cipher_suite,
+               int proto_version);
+
+int
+test_https_transfer (void *cls, const char *cipher_suite, int proto_version);
+
+int
+setup_testcase (struct MHD_Daemon **d, int daemon_flags, va_list arg_list);
+
+void teardown_testcase (struct MHD_Daemon *d);
+
+int
+setup_session (gnutls_session_t * session,
+               gnutls_datum_t * key,
+               gnutls_datum_t * cert,
+               gnutls_certificate_credentials_t * xcred);
+
+int
+teardown_session (gnutls_session_t session,
+                  gnutls_datum_t * key,
+                  gnutls_datum_t * cert,
+                  gnutls_certificate_credentials_t xcred);
+
+int
+test_wrap (const char *test_name, int
+           (*test_function) (void * cls, const char *cipher_suite,
+                             int proto_version), void *test_function_cls,
+           int daemon_flags, const char *cipher_suite, int proto_version, ...);
+#endif /* TLS_TEST_COMMON_H_ */
diff --git a/src/testcurl/https/tls_test_keys.h b/src/testcurl/https/tls_test_keys.h
new file mode 100644
index 0000000..b6e37ee
--- /dev/null
+++ b/src/testcurl/https/tls_test_keys.h
@@ -0,0 +1,173 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2006, 2007, 2008 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef MHD_TLS_TEST_KEYS_H
+#define MHD_TLS_TEST_KEYS_H
+
+/* Test Certificates */
+
+/* Certificate Authority key */
+const char ca_key_pem[] = "-----BEGIN RSA PRIVATE KEY-----\n"
+  "MIIEowIBAAKCAQEA0EdlP613rjFvEj93tGo9fzBoKWU3CW+AbbfcJ397C89MyZ9J\n"
+  "rlxyLGfa6qVX7CFVNmzgWWfcl2tHlw/fZmWtf/SFgrlkldvuGyY8H3n2HuMsWz/E\n"
+  "h7n5VgwBX8NsP4eZNmikepxpr1mYx25K8FjnsKjAR9jGUSV8UfZ7VLIY0x/yqe+3\n"
+  "32oqc4D/wJbV1AwwvC5Xf9rvHJwcZg57eqbDCL/4GDDk7d9Gark4XK6ZG+FnnxQn\n"
+  "4a4jIdf4FoPp9s0EieHrHwYzs/uBqmfCSF4wXiaO8bmEwtbAsVbZH74Le7ggUbEe\n"
+  "o+jan9XK0dE88AIImGzgoBnlic/Rr7J8OWA+iwIDAQABAoIBAEICZqXkUdpsw2F6\n"
+  "qPMOergNPO3lrKg6ZO8hBs6j2fj3tcPuzljK5sqJDboxNejZ9Zo+rmnXf3Oj5fgL\n"
+  "6UcYMYEsm4W/QRA3uEJ1fzeQnT7Ty9KNprlHaSzquCLEGlIWJSo3xu0vFlWjJUcL\n"
+  "fwemfaOhD/OVUeEU6s5FOngwy6pZUsOajs3fNRtwBGuuXjniKZZlpSf2Wqu3xpHZ\n"
+  "31OF1V0ycUCGPPFtpmUCtnZhS9L8QBTkNtfTIdXv6SfoBRFm0oXb0uL5HGft6yc7\n"
+  "eYRXIscllQciqG3ymJ/y9o0E3A0YsBVauQyi7OEk+Kg8uoYOBkZCIY69hoN2Znlk\n"
+  "OY5S5Z0CgYEA3j8pRAJzvc827KcX4vJf05HYD4aCyaI80fNmx1DgXfglTSGLQ361\n"
+  "6i05YW8WtIvgkma3wF+jJOckBCW/7iq8wAX7Kz75WKGRyyTEb0wSfjx0G8grxX4d\n"
+  "7sTIAAOnQj5WT6E/bkqxQZAYnVtIPxKtSlwts0H/bjPVYwSFchHK7t8CgYEA7+ks\n"
+  "C0EMjF8CDeCfvbOUGiiqAvU3G20LEC3WlJM3AU+J9Jzp6AMkgaIA8J5oNdsbFBn4\n"
+  "N12JPOO+7WRUk6Av8bsh4faE36ThnHohgAL8guRU7jIXvsFyO5yiY7/o/0lES0/V\n"
+  "6xkh/Epj4MReuCGkiD9ifCVAo+dhHskeE9qbYdUCgYA4yBpa7eV0UUTPIcHQkew5\n"
+  "ucFh9hPkQDcZzP4tXlR0rbmaAz/5dp4zvmoyopdCeZpezS+VTtn3y7Y/+QUYbILc\n"
+  "7KpHWkeKhX0iUbp+VQlEh12C25mTU62CG3SdzFEnc5XJsoDqRNsUzSP80B2dP8BW\n"
+  "h0aFzg7csRGLwtP1WOZoMQKBgQCrgsKd+Q8Dexh421DXyX3jhZalLrEKxlXWZy60\n"
+  "YNo98aLqYRNHbpe2pR6O5nARsGYXZMlyq0flY9um0sc0Epyz79g1NoufZrxzpUw1\n"
+  "u+zRlnKxJtaa5KjJvRzKuvPTLYnJXXXM8Na/Cl+E3F3qvQJm9QlvPyKLCmsAGz+J\n"
+  "agsTUQKBgC0wqqJ6b1tbrAD8AVeeAn/IiP1rxYpc3x2s6ikFO2FMHXHC9wgrRPOc\n"
+  "mkokV+DrUOv3I/7jG8wQA/FmBUPy562a1bObIKzg6CPXzrN68AmNnOIVU+H8fdxI\n"
+  "iGyfT8WNpcRmtN11v34qXHwOWGQhpyyk2yNa8VIBSpkShq/EseZ1\n"
+  "-----END RSA PRIVATE KEY-----\n";
+
+/* Certificate Authority cert */
+const char ca_cert_pem[] = "-----BEGIN CERTIFICATE-----\n"
+  "MIIC6DCCAdKgAwIBAgIES0KCvTALBgkqhkiG9w0BAQUwFzEVMBMGA1UEAxMMdGVz\n"
+  "dF9jYV9jZXJ0MB4XDTEwMDEwNTAwMDcyNVoXDTQ1MDMxMjAwMDcyNVowFzEVMBMG\n"
+  "A1UEAxMMdGVzdF9jYV9jZXJ0MIIBHzALBgkqhkiG9w0BAQEDggEOADCCAQkCggEA\n"
+  "0EdlP613rjFvEj93tGo9fzBoKWU3CW+AbbfcJ397C89MyZ9JrlxyLGfa6qVX7CFV\n"
+  "NmzgWWfcl2tHlw/fZmWtf/SFgrlkldvuGyY8H3n2HuMsWz/Eh7n5VgwBX8NsP4eZ\n"
+  "Nmikepxpr1mYx25K8FjnsKjAR9jGUSV8UfZ7VLIY0x/yqe+332oqc4D/wJbV1Aww\n"
+  "vC5Xf9rvHJwcZg57eqbDCL/4GDDk7d9Gark4XK6ZG+FnnxQn4a4jIdf4FoPp9s0E\n"
+  "ieHrHwYzs/uBqmfCSF4wXiaO8bmEwtbAsVbZH74Le7ggUbEeo+jan9XK0dE88AII\n"
+  "mGzgoBnlic/Rr7J8OWA+iwIDAQABo0MwQTAPBgNVHRMBAf8EBTADAQH/MA8GA1Ud\n"
+  "DwEB/wQFAwMHBAAwHQYDVR0OBBYEFP2olB4s2T/xuoQ5pT2RKojFwZo2MAsGCSqG\n"
+  "SIb3DQEBBQOCAQEAebD5m+vZkVXa8y+QZ5GtsiR9gpH+LKtdWBjk1kmfSgvQI/xA\n"
+  "aDCV/9BhdNGIBOTYGkln8urWd7g2Mj3TwKEAfNTUFpAsrBAlSSLTGYCSt72S2NsS\n"
+  "L/qUxmj1W6X95UHXCo49mSZx3LlaY3mz1L87gq/kK0XpzA3g2uF25jt84RvshsXy\n"
+  "clOc+eRrVETqFZqer96WB7kzFTv+qmROQKmW8X4a2A5r5Jl4vRwOz5/rEeB9Qs0K\n"
+  "rmK8+5HgvWd80WB8BtfFtZfoY/hHVM8nLD3ELVJrOKiTeIACunQFyT5lV0QkdmSA\n"
+  "CGInU7jzs8nu+s2avf6j+eVZUbVJ+dFMApTJgg==\n"
+  "-----END CERTIFICATE-----\n";
+
+/* test server CA signed certificates */
+const char srv_signed_cert_pem[] = "-----BEGIN CERTIFICATE-----\n"
+  "MIIDGzCCAgWgAwIBAgIES0KCvTALBgkqhkiG9w0BAQUwFzEVMBMGA1UEAxMMdGVz\n"
+  "dF9jYV9jZXJ0MB4XDTEwMDEwNTAwMDcyNVoXDTQ1MDMxMjAwMDcyNVowFzEVMBMG\n"
+  "A1UEAxMMdGVzdF9jYV9jZXJ0MIIBHzALBgkqhkiG9w0BAQEDggEOADCCAQkCggEA\n"
+  "vfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW+K03KwEku55QvnUn\n"
+  "dwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8ILq4sw32vo0fbMu5BZ\n"
+  "F49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ020Q5EAAEseD1YtWC\n"
+  "IpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6QYGGh1QmHRPAy3CB\n"
+  "II6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6xyoOl204xuekZOaG9\n"
+  "RUPId74Rtmwfi1TLbBzo2wIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQM\n"
+  "MAoGCCsGAQUFBwMBMA8GA1UdDwEB/wQFAwMHIAAwHQYDVR0OBBYEFOFi4ilKOP1d\n"
+  "XHlWCMwmVKr7mgy8MB8GA1UdIwQYMBaAFP2olB4s2T/xuoQ5pT2RKojFwZo2MAsG\n"
+  "CSqGSIb3DQEBBQOCAQEAHVWPxazupbOkG7Did+dY9z2z6RjTzYvurTtEKQgzM2Vz\n"
+  "GQBA+3pZ3c5mS97fPIs9hZXfnQeelMeZ2XP1a+9vp35bJjZBBhVH+pqxjCgiUflg\n"
+  "A3Zqy0XwwVCgQLE2HyaU3DLUD/aeIFK5gJaOSdNTXZLv43K8kl4cqDbMeRpVTbkt\n"
+  "YmG4AyEOYRNKGTqMEJXJoxD5E3rBUNrVI/XyTjYrulxbNPcMWEHKNeeqWpKDYTFo\n"
+  "Bb01PCthGXiq/4A2RLAFosadzRa8SBpoSjPPfZ0b2w4MJpReHqKbR5+T2t6hzml6\n"
+  "4ToyOKPDmamiTuN5KzLN3cw7DQlvWMvqSOChPLnA3Q==\n"
+  "-----END CERTIFICATE-----\n";
+
+/* test server key */
+const char srv_signed_key_pem[] = "-----BEGIN RSA PRIVATE KEY-----\n"
+  "MIIEowIBAAKCAQEAvfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW\n"
+  "+K03KwEku55QvnUndwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8IL\n"
+  "q4sw32vo0fbMu5BZF49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ0\n"
+  "20Q5EAAEseD1YtWCIpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6\n"
+  "QYGGh1QmHRPAy3CBII6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6x\n"
+  "yoOl204xuekZOaG9RUPId74Rtmwfi1TLbBzo2wIDAQABAoIBADu09WSICNq5cMe4\n"
+  "+NKCLlgAT1NiQpLls1gKRbDhKiHU9j8QWNvWWkJWrCya4QdUfLCfeddCMeiQmv3K\n"
+  "lJMvDs+5OjJSHFoOsGiuW2Ias7IjnIojaJalfBml6frhJ84G27IXmdz6gzOiTIer\n"
+  "DjeAgcwBaKH5WwIay2TxIaScl7AwHBauQkrLcyb4hTmZuQh6ArVIN6+pzoVuORXM\n"
+  "bpeNWl2l/HSN3VtUN6aCAKbN/X3o0GavCCMn5Fa85uJFsab4ss/uP+2PusU71+zP\n"
+  "sBm6p/2IbGvF5k3VPDA7X5YX61sukRjRBihY8xSnNYx1UcoOsX6AiPnbhifD8+xQ\n"
+  "Tlf8oJUCgYEA0BTfzqNpr9Wxw5/QXaSdw7S/0eP5a0C/nwURvmfSzuTD4equzbEN\n"
+  "d+dI/s2JMxrdj/I4uoAfUXRGaabevQIjFzC9uyE3LaOyR2zhuvAzX+vVcs6bSXeU\n"
+  "pKpCAcN+3Z3evMaX2f+z/nfSUAl2i4J2R+/LQAWJW4KwRky/m+cxpfUCgYEA6bN1\n"
+  "b73bMgM8wpNt6+fcmS+5n0iZihygQ2U2DEud8nZJL4Nrm1dwTnfZfJBnkGj6+0Q0\n"
+  "cOwj2KS0/wcEdJBP0jucU4v60VMhp75AQeHqidIde0bTViSRo3HWKXHBIFGYoU3T\n"
+  "LyPyKndbqsOObnsFXHn56Nwhr2HLf6nw4taGQY8CgYBoSW36FLCNbd6QGvLFXBGt\n"
+  "2lMhEM8az/K58kJ4WXSwOLtr6MD/WjNT2tkcy0puEJLm6BFCd6A6pLn9jaKou/92\n"
+  "SfltZjJPb3GUlp9zn5tAAeSSi7YMViBrfuFiHObij5LorefBXISLjuYbMwL03MgH\n"
+  "Ocl2JtA2ywMp2KFXs8GQWQKBgFyIVv5ogQrbZ0pvj31xr9HjqK6d01VxIi+tOmpB\n"
+  "4ocnOLEcaxX12BzprW55ytfOCVpF1jHD/imAhb3YrHXu0fwe6DXYXfZV4SSG2vB7\n"
+  "IB9z14KBN5qLHjNGFpMQXHSMek+b/ftTU0ZnPh9uEM5D3YqRLVd7GcdUhHvG8P8Q\n"
+  "C9aXAoGBAJtID6h8wOGMP0XYX5YYnhlC7dOLfk8UYrzlp3xhqVkzKthTQTj6wx9R\n"
+  "GtC4k7U1ki8oJsfcIlBNXd768fqDVWjYju5rzShMpo8OCTS6ipAblKjCxPPVhIpv\n"
+  "tWPlbSn1qj6wylstJ5/3Z+ZW5H4wIKp5jmLiioDhcP0L/Ex3Zx8O\n"
+  "-----END RSA PRIVATE KEY-----\n";
+
+/* test server self signed certificates */
+const char srv_self_signed_cert_pem[] = "-----BEGIN CERTIFICATE-----\n"
+  "MIIC+jCCAeSgAwIBAgIES0KCvTALBgkqhkiG9w0BAQUwFzEVMBMGA1UEAxMMdGVz\n"
+  "dF9jYV9jZXJ0MB4XDTEwMDEwNTAwMDcyNVoXDTQ1MDMxMjAwMDcyNVowFzEVMBMG\n"
+  "A1UEAxMMdGVzdF9jYV9jZXJ0MIIBHzALBgkqhkiG9w0BAQEDggEOADCCAQkCggEA\n"
+  "tDEagv3p9OUhUL55jMucxjNK9N5cuozhcnrwDfBSU6oVrqm5kPqO1I7Cggzw68Y5\n"
+  "jhTcBi4FXmYOZppm1R3MhSJ5JSi/67Q7X4J5rnJLXYGN27qjMpnoGQ/2xmsNG/is\n"
+  "i+h/2vbtPU+WP9SEJnTfPLLpZ7KqCAk7FUUzKsuLx3/SOKtdkrWxPKwYTgnDEN6D\n"
+  "JL7tEzCnG5DFc4mQ7YW9PaRdC3rS1T8PvQ3jB2BUnohM0cFvKRuiU35tU7h7CPbL\n"
+  "4L66VglXoiwqmgcrwI2U968bD0+wRQ5c5bzNoshJOzN6CTMh1IhbklSh/Z6FA/e8\n"
+  "hj0yVo2tdllXuJGVs3PIEwIDAQABo1UwUzAMBgNVHRMBAf8EAjAAMBMGA1UdJQQM\n"
+  "MAoGCCsGAQUFBwMBMA8GA1UdDwEB/wQFAwMHIAAwHQYDVR0OBBYEFDfU7pAv9LYn\n"
+  "n7jb4WHl4+Vgi2FnMAsGCSqGSIb3DQEBBQOCAQEAkaembPQMmv6OOjbIod8zTatr\n"
+  "x5Bwkwp3TOE1NRyy2OytzFIYRUkNrZYlcmrxcbNNycIK41CNVXbriFCF8gcmIq9y\n"
+  "vaKZn8Gcy+vGggv+1BP9IAPBGKRwSi0wmq9JoGE8hx+qqTpRSdfbM/cps/09hicO\n"
+  "0EIR7kWEbvnpMBcMKYOtYE9Gce7rdSMWVAsKc174xn8vW6TxCUvmWFv5DPg5HG1v\n"
+  "y1SUX73qafRo+W6FN4UC/DHfwRhF8RSKEnVbmgDVCs6GHdKBjU2qRgYyj6nWZqK1\n"
+  "XFUTWgia+Fl3D9vlsXaFcSZKA0Bq1eojl0B0AfeYAxTFwPWXscKvt/bXZfH8bg==\n"
+  "-----END CERTIFICATE-----\n";
+
+/* test server key */
+const char srv_key_pem[] = "-----BEGIN RSA PRIVATE KEY-----\n"
+  "MIIEpAIBAAKCAQEAtDEagv3p9OUhUL55jMucxjNK9N5cuozhcnrwDfBSU6oVrqm5\n"
+  "kPqO1I7Cggzw68Y5jhTcBi4FXmYOZppm1R3MhSJ5JSi/67Q7X4J5rnJLXYGN27qj\n"
+  "MpnoGQ/2xmsNG/isi+h/2vbtPU+WP9SEJnTfPLLpZ7KqCAk7FUUzKsuLx3/SOKtd\n"
+  "krWxPKwYTgnDEN6DJL7tEzCnG5DFc4mQ7YW9PaRdC3rS1T8PvQ3jB2BUnohM0cFv\n"
+  "KRuiU35tU7h7CPbL4L66VglXoiwqmgcrwI2U968bD0+wRQ5c5bzNoshJOzN6CTMh\n"
+  "1IhbklSh/Z6FA/e8hj0yVo2tdllXuJGVs3PIEwIDAQABAoIBAAEtcg+LFLGtoxjq\n"
+  "b+tFttBJfbRcfdG6ocYqBGmUXF+MgFs573DHX3sHNOQxlaNHtSgIclF1eYgNZFFt\n"
+  "VLIoBFTzfEQXoFosPUDoEuqVMeXLttmD7P2jwL780XJLZ4Xj6GY07npq1iGBcEZf\n"
+  "yCcdoyGkr9jgc5Auyis8DStGg/jfUBC4NBvF0GnuuNPAdYRPKUpKw9EatI+FdMjy\n"
+  "BuroD90fhdkK8EwMEVb9P17bdIc1MCIZFpUE9YHjVdK/oxCUhQ8KRfdbI4JU5Zh3\n"
+  "UtO6Jm2wFuP3VmeVpPvE/C2rxI70pyl6HMSiFGNc0rhJYCQ+yhohWj7nZ67H4vLx\n"
+  "plv5LxkCgYEAz7ewou8oFafDAMNoxaqKudvUg+lxXewdLDKaYBF5ACi9uAPCJ+v7\n"
+  "M5c/fvPFn/XHzo7xaXbtTAH3Z5xzBs+80OsvL+e1Ut4xR+ELRkybknh/s2wQeABk\n"
+  "Kb0vA59ukQGj12LV5phZMaVoXe6KJ7hZnN62d3K6m1wGE/k58i4pPLUCgYEA3hN8\n"
+  "G95zW7g0jVdSr+KUeVmephph9yh8Yb+3I3ojwOIv6d45TopGx8pFZlnBAMZf1ZQx\n"
+  "DIhzJNnaqZy/4w7RNaOGWnPA/5f+MIoHBiLGEEmfHC3lt087Yp9OuwDUHwpETYdV\n"
+  "o+KBCvVh60Et3bZUgF/1k/3YXxn8J5dsmJsjNqcCgYBLflyRa1BrRnTGMz9CEDCp\n"
+  "Si9b3h1Y4Hbd2GppHhCXMTd6yMrpDYhYANGQB3M9Juv+s88j4JhwNoq/uonH4Pqk\n"
+  "B8Y3qAQr4RuSH0WkwDUOsALhqBX4N1QwI1USAQEDbNAqeP5698X7GD3tXcQSmZrg\n"
+  "O8WfdjBCRNjkq4EW9xX/vQKBgQDONtmwJ0iHiu2BseyeVo/4fzfKlgUSNQ4K1rOA\n"
+  "xhIdMeu8Bxa/z7caHsGC4SVPSuYCtbE2Kh6BwapChcPJXCD45fgEViiJLuJiwEj1\n"
+  "caTpyvNsf1IoffJvCe9ZxtMyX549P8ZOgC3Dt0hN5CBrGLwu2Ox5l+YrqT10pi+5\n"
+  "JZX1UQKBgQCrcXrdkkDAc/a4+PxNRpJRLcU4fhv8/lr+UWItE8eUe7bd25bTQfQm\n"
+  "VpNKc/kAJ66PjIED6fy3ADhd2y4naT2a24uAgQ/M494J68qLnGh6K4JU/09uxR2v\n"
+  "1i2q/4FNLdFFk1XP4iNnTHRLZ+NYr2p5Y9RcvQfTjOauz8Ahav0lyg==\n"
+  "-----END RSA PRIVATE KEY-----\n";
+
+#endif
diff --git a/src/testcurl/perf_get.c b/src/testcurl/perf_get.c
new file mode 100644
index 0000000..1e42f7d
--- /dev/null
+++ b/src/testcurl/perf_get.c
@@ -0,0 +1,532 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2009, 2011 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file perf_get.c
+ * @brief benchmark simple GET operations (sequential access).
+ *        Note that we run libcurl in the same process at the
+ *        same time, so the execution time given is the combined
+ *        time for both MHD and libcurl; it is quite possible
+ *        that more time is spend with libcurl than with MHD,
+ *        so the performance scores calculated with this code
+ *        should NOT be used to compare with other HTTP servers
+ *        (since MHD is actually better); only the relative
+ *        scores between MHD versions are meaningful.  
+ *        Furthermore, this code ONLY tests MHD processing
+ *        a single request at a time.  This is again
+ *        not universally meaningful (i.e. when comparing
+ *        multithreaded vs. single-threaded or select/poll).
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "gauger.h"
+
+#ifndef WINDOWS
+#include <unistd.h>
+#include <sys/socket.h>
+#endif
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+/**
+ * How many rounds of operations do we do for each
+ * test?
+ */
+#define ROUNDS 500
+
+/**
+ * Do we use HTTP 1.1?
+ */
+static int oneone;
+
+/**
+ * Response to return (re-used).
+ */
+static struct MHD_Response *response;
+
+/**
+ * Time this round was started.
+ */
+static unsigned long long start_time;
+
+
+/**
+ * Get the current timestamp 
+ *
+ * @return current time in ms
+ */
+static unsigned long long 
+now ()
+{
+  struct timeval tv;
+
+  gettimeofday (&tv, NULL);
+  return (((unsigned long long) tv.tv_sec * 1000LL) +
+	  ((unsigned long long) tv.tv_usec / 1000LL));
+}
+
+
+/**
+ * Start the timer.
+ */
+static void 
+start_timer()
+{
+  start_time = now ();
+}
+
+
+/**
+ * Stop the timer and report performance
+ *
+ * @param desc description of the threading mode we used
+ */
+static void 
+stop (const char *desc)
+{
+  double rps = ((double) (ROUNDS * 1000)) / ((double) (now() - start_time));
+
+  fprintf (stderr,
+	   "Sequential GETs using %s: %f %s\n",
+	   desc,
+	   rps,
+	   "requests/s");
+  GAUGER (desc,
+	  "Sequential GETs",
+	  rps,
+	  "requests/s");
+}
+
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+
+static size_t
+copyBuffer (void *ptr, 
+	    size_t size, size_t nmemb, 
+	    void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  int ret;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  *unused = NULL;
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+
+static int
+testInternalGet (int port, int poll_flag)
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+  unsigned int i;
+  char url[64];
+
+  sprintf(url, "http://127.0.0.1:%d/hello_world", port);
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG  | poll_flag,
+                        port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  start_timer ();
+  for (i=0;i<ROUNDS;i++)
+    {
+      cbc.pos = 0;
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, url);
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+      if (oneone)
+	curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+	curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      /* NOTE: use of CONNECTTIMEOUT without also
+	 setting NOSIGNAL results in really weird
+	 crashes on my system!*/
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      if (CURLE_OK != (errornum = curl_easy_perform (c)))
+	{
+	  fprintf (stderr,
+		   "curl_easy_perform failed: `%s'\n",
+		   curl_easy_strerror (errornum));
+	  curl_easy_cleanup (c);
+	  MHD_stop_daemon (d);
+	  return 2;
+	}
+      curl_easy_cleanup (c);
+    }
+  stop (poll_flag == MHD_USE_POLL ? "internal poll" :
+	poll_flag == MHD_USE_EPOLL_LINUX_ONLY ? "internal epoll" : "internal select");
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 4;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 8;
+  return 0;
+}
+
+
+static int
+testMultithreadedGet (int port, int poll_flag)
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+  unsigned int i;
+  char url[64];
+
+  sprintf(url, "http://127.0.0.1:%d/hello_world", port);
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG  | poll_flag,
+                        port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  start_timer ();
+  for (i=0;i<ROUNDS;i++)
+    {
+      cbc.pos = 0;
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, url);
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+      if (oneone)
+	curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+	curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+      /* NOTE: use of CONNECTTIMEOUT without also
+	 setting NOSIGNAL results in really weird
+	 crashes on my system! */
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      if (CURLE_OK != (errornum = curl_easy_perform (c)))
+	{
+	  fprintf (stderr,
+		   "curl_easy_perform failed: `%s'\n",
+		   curl_easy_strerror (errornum));
+	  curl_easy_cleanup (c);
+	  MHD_stop_daemon (d);
+	  return 32;
+	}
+      curl_easy_cleanup (c);
+    }
+  stop ((poll_flag & MHD_USE_POLL) ? "thread with poll" :
+	(poll_flag & MHD_USE_EPOLL_LINUX_ONLY) ? "thread with epoll" : "thread with select");
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 64;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+  return 0;
+}
+
+static int
+testMultithreadedPoolGet (int port, int poll_flag)
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+  unsigned int i;
+  char url[64];
+
+  sprintf(url, "http://127.0.0.1:%d/hello_world", port);
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag,
+                        port, NULL, NULL, &ahc_echo, "GET",
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  start_timer ();
+  for (i=0;i<ROUNDS;i++)
+    {
+      cbc.pos = 0;
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, url);
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+      if (oneone)
+	curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+	curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+      /* NOTE: use of CONNECTTIMEOUT without also
+	 setting NOSIGNAL results in really weird
+	 crashes on my system!*/
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      if (CURLE_OK != (errornum = curl_easy_perform (c)))
+	{
+	  fprintf (stderr,
+		   "curl_easy_perform failed: `%s'\n",
+		   curl_easy_strerror (errornum));
+	  curl_easy_cleanup (c);
+	  MHD_stop_daemon (d);
+	  return 32;
+	}
+      curl_easy_cleanup (c);
+    }
+  stop (0 != (poll_flag & MHD_USE_POLL) ? "thread pool with poll" : 
+	0 != (poll_flag & MHD_USE_EPOLL_LINUX_ONLY) ? "thread pool with epoll" : "thread pool with select");
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 64;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+  return 0;
+}
+
+static int
+testExternalGet (int port)
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+  unsigned int i;
+  char url[64];
+
+  sprintf(url, "http://127.0.0.1:%d/hello_world", port);
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  start_timer ();
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  for (i=0;i<ROUNDS;i++)
+    {
+      cbc.pos = 0;
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, url);
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      if (oneone)
+	curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+	curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+      /* NOTE: use of CONNECTTIMEOUT without also
+	 setting NOSIGNAL results in really weird
+	 crashes on my system! */
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      mret = curl_multi_add_handle (multi, c);
+      if (mret != CURLM_OK)
+	{
+	  curl_multi_cleanup (multi);
+	  curl_easy_cleanup (c);
+	  MHD_stop_daemon (d);
+	  return 1024;
+	}
+      start = time (NULL);
+      while ((time (NULL) - start < 5) && (c != NULL))
+	{
+	  max = 0;
+	  FD_ZERO (&rs);
+	  FD_ZERO (&ws);
+	  FD_ZERO (&es);
+	  curl_multi_perform (multi, &running);
+	  mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+	  if (mret != CURLM_OK)
+	    {
+	      curl_multi_remove_handle (multi, c);
+	      curl_multi_cleanup (multi);
+	      curl_easy_cleanup (c);
+	      MHD_stop_daemon (d);
+	      return 2048;
+	    }
+	  if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+	    {
+	      curl_multi_remove_handle (multi, c);
+	      curl_multi_cleanup (multi);
+	      curl_easy_cleanup (c);
+	      MHD_stop_daemon (d);
+	      return 4096;
+	    }
+	  tv.tv_sec = 0;
+	  tv.tv_usec = 1000;
+	  select (max + 1, &rs, &ws, &es, &tv);
+	  curl_multi_perform (multi, &running);
+	  if (running == 0)
+	    {
+	      msg = curl_multi_info_read (multi, &running);
+	      if (msg == NULL)
+		break;
+	      if (msg->msg == CURLMSG_DONE)
+		{
+		  if (msg->data.result != CURLE_OK)
+		    printf ("%s failed at %s:%d: `%s'\n",
+			    "curl_multi_perform",
+			    __FILE__,
+			    __LINE__, curl_easy_strerror (msg->data.result));
+		  curl_multi_remove_handle (multi, c);
+		  curl_easy_cleanup (c);
+		  c = NULL;
+		}
+	    }
+	  /* two possibilities here; as select sets are
+	     tiny, this makes virtually no difference
+	     in actual runtime right now, even though the
+	     number of select calls is virtually cut in half
+	     (and 'select' is the most expensive of our system
+	     calls according to 'strace') */
+	  if (0)
+	    MHD_run (d);
+	  else
+	    MHD_run_from_select (d, &rs, &ws, &es);
+	}
+      if (NULL != c)
+	{
+	  curl_multi_remove_handle (multi, c);
+	  curl_easy_cleanup (c);
+	  fprintf (stderr, "Timeout!?\n");
+	}
+    }
+  stop ("external select");
+  if (multi != NULL)
+    {
+      curl_multi_cleanup (multi);
+    }
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 8192;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 16384;
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+  int port = 1081;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  response = MHD_create_response_from_buffer (strlen ("/hello_world"),
+					      "/hello_world",
+					      MHD_RESPMEM_MUST_COPY);
+  errorCount += testExternalGet (port++);
+  errorCount += testInternalGet (port++, 0);
+  errorCount += testMultithreadedGet (port++, 0);
+  errorCount += testMultithreadedPoolGet (port++, 0);
+  if (MHD_YES == MHD_is_feature_supported(MHD_FEATURE_POLL))
+    {
+      errorCount += testInternalGet(port++, MHD_USE_POLL);
+      errorCount += testMultithreadedGet(port++, MHD_USE_POLL);
+      errorCount += testMultithreadedPoolGet(port++, MHD_USE_POLL);
+    }
+  if (MHD_YES == MHD_is_feature_supported(MHD_FEATURE_EPOLL))
+    {
+      errorCount += testInternalGet(port++, MHD_USE_EPOLL_LINUX_ONLY);
+      errorCount += testMultithreadedPoolGet(port++, MHD_USE_EPOLL_LINUX_ONLY);
+    }
+  MHD_destroy_response (response);
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/perf_get_concurrent.c b/src/testcurl/perf_get_concurrent.c
new file mode 100644
index 0000000..28559ee
--- /dev/null
+++ b/src/testcurl/perf_get_concurrent.c
@@ -0,0 +1,364 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2009, 2011 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file perf_get_concurrent.c
+ * @brief benchmark concurrent GET operations
+ *        Note that we run libcurl on the machine at the
+ *        same time, so the execution time may be influenced
+ *        by the concurrent activity; it is quite possible
+ *        that more time is spend with libcurl than with MHD,
+ *        so the performance scores calculated with this code
+ *        should NOT be used to compare with other HTTP servers
+ *        (since MHD is actually better); only the relative
+ *        scores between MHD versions are meaningful.  
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "gauger.h"
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+/**
+ * How many rounds of operations do we do for each
+ * test (total number of requests will be ROUNDS * PAR).
+ */
+#define ROUNDS 500
+
+/**
+ * How many requests do we do in parallel?
+ */
+#define PAR CPU_COUNT
+
+/**
+ * Do we use HTTP 1.1?
+ */
+static int oneone;
+
+/**
+ * Response to return (re-used).
+ */
+static struct MHD_Response *response;
+
+/**
+ * Time this round was started.
+ */
+static unsigned long long start_time;
+
+
+/**
+ * Get the current timestamp 
+ *
+ * @return current time in ms
+ */
+static unsigned long long 
+now ()
+{
+  struct timeval tv;
+
+  gettimeofday (&tv, NULL);
+  return (((unsigned long long) tv.tv_sec * 1000LL) +
+	  ((unsigned long long) tv.tv_usec / 1000LL));
+}
+
+
+/**
+ * Start the timer.
+ */
+static void 
+start_timer()
+{
+  start_time = now ();
+}
+
+
+/**
+ * Stop the timer and report performance
+ *
+ * @param desc description of the threading mode we used
+ */
+static void 
+stop (const char *desc)
+{
+  double rps = ((double) (PAR * ROUNDS * 1000)) / ((double) (now() - start_time));
+
+  fprintf (stderr,
+	   "Parallel GETs using %s: %f %s\n",
+	   desc,
+	   rps,
+	   "requests/s");
+  GAUGER (desc,
+	  "Parallel GETs",
+	  rps,
+	  "requests/s");
+}
+
+
+static size_t
+copyBuffer (void *ptr, 
+	    size_t size, size_t nmemb, 
+	    void *ctx)
+{
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  int ret;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  *unused = NULL;
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+
+static pid_t
+do_gets (int port)
+{
+  pid_t ret;
+  CURL *c;
+  CURLcode errornum;
+  unsigned int i;
+  unsigned int j;
+  pid_t par[PAR];
+  char url[64];
+
+  sprintf(url, "http://127.0.0.1:%d/hello_world", port);
+  
+  ret = fork ();
+  if (ret == -1) abort ();
+  if (ret != 0)
+    return ret;
+  for (j=0;j<PAR;j++)
+    {
+      par[j] = fork ();
+      if (par[j] == 0)
+	{
+	  for (i=0;i<ROUNDS;i++)
+	    {
+	      c = curl_easy_init ();
+	      curl_easy_setopt (c, CURLOPT_URL, url);
+	      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+	      curl_easy_setopt (c, CURLOPT_WRITEDATA, NULL);
+	      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+	      curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+	      if (oneone)
+		curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+	      else
+		curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+	      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+	      /* NOTE: use of CONNECTTIMEOUT without also
+		 setting NOSIGNAL results in really weird
+		 crashes on my system! */
+	      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+	      if (CURLE_OK != (errornum = curl_easy_perform (c)))
+		{
+		  fprintf (stderr,
+			   "curl_easy_perform failed: `%s'\n",
+			   curl_easy_strerror (errornum));
+		  curl_easy_cleanup (c);
+		  _exit (1);
+		}
+	      curl_easy_cleanup (c);
+	    }
+	  _exit (0);
+	}
+    }
+  for (j=0;j<PAR;j++)
+    waitpid (par[j], NULL, 0);
+  _exit (0);
+}
+
+
+static void 
+join_gets (pid_t pid)
+{
+  int status;
+  
+  status = 1;
+  waitpid (pid, &status, 0);
+  if (0 != status)
+    abort ();
+}
+
+
+static int
+testInternalGet (int port, int poll_flag)
+{
+  struct MHD_Daemon *d;
+
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG  | poll_flag,
+                        port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  start_timer ();
+  join_gets (do_gets (port));
+  stop (poll_flag ? "internal poll" : "internal select");
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+static int
+testMultithreadedGet (int port, int poll_flag)
+{
+  struct MHD_Daemon *d;
+
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG  | poll_flag,
+                        port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  start_timer ();
+  join_gets (do_gets (port));
+  stop (poll_flag ? "thread with poll" : "thread with select");
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+static int
+testMultithreadedPoolGet (int port, int poll_flag)
+{
+  struct MHD_Daemon *d;
+
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag,
+                        port, NULL, NULL, &ahc_echo, "GET",
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  start_timer ();
+  join_gets (do_gets (port));
+  stop (poll_flag ? "thread pool with poll" : "thread pool with select");
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+static int
+testExternalGet (int port)
+{
+  struct MHD_Daemon *d;
+  pid_t pid;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  struct timeval tv;
+  MHD_UNSIGNED_LONG_LONG tt;
+  int tret;
+
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  start_timer ();
+  pid = do_gets (port);
+  while (0 == waitpid (pid, NULL, WNOHANG))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+	{
+	  MHD_stop_daemon (d);
+	  return 4096;
+	}
+      tret = MHD_get_timeout (d, &tt);
+      if (MHD_YES != tret) tt = 1;
+      tv.tv_sec = tt / 1000;
+      tv.tv_usec = 1000 * (tt % 1000);
+      if (-1 == select (max + 1, &rs, &ws, &es, &tv))
+	{
+	  if (EINTR == errno)
+	    continue;
+	  fprintf (stderr,
+		   "select failed: %s\n",
+		   strerror (errno));
+	  break;	      	  
+	}
+      MHD_run (d);
+    }
+  stop ("external select");
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+  int port = 1081;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  response = MHD_create_response_from_buffer (strlen ("/hello_world"),
+					      "/hello_world",
+					      MHD_RESPMEM_MUST_COPY);
+  errorCount += testInternalGet (port++, 0);
+  errorCount += testMultithreadedGet (port++, 0);
+  errorCount += testMultithreadedPoolGet (port++, 0);
+  errorCount += testExternalGet (port++);
+#ifndef WINDOWS
+  errorCount += testInternalGet (port++, MHD_USE_POLL);
+  errorCount += testMultithreadedGet (port++, MHD_USE_POLL);
+  errorCount += testMultithreadedPoolGet (port++, MHD_USE_POLL);
+#endif
+#if EPOLL_SUPPORT
+  errorCount += testInternalGet (port++, MHD_USE_EPOLL_LINUX_ONLY);
+  errorCount += testMultithreadedPoolGet (port++, MHD_USE_EPOLL_LINUX_ONLY);
+#endif
+  MHD_destroy_response (response);
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_callback.c b/src/testcurl/test_callback.c
new file mode 100644
index 0000000..83233e6
--- /dev/null
+++ b/src/testcurl/test_callback.c
@@ -0,0 +1,189 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2009, 2011 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_callback.c
+ * @brief Testcase for MHD not calling the callback too often
+ * @author Jan Seeger 
+ * @author Christian Grothoff
+ */
+
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+
+struct callback_closure {
+  unsigned int called;
+};
+
+
+static ssize_t 
+called_twice(void *cls, uint64_t pos, char *buf, size_t max) 
+{
+  struct callback_closure *cls2 = cls;
+  
+  if (cls2->called == 0) 
+    {
+      memset(buf, 0, max);
+      strcat(buf, "test");
+      cls2->called = 1;
+      return strlen(buf);
+    }
+  if (cls2->called == 1) 
+    {
+      cls2->called = 2;
+      return MHD_CONTENT_READER_END_OF_STREAM;
+    }
+  fprintf(stderr, 
+	  "Handler called after returning END_OF_STREAM!\n");
+  return MHD_CONTENT_READER_END_WITH_ERROR;
+}
+
+
+static int
+callback(void *cls, struct MHD_Connection *connection, const char *url,
+	 const char *method, const char *version, const char *upload_data,
+	 size_t *upload_data_size, void **con_cls) {
+  struct callback_closure *cbc = calloc(1, sizeof(struct callback_closure));
+  struct MHD_Response *r;
+
+  r = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 1024, 
+					 &called_twice, cbc, 
+					 &free);
+  MHD_queue_response(connection, 200, r);
+  MHD_destroy_response(r);
+  return MHD_YES;
+}
+
+
+static size_t
+discard_buffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  return size * nmemb;
+}
+
+
+int main(int argc, char **argv) 
+{
+  struct MHD_Daemon *d;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  CURL *c;
+  CURLM *multi;
+  CURLMcode mret;
+  struct CURLMsg *msg;
+  int running;
+  struct timeval tv;
+  int extra;
+
+  d = MHD_start_daemon(0, 
+		       8000,
+		       NULL,
+		       NULL,
+		       callback,
+		       NULL,
+		       MHD_OPTION_END);
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:8000/");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &discard_buffer);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 1;
+    }
+  mret = curl_multi_add_handle (multi, c);
+  if (mret != CURLM_OK)
+    {
+      curl_multi_cleanup (multi);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+  extra = 10;
+  while ( (c != NULL) || (--extra > 0) )
+    {
+      max = MHD_INVALID_SOCKET;
+      FD_ZERO(&ws);
+      FD_ZERO(&rs);
+      FD_ZERO(&es);
+      curl_multi_perform (multi, &running);
+      if (NULL != multi)
+	{
+	  mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+	  if (mret != CURLM_OK)
+	    {
+	      curl_multi_remove_handle (multi, c);
+	      curl_multi_cleanup (multi);
+	      curl_easy_cleanup (c);
+	      MHD_stop_daemon (d);
+	      return 3;
+	    }   
+	}
+      if (MHD_YES !=
+	  MHD_get_fdset(d, &rs, &ws, &es, &max))
+	{
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+	  return 4;
+	}
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select(max + 1, &rs, &ws, &es, &tv);
+      if (NULL != multi)
+	{
+	  curl_multi_perform (multi, &running);
+	  if (running == 0)
+	    {
+	      msg = curl_multi_info_read (multi, &running);
+	      if (msg == NULL)
+		break;
+	      if (msg->msg == CURLMSG_DONE)
+		{
+		  if (msg->data.result != CURLE_OK)
+		    printf ("%s failed at %s:%d: `%s'\n",
+			    "curl_multi_perform",
+			    __FILE__,
+			    __LINE__, curl_easy_strerror (msg->data.result));
+		  curl_multi_remove_handle (multi, c);
+		  curl_multi_cleanup (multi);
+		  curl_easy_cleanup (c);
+		  c = NULL;
+		  multi = NULL;
+		}
+	    }
+	}
+      MHD_run(d);
+    }
+  MHD_stop_daemon(d);
+  return 0;
+}
diff --git a/src/testcurl/test_concurrent_stop.c b/src/testcurl/test_concurrent_stop.c
new file mode 100644
index 0000000..60cc98d
--- /dev/null
+++ b/src/testcurl/test_concurrent_stop.c
@@ -0,0 +1,228 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2009, 2011, 2015 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 3, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_concurrent_stop.c
+ * @brief test stopping server while concurrent GETs are ongoing
+ * @author Christian Grothoff
+ */
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "gauger.h"
+
+#ifdef CPU_COUNT
+#undef CPU_COUNT
+#endif
+#define CPU_COUNT 40
+
+
+/**
+ * How many rounds of operations do we do for each
+ * test (total number of requests will be ROUNDS * PAR).
+ */
+#define ROUNDS 50000
+
+/**
+ * How many requests do we do in parallel?
+ */
+#define PAR CPU_COUNT
+
+/**
+ * Do we use HTTP 1.1?
+ */
+static int oneone;
+
+/**
+ * Response to return (re-used).
+ */
+static struct MHD_Response *response;
+
+
+static size_t
+copyBuffer (void *ptr,
+	    size_t size, size_t nmemb,
+	    void *ctx)
+{
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  int ret;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  *unused = NULL;
+  ret = MHD_queue_response (connection,
+                            MHD_HTTP_OK,
+                            response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+
+static pid_t
+do_gets (int port)
+{
+  pid_t ret;
+  CURL *c;
+  CURLcode errornum;
+  unsigned int i;
+  unsigned int j;
+  pid_t par[PAR];
+  char url[64];
+
+  sprintf(url, "http://127.0.0.1:%d/hello_world", port);
+
+  ret = fork ();
+  if (ret == -1) abort ();
+  if (ret != 0)
+    return ret;
+  for (j=0;j<PAR;j++)
+    {
+      par[j] = fork ();
+      if (par[j] == 0)
+	{
+	  for (i=0;i<ROUNDS;i++)
+	    {
+	      c = curl_easy_init ();
+	      curl_easy_setopt (c, CURLOPT_URL, url);
+	      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+	      curl_easy_setopt (c, CURLOPT_WRITEDATA, NULL);
+	      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+	      curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+	      if (oneone)
+		curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+	      else
+		curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+	      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+	      /* NOTE: use of CONNECTTIMEOUT without also
+		 setting NOSIGNAL results in really weird
+		 crashes on my system! */
+	      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+	      if (CURLE_OK != (errornum = curl_easy_perform (c)))
+		{
+		  curl_easy_cleanup (c);
+		  _exit (1);
+		}
+	      curl_easy_cleanup (c);
+	    }
+	  _exit (0);
+	}
+    }
+  for (j=0;j<PAR;j++)
+    waitpid (par[j], NULL, 0);
+  _exit (0);
+}
+
+
+static void
+join_gets (pid_t pid)
+{
+  int status;
+
+  status = 1;
+  waitpid (pid, &status, 0);
+  if (0 != status)
+    abort ();
+}
+
+
+static int
+testMultithreadedGet (int port, int poll_flag)
+{
+  struct MHD_Daemon *d;
+  pid_t p;
+
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG  | poll_flag,
+                        port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  p = do_gets (port);
+  sleep (1);
+  MHD_stop_daemon (d);
+  join_gets (p);
+  return 0;
+}
+
+
+static int
+testMultithreadedPoolGet (int port, int poll_flag)
+{
+  struct MHD_Daemon *d;
+  pid_t p;
+
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag,
+                        port,
+                        NULL, NULL,
+                        &ahc_echo, "GET",
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT,
+                        MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  p = do_gets (port);
+  sleep (1);
+  MHD_stop_daemon (d);
+  join_gets (p);
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+  int port = 1081;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  response = MHD_create_response_from_buffer (strlen ("/hello_world"),
+					      "/hello_world",
+					      MHD_RESPMEM_MUST_COPY);
+  errorCount += testMultithreadedGet (port++, 0);
+  errorCount += testMultithreadedPoolGet (port++, 0);
+  MHD_destroy_response (response);
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_digestauth.c b/src/testcurl/test_digestauth.c
new file mode 100644
index 0000000..255e086
--- /dev/null
+++ b/src/testcurl/test_digestauth.c
@@ -0,0 +1,247 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2010 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file daemontest_digestauth.c
+ * @brief  Testcase for libmicrohttpd Digest Auth
+ * @author Amr Ali
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef HAVE_GCRYPT_H
+#include <gcrypt.h>
+#endif
+
+#ifndef WINDOWS
+#include <sys/socket.h>
+#include <unistd.h>
+#else
+#include <wincrypt.h>
+#endif
+
+#define PAGE "<html><head><title>libmicrohttpd demo</title></head><body>Access granted</body></html>"
+
+#define DENIED "<html><head><title>libmicrohttpd demo</title></head><body>Access denied</body></html>"
+
+#define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4"
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  struct MHD_Response *response;
+  char *username;
+  const char *password = "testpass";
+  const char *realm = "test@example.com";
+  int ret;
+
+  username = MHD_digest_auth_get_username(connection);
+  if ( (username == NULL) ||
+       (0 != strcmp (username, "testuser")) )
+    {
+      response = MHD_create_response_from_buffer(strlen (DENIED), 
+						 DENIED,
+						 MHD_RESPMEM_PERSISTENT);  
+      ret = MHD_queue_auth_fail_response(connection, realm,
+					 MY_OPAQUE,
+					 response,
+					 MHD_NO);    
+      MHD_destroy_response(response);  
+      return ret;
+    }
+  ret = MHD_digest_auth_check(connection, realm,
+			      username, 
+			      password, 
+			      300);
+  free(username);
+  if ( (ret == MHD_INVALID_NONCE) ||
+       (ret == MHD_NO) )
+    {
+      response = MHD_create_response_from_buffer(strlen (DENIED), 
+						 DENIED,
+						 MHD_RESPMEM_PERSISTENT);  
+      if (NULL == response) 
+	return MHD_NO;
+      ret = MHD_queue_auth_fail_response(connection, realm,
+					 MY_OPAQUE,
+					 response,
+					 (ret == MHD_INVALID_NONCE) ? MHD_YES : MHD_NO);  
+      MHD_destroy_response(response);  
+      return ret;
+    }
+  response = MHD_create_response_from_buffer(strlen(PAGE), PAGE,
+					     MHD_RESPMEM_PERSISTENT);
+  ret = MHD_queue_response(connection, MHD_HTTP_OK, response);  
+  MHD_destroy_response(response);
+  return ret;
+}
+
+
+static int
+testDigestAuth ()
+{
+  int fd;
+  CURL *c;
+  CURLcode errornum;
+  struct MHD_Daemon *d;
+  struct CBC cbc;
+  size_t len;
+  size_t off = 0;
+  char buf[2048];
+  char rnd[8];
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+#ifndef WINDOWS
+  fd = open("/dev/urandom", O_RDONLY);
+  if (-1 == fd)
+    {
+	  fprintf(stderr, "Failed to open `%s': %s\n",
+	       "/dev/urandom",
+		   strerror(errno));
+	  return 1;
+	}
+  while (off < 8)
+	{
+	  len = read(fd, rnd, 8);
+	  if (len == -1)
+	    {
+		  fprintf(stderr, "Failed to read `%s': %s\n",
+		       "/dev/urandom",
+			   strerror(errno));
+		  (void) close(fd);
+		  return 1;
+		}
+	  off += len;
+	}
+  (void) close(fd);
+#else
+  {
+    HCRYPTPROV cc;
+    BOOL b;
+    b = CryptAcquireContext (&cc, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
+    if (b == 0)
+    {
+      fprintf (stderr, "Failed to acquire crypto provider context: %lu\n",
+          GetLastError ());
+      return 1;
+    }
+    b = CryptGenRandom (cc, 8, rnd);
+    if (b == 0)
+    {
+      fprintf (stderr, "Failed to generate 8 random bytes: %lu\n",
+          GetLastError ());
+    }
+    CryptReleaseContext (cc, 0);
+    if (b == 0)
+      return 1;
+  }
+#endif
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1337, NULL, NULL, &ahc_echo, PAGE,
+			MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (rnd), rnd,
+			MHD_OPTION_NONCE_NC_SIZE, 300,
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1337/");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
+  curl_easy_setopt (c, CURLOPT_USERPWD, "testuser:testpass");
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system!*/
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen (PAGE))
+    return 4;
+  if (0 != strncmp (PAGE, cbc.buf, strlen (PAGE)))
+    return 8;
+  return 0;
+}
+
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+#ifdef HAVE_GCRYPT_H
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+#endif
+if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testDigestAuth ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_digestauth_with_arguments.c b/src/testcurl/test_digestauth_with_arguments.c
new file mode 100644
index 0000000..51868ab
--- /dev/null
+++ b/src/testcurl/test_digestauth_with_arguments.c
@@ -0,0 +1,245 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2010, 2012 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file daemontest_digestauth_with_arguments.c
+ * @brief  Testcase for libmicrohttpd Digest Auth with arguments
+ * @author Amr Ali
+ */
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef HAVE_GCRYPT_H
+#include <gcrypt.h>
+#endif
+
+#ifndef WINDOWS
+#include <sys/socket.h>
+#include <unistd.h>
+#else
+#include <wincrypt.h>
+#endif
+
+#define PAGE "<html><head><title>libmicrohttpd demo</title></head><body>Access granted</body></html>"
+
+#define DENIED "<html><head><title>libmicrohttpd demo</title></head><body>Access denied</body></html>"
+
+#define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4"
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  struct MHD_Response *response;
+  char *username;
+  const char *password = "testpass";
+  const char *realm = "test@example.com";
+  int ret;
+
+  username = MHD_digest_auth_get_username(connection);
+  if ( (username == NULL) ||
+       (0 != strcmp (username, "testuser")) )
+    {
+      response = MHD_create_response_from_buffer(strlen (DENIED), 
+						 DENIED,
+						 MHD_RESPMEM_PERSISTENT);  
+      ret = MHD_queue_auth_fail_response(connection, realm,
+					 MY_OPAQUE,
+					 response,
+					 MHD_NO);    
+      MHD_destroy_response(response);  
+      return ret;
+    }
+  ret = MHD_digest_auth_check(connection, realm,
+			      username, 
+			      password, 
+			      300);
+  free(username);
+  if ( (ret == MHD_INVALID_NONCE) ||
+       (ret == MHD_NO) )
+    {
+      response = MHD_create_response_from_buffer(strlen (DENIED), 
+						 DENIED,
+						 MHD_RESPMEM_PERSISTENT);  
+      if (NULL == response) 
+	return MHD_NO;
+      ret = MHD_queue_auth_fail_response(connection, realm,
+					 MY_OPAQUE,
+					 response,
+					 (ret == MHD_INVALID_NONCE) ? MHD_YES : MHD_NO);  
+      MHD_destroy_response(response);  
+      return ret;
+    }
+  response = MHD_create_response_from_buffer(strlen(PAGE), PAGE,
+					     MHD_RESPMEM_PERSISTENT);
+  ret = MHD_queue_response(connection, MHD_HTTP_OK, response);  
+  MHD_destroy_response(response);
+  return ret;
+}
+
+
+static int
+testDigestAuth ()
+{
+  int fd;
+  CURL *c;
+  CURLcode errornum;
+  struct MHD_Daemon *d;
+  struct CBC cbc;
+  size_t len;
+  size_t off = 0;
+  char buf[2048];
+  char rnd[8];
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+#ifndef WINDOWS
+  fd = open("/dev/urandom", O_RDONLY);
+  if (-1 == fd)
+    {
+	  fprintf(stderr, "Failed to open `%s': %s\n",
+	       "/dev/urandom",
+		   strerror(errno));
+	  return 1;
+	}
+  while (off < 8)
+	{
+	  len = read(fd, rnd, 8);
+	  if (len == -1)
+	    {
+		  fprintf(stderr, "Failed to read `%s': %s\n",
+		       "/dev/urandom",
+			   strerror(errno));
+		  (void) close(fd);
+		  return 1;
+		}
+	  off += len;
+	}
+  (void) close(fd);
+#else
+  {
+    HCRYPTPROV cc;
+    BOOL b;
+    b = CryptAcquireContext (&cc, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
+    if (b == 0)
+    {
+      fprintf (stderr, "Failed to acquire crypto provider context: %lu\n",
+          GetLastError ());
+      return 1;
+    }
+    b = CryptGenRandom (cc, 8, rnd);
+    if (b == 0)
+    {
+      fprintf (stderr, "Failed to generate 8 random bytes: %lu\n",
+          GetLastError ());
+    }
+    CryptReleaseContext (cc, 0);
+    if (b == 0)
+      return 1;
+  }
+#endif
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1337, NULL, NULL, &ahc_echo, PAGE,
+			MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (rnd), rnd,
+			MHD_OPTION_NONCE_NC_SIZE, 300,
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1337/foo?key=value");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
+  curl_easy_setopt (c, CURLOPT_USERPWD, "testuser:testpass");
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system!*/
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen (PAGE))
+    return 4;
+  if (0 != strncmp (PAGE, cbc.buf, strlen (PAGE)))
+    return 8;
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+#ifdef HAVE_GCRYPT_H
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+#endif
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testDigestAuth ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_get.c b/src/testcurl/test_get.c
new file mode 100644
index 0000000..e4c531d
--- /dev/null
+++ b/src/testcurl/test_get.c
@@ -0,0 +1,640 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2009, 2011 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_get.c
+ * @brief  Testcase for libmicrohttpd GET operations
+ *         TODO: test parsing of query
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include "platform_interface.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif
+
+#ifndef WINDOWS
+#include <unistd.h>
+#include <sys/socket.h>
+#endif
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+static int oneone;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  *unused = NULL;
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url,
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+
+static int
+testInternalGet (int poll_flag)
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG  | poll_flag,
+                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system!*/
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 4;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 8;
+  return 0;
+}
+
+
+static int
+testMultithreadedGet (int poll_flag)
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG  | poll_flag,
+                        1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 64;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+  return 0;
+}
+
+
+static int
+testMultithreadedPoolGet (int poll_flag)
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag,
+                        1081, NULL, NULL, &ahc_echo, "GET",
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system!*/
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 64;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+  return 0;
+}
+
+
+static int
+testExternalGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        1082, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  mret = curl_multi_add_handle (multi, c);
+  if (mret != CURLM_OK)
+    {
+      curl_multi_cleanup (multi);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 1024;
+    }
+  start = time (NULL);
+  while ((time (NULL) - start < 5) && (multi != NULL))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      curl_multi_perform (multi, &running);
+      mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 2048;
+        }
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 4096;
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select (max + 1, &rs, &ws, &es, &tv);
+      curl_multi_perform (multi, &running);
+      if (running == 0)
+        {
+          msg = curl_multi_info_read (multi, &running);
+          if (msg == NULL)
+            break;
+          if (msg->msg == CURLMSG_DONE)
+            {
+              if (msg->data.result != CURLE_OK)
+                printf ("%s failed at %s:%d: `%s'\n",
+                        "curl_multi_perform",
+                        __FILE__,
+                        __LINE__, curl_easy_strerror (msg->data.result));
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              c = NULL;
+              multi = NULL;
+            }
+        }
+      MHD_run (d);
+    }
+  if (multi != NULL)
+    {
+      curl_multi_remove_handle (multi, c);
+      curl_easy_cleanup (c);
+      curl_multi_cleanup (multi);
+    }
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 8192;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 16384;
+  return 0;
+}
+
+
+static int
+testUnknownPortGet (int poll_flag)
+{
+  struct MHD_Daemon *d;
+  const union MHD_DaemonInfo *di;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  struct sockaddr_in addr;
+  socklen_t addr_len = sizeof(addr);
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_port = 0;
+  addr.sin_addr.s_addr = INADDR_ANY;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG  | poll_flag,
+                        1, NULL, NULL, &ahc_echo, "GET",
+                        MHD_OPTION_SOCK_ADDR, &addr,
+                        MHD_OPTION_END);
+  if (d == NULL)
+    return 32768;
+
+  di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD);
+  if (di == NULL)
+    return 65536;
+
+  if (0 != getsockname(di->listen_fd, (struct sockaddr *) &addr, &addr_len))
+    return 131072;
+
+  if (addr.sin_family != AF_INET)
+    return 26214;
+
+  snprintf(buf, sizeof(buf), "http://127.0.0.1:%hu/hello_world",
+           ntohs(addr.sin_port));
+
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, buf);
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 524288;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 1048576;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 2097152;
+  return 0;
+}
+
+
+static int
+testStopRace (int poll_flag)
+{
+    struct sockaddr_in sin;
+    MHD_socket fd;
+    struct MHD_Daemon *d;
+
+    d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG | poll_flag,
+                         1081, NULL, NULL, &ahc_echo, "GET",
+                         MHD_OPTION_CONNECTION_TIMEOUT, 5, MHD_OPTION_END);
+    if (d == NULL)
+       return 16;
+
+    fd = socket (PF_INET, SOCK_STREAM, 0);
+    if (fd == MHD_INVALID_SOCKET)
+    {
+       fprintf(stderr, "socket error\n");
+       return 256;
+    }
+
+    memset(&sin, 0, sizeof(sin));
+    sin.sin_family = AF_INET;
+    sin.sin_port = htons(1081);
+    sin.sin_addr.s_addr = htonl(0x7f000001);
+
+    if (connect (fd, (struct sockaddr *)(&sin), sizeof(sin)) < 0)
+    {
+       fprintf(stderr, "connect error\n");
+       MHD_socket_close_ (fd);
+       return 512;
+    }
+
+    /*  printf("Waiting\n"); */
+    /* Let the thread get going. */
+    usleep(500000);
+
+    /* printf("Stopping daemon\n"); */
+    MHD_stop_daemon (d);
+
+    MHD_socket_close_ (fd);
+
+    /* printf("good\n"); */
+    return 0;
+}
+
+
+static int
+ahc_empty (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp ("GET", method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  *unused = NULL;
+  response = MHD_create_response_from_buffer (0,
+					      NULL,
+					      MHD_RESPMEM_PERSISTENT);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+
+static int
+curlExcessFound(CURL *c, curl_infotype type, char *data, size_t size, void *cls)
+{
+  static const char *excess_found = "Excess found";
+  const size_t str_size = strlen (excess_found);
+
+  if (CURLINFO_TEXT == type
+      && size >= str_size
+      && 0 == strncmp(excess_found, data, str_size))
+    *(int *)cls = 1;
+  return 0;
+}
+
+
+static int
+testEmptyGet (int poll_flag)
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+  int excess_found = 0;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG  | poll_flag,
+                        11081, NULL, NULL, &ahc_empty, NULL, MHD_OPTION_END);
+  if (d == NULL)
+    return 4194304;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION, &curlExcessFound);
+  curl_easy_setopt (c, CURLOPT_DEBUGDATA, &excess_found);
+  curl_easy_setopt (c, CURLOPT_VERBOSE, 1);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system!*/
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 8388608;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != 0)
+    return 16777216;
+  if (excess_found)
+    return 33554432;
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testInternalGet (0);
+  errorCount += testMultithreadedGet (0);
+  errorCount += testMultithreadedPoolGet (0);
+  errorCount += testUnknownPortGet (0);
+  errorCount += testStopRace (0);
+  errorCount += testExternalGet ();
+  errorCount += testEmptyGet (0);
+  if (MHD_YES == MHD_is_feature_supported(MHD_FEATURE_POLL))
+    {
+      errorCount += testInternalGet(MHD_USE_POLL);
+      errorCount += testMultithreadedGet(MHD_USE_POLL);
+      errorCount += testMultithreadedPoolGet(MHD_USE_POLL);
+      errorCount += testUnknownPortGet(MHD_USE_POLL);
+      errorCount += testStopRace(MHD_USE_POLL);
+      errorCount += testEmptyGet(MHD_USE_POLL);
+    }
+  if (MHD_YES == MHD_is_feature_supported(MHD_FEATURE_EPOLL))
+    {
+      errorCount += testInternalGet(MHD_USE_EPOLL_LINUX_ONLY);
+      errorCount += testMultithreadedPoolGet(MHD_USE_EPOLL_LINUX_ONLY);
+      errorCount += testUnknownPortGet(MHD_USE_EPOLL_LINUX_ONLY);
+      errorCount += testEmptyGet(MHD_USE_EPOLL_LINUX_ONLY);
+    }
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_get_chunked.c b/src/testcurl/test_get_chunked.c
new file mode 100644
index 0000000..3b8bf39
--- /dev/null
+++ b/src/testcurl/test_get_chunked.c
@@ -0,0 +1,414 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file daemontest_get_chunked.c
+ * @brief  Testcase for libmicrohttpd GET operations with chunked content encoding
+ *         TODO:
+ *         - how to test that chunking was actually used?
+ *         - use CURLOPT_HEADERFUNCTION to validate
+ *           footer was sent
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+/**
+ * MHD content reader callback that returns
+ * data in chunks.
+ */
+static ssize_t
+crc (void *cls, uint64_t pos, char *buf, size_t max)
+{
+  struct MHD_Response **responseptr = cls;
+
+  if (pos == 128 * 10)
+    {
+      MHD_add_response_header (*responseptr, "Footer", "working");
+      return MHD_CONTENT_READER_END_OF_STREAM;
+    }
+  if (max < 128)
+    abort ();                   /* should not happen in this testcase... */
+  memset (buf, 'A' + (pos / 128), 128);
+  return 128;
+}
+
+/**
+ * Dummy function that does nothing.
+ */
+static void
+crcf (void *ptr)
+{
+  free (ptr);
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size, void **ptr)
+{
+  static int aptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  struct MHD_Response **responseptr;
+  int ret;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+    {
+      /* do never respond on first call */
+      *ptr = &aptr;
+      return MHD_YES;
+    }
+  responseptr = malloc (sizeof (struct MHD_Response *));
+  if (responseptr == NULL)
+    return MHD_NO;
+  response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
+                                                1024,
+                                                &crc, responseptr, &crcf);
+  *responseptr = response;
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+static int
+validate (struct CBC cbc, int ebase)
+{
+  int i;
+  char buf[128];
+
+  if (cbc.pos != 128 * 10)
+    return ebase;
+
+  for (i = 0; i < 10; i++)
+    {
+      memset (buf, 'A' + i, 128);
+      if (0 != memcmp (buf, &cbc.buf[i * 128], 128))
+        {
+          fprintf (stderr,
+                   "Got  `%.*s'\nWant `%.*s'\n",
+                   128, buf, 128, &cbc.buf[i * 128]);
+          return ebase * 2;
+        }
+    }
+  return 0;
+}
+
+static int
+testInternalGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  return validate (cbc, 4);
+}
+
+static int
+testMultithreadedGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  return validate (cbc, 64);
+}
+
+static int
+testMultithreadedPoolGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1081, NULL, NULL, &ahc_echo, "GET",
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  return validate (cbc, 64);
+}
+
+static int
+testExternalGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        1082, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  mret = curl_multi_add_handle (multi, c);
+  if (mret != CURLM_OK)
+    {
+      curl_multi_cleanup (multi);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 1024;
+    }
+  start = time (NULL);
+  while ((time (NULL) - start < 5) && (multi != NULL))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      curl_multi_perform (multi, &running);
+      mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 2048;
+        }
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 4096;
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select (max + 1, &rs, &ws, &es, &tv);
+      curl_multi_perform (multi, &running);
+      if (running == 0)
+        {
+          msg = curl_multi_info_read (multi, &running);
+          if (msg == NULL)
+            break;
+          if (msg->msg == CURLMSG_DONE)
+            {
+              if (msg->data.result != CURLE_OK)
+                printf ("%s failed at %s:%d: `%s'\n",
+                        "curl_multi_perform",
+                        __FILE__,
+                        __LINE__, curl_easy_strerror (msg->data.result));
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              c = NULL;
+              multi = NULL;
+            }
+        }
+      MHD_run (d);
+    }
+  if (multi != NULL)
+    {
+      curl_multi_remove_handle (multi, c);
+      curl_easy_cleanup (c);
+      curl_multi_cleanup (multi);
+    }
+  MHD_stop_daemon (d);
+  return validate (cbc, 8192);
+}
+
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testInternalGet ();
+  errorCount += testMultithreadedGet ();
+  errorCount += testMultithreadedPoolGet ();
+  errorCount += testExternalGet ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_get_response_cleanup.c b/src/testcurl/test_get_response_cleanup.c
new file mode 100644
index 0000000..f881746
--- /dev/null
+++ b/src/testcurl/test_get_response_cleanup.c
@@ -0,0 +1,302 @@
+/* DO NOT CHANGE THIS LINE */
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2009 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file daemontest_get_response_cleanup.c
+ * @brief  Testcase for libmicrohttpd response cleanup
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#ifndef WINDOWS
+#include <sys/socket.h>
+#include <unistd.h>
+#endif
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+#define TESTSTR "/* DO NOT CHANGE THIS LINE */"
+
+static int oneone;
+
+static int ok;
+
+
+static pid_t
+fork_curl (const char *url)
+{
+  pid_t ret;
+
+  ret = fork();
+  if (ret != 0)
+    return ret;
+  execlp ("curl", "curl", "-s", "-N", "-o", "/dev/null", "-GET", url, NULL);
+  fprintf (stderr, 
+	   "Failed to exec curl: %s\n",
+	   strerror (errno));
+  _exit (-1);  
+}
+
+static void
+kill_curl (pid_t pid)
+{
+  int status;
+
+  //fprintf (stderr, "Killing curl\n");
+  kill (pid, SIGTERM);
+  waitpid (pid, &status, 0);
+}
+
+
+static ssize_t
+push_callback (void *cls, uint64_t pos, char *buf, size_t max)
+{
+  if (max == 0)
+    return 0;
+  buf[0] = 'd';
+  return 1;
+}
+
+static void
+push_free_callback (void *cls)
+{
+  int *ok = cls;
+
+  //fprintf (stderr, "Cleanup callback called!\n");
+  *ok = 0;
+}
+
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  //fprintf (stderr, "In CB: %s!\n", method);
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  *unused = NULL;
+  response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 
+						32 * 1024,
+						&push_callback,
+						&ok,
+						&push_free_callback);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+
+static int
+testInternalGet ()
+{
+  struct MHD_Daemon *d;
+  pid_t curl;
+
+  ok = 1;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  curl = fork_curl ("http://127.0.0.1:11080/");
+  sleep (1);
+  kill_curl (curl);
+  sleep (1);
+  // fprintf (stderr, "Stopping daemon!\n");
+  MHD_stop_daemon (d);
+  if (ok != 0)
+    return 2;
+  return 0;
+}
+
+static int
+testMultithreadedGet ()
+{
+  struct MHD_Daemon *d;
+  pid_t curl;
+
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        1081, NULL, NULL, &ahc_echo, "GET",
+			MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 2,
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  ok = 1;
+  //fprintf (stderr, "Forking cURL!\n");
+  curl = fork_curl ("http://127.0.0.1:1081/");
+  sleep (1);
+  kill_curl (curl);
+  sleep (1);
+  curl = fork_curl ("http://127.0.0.1:1081/");
+  sleep (1);
+  if (ok != 0)
+    {
+      kill_curl (curl);
+      MHD_stop_daemon (d);
+      return 64;
+    }
+  kill_curl (curl);
+  sleep (1);
+  //fprintf (stderr, "Stopping daemon!\n");
+  MHD_stop_daemon (d);
+  if (ok != 0)
+    return 32;
+
+  return 0;
+}
+
+static int
+testMultithreadedPoolGet ()
+{
+  struct MHD_Daemon *d;
+  pid_t curl;
+
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1081, NULL, NULL, &ahc_echo, "GET",
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
+  if (d == NULL)
+    return 64;
+  ok = 1;
+  curl = fork_curl ("http://127.0.0.1:1081/");
+  sleep (1);
+  kill_curl (curl);
+  sleep (1);
+  //fprintf (stderr, "Stopping daemon!\n");
+  MHD_stop_daemon (d);
+  if (ok != 0)
+    return 128;
+  return 0;
+}
+
+static int
+testExternalGet ()
+{
+  struct MHD_Daemon *d;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  time_t start;
+  struct timeval tv;
+  pid_t curl;
+
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        1082, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  curl = fork_curl ("http://127.0.0.1:1082/");
+  
+  start = time (NULL);
+  while ((time (NULL) - start < 2))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+        {
+          MHD_stop_daemon (d);
+          return 4096;
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select (max + 1, &rs, &ws, &es, &tv);
+      MHD_run (d);
+    }
+  kill_curl (curl);
+  start = time (NULL);
+  while ((time (NULL) - start < 2))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+        {
+          MHD_stop_daemon (d);
+          return 4096;
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select (max + 1, &rs, &ws, &es, &tv);
+      MHD_run (d);
+    }
+  // fprintf (stderr, "Stopping daemon!\n");
+  MHD_stop_daemon (d);
+  if (ok != 0)
+    return 1024;
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  errorCount += testInternalGet ();
+  errorCount += testMultithreadedGet ();
+  errorCount += testMultithreadedPoolGet ();
+  errorCount += testExternalGet ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_get_sendfile.c b/src/testcurl/test_get_sendfile.c
new file mode 100644
index 0000000..f0853b2
--- /dev/null
+++ b/src/testcurl/test_get_sendfile.c
@@ -0,0 +1,500 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2009 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file daemontest_get_sendfile.c
+ * @brief  Testcase for libmicrohttpd response from FD
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include "platform_interface.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#ifndef WINDOWS
+#include <sys/socket.h>
+#include <unistd.h>
+#endif
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+#define TESTSTR "This is the content of the test file we are sending using sendfile (if available)"
+
+char *sourcefile;
+
+static int oneone;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+  int fd;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  *unused = NULL;
+  fd = open (sourcefile, O_RDONLY);
+  if (fd == -1)
+    {
+      fprintf (stderr, "Failed to open `%s': %s\n",
+	       sourcefile,
+	       MHD_strerror_ (errno));
+      exit (1);
+    }
+  response = MHD_create_response_from_fd (strlen (TESTSTR), fd);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+
+static int
+testInternalGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11080/");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system!*/
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen (TESTSTR))
+    return 4;
+  if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
+    return 8;
+  return 0;
+}
+
+static int
+testMultithreadedGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen (TESTSTR))
+    return 64;
+  if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
+    return 128;
+  return 0;
+}
+
+static int
+testMultithreadedPoolGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1081, NULL, NULL, &ahc_echo, "GET",
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system!*/
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen (TESTSTR))
+    return 64;
+  if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
+    return 128;
+  return 0;
+}
+
+static int
+testExternalGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        1082, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  mret = curl_multi_add_handle (multi, c);
+  if (mret != CURLM_OK)
+    {
+      curl_multi_cleanup (multi);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 1024;
+    }
+  start = time (NULL);
+  while ((time (NULL) - start < 5) && (multi != NULL))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      curl_multi_perform (multi, &running);
+      mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 2048;
+        }
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 4096;
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select (max + 1, &rs, &ws, &es, &tv);
+      curl_multi_perform (multi, &running);
+      if (running == 0)
+        {
+          msg = curl_multi_info_read (multi, &running);
+          if (msg == NULL)
+            break;
+          if (msg->msg == CURLMSG_DONE)
+            {
+              if (msg->data.result != CURLE_OK)
+                printf ("%s failed at %s:%d: `%s'\n",
+                        "curl_multi_perform",
+                        __FILE__,
+                        __LINE__, curl_easy_strerror (msg->data.result));
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              c = NULL;
+              multi = NULL;
+            }
+        }
+      MHD_run (d);
+    }
+  if (multi != NULL)
+    {
+      curl_multi_remove_handle (multi, c);
+      curl_easy_cleanup (c);
+      curl_multi_cleanup (multi);
+    }
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen (TESTSTR))
+    return 8192;
+  if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
+    return 16384;
+  return 0;
+}
+
+static int
+testUnknownPortGet ()
+{
+  struct MHD_Daemon *d;
+  const union MHD_DaemonInfo *di;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  struct sockaddr_in addr;
+  socklen_t addr_len = sizeof(addr);
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_port = 0;
+  addr.sin_addr.s_addr = INADDR_ANY;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1, NULL, NULL, &ahc_echo, "GET",
+                        MHD_OPTION_SOCK_ADDR, &addr,
+                        MHD_OPTION_END);
+  if (d == NULL)
+    return 32768;
+
+  di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD);
+  if (di == NULL)
+    return 65536;
+
+  if (0 != getsockname(di->listen_fd, (struct sockaddr *) &addr, &addr_len))
+    return 131072;
+
+  if (addr.sin_family != AF_INET)
+    return 26214;
+
+  snprintf(buf, sizeof(buf), "http://127.0.0.1:%hu/",
+           ntohs(addr.sin_port));
+
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, buf);
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 524288;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen (TESTSTR))
+    return 1048576;
+  if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
+    return 2097152;
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+  const char *tmp;
+  FILE *f;
+
+  if ( (NULL == (tmp = getenv ("TMPDIR"))) &&
+       (NULL == (tmp = getenv ("TMP"))) &&
+       (NULL == (tmp = getenv ("TEMP"))) )
+    tmp = "/tmp";
+  sourcefile = malloc (strlen (tmp) + 32);
+  sprintf (sourcefile,
+	   "%s/%s",
+	   tmp,
+	   "test-mhd-sendfile");
+  f = fopen (sourcefile, "w");
+  if (NULL == f)
+    {
+      fprintf (stderr, "failed to write test file\n");
+      free (sourcefile);
+      return 1;
+    }
+  fwrite (TESTSTR, strlen (TESTSTR), 1, f);
+  fclose (f);
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testInternalGet ();
+  errorCount += testMultithreadedGet ();
+  errorCount += testMultithreadedPoolGet ();
+  errorCount += testExternalGet ();
+  errorCount += testUnknownPortGet ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  unlink (sourcefile);
+  free (sourcefile);
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_iplimit.c b/src/testcurl/test_iplimit.c
new file mode 100644
index 0000000..813fc8b
--- /dev/null
+++ b/src/testcurl/test_iplimit.c
@@ -0,0 +1,313 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_iplimit.c
+ * @brief  Testcase for libmicrohttpd GET operations
+ *         TODO: test parsing of query
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+static int oneone;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  *unused = NULL;
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url,
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+static int
+testMultithreadedGet ()
+{
+  struct MHD_Daemon *d;
+  char buf[2048];
+  int k;
+  unsigned int success;
+  unsigned int failure;
+
+  /* Test only valid for HTTP/1.1 (uses persistent connections) */
+  if (!oneone)
+    return 0;
+
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1081, NULL, NULL,
+                        &ahc_echo, "GET",
+                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, 2,
+                        MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+
+  for (k = 0; k < 3; ++k)
+    {
+      struct CBC cbc[3];
+      CURL *cenv[3];
+      int i;
+
+      success = 0;
+      failure = 0;
+      for (i = 0; i < 3; ++i)
+        {
+          CURL *c;
+          CURLcode errornum;
+
+          cenv[i] = c = curl_easy_init ();
+          cbc[i].buf = buf;
+          cbc[i].size = 2048;
+          cbc[i].pos = 0;
+
+          curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
+          curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+          curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc[i]);
+          curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+          curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+          curl_easy_setopt (c, CURLOPT_FORBID_REUSE, 0L);
+          curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+          curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+          // NOTE: use of CONNECTTIMEOUT without also
+          //   setting NOSIGNAL results in really weird
+          //   crashes on my system!
+          curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+          errornum = curl_easy_perform (c);
+          if (CURLE_OK == errornum)
+            success++;
+          else
+            failure++;
+        }
+
+      /* Cleanup the environments */
+      for (i = 0; i < 3; ++i)
+        curl_easy_cleanup (cenv[i]);
+      if ( (2 != success) ||
+           (1 != failure) )
+      {
+        fprintf (stderr,
+                 "Unexpected number of success (%u) or failure (%u)\n",
+                 success,
+                 failure);
+        MHD_stop_daemon (d);
+        return 32;
+      }
+
+      sleep(2);
+
+      for (i = 0; i < 2; ++i)
+        {
+          if (cbc[i].pos != strlen ("/hello_world"))
+            {
+              MHD_stop_daemon (d);
+              return 64;
+            }
+          if (0 != strncmp ("/hello_world", cbc[i].buf, strlen ("/hello_world")))
+            {
+              MHD_stop_daemon (d);
+              return 128;
+            }
+        }
+    }
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+static int
+testMultithreadedPoolGet ()
+{
+  struct MHD_Daemon *d;
+  char buf[2048];
+  int k;
+
+  /* Test only valid for HTTP/1.1 (uses persistent connections) */
+  if (!oneone)
+    return 0;
+
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1081, NULL, NULL, &ahc_echo, "GET",
+                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, 2,
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT,
+                        MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+
+  for (k = 0; k < 3; ++k)
+    {
+      struct CBC cbc[3];
+      CURL *cenv[3];
+      int i;
+
+      for (i = 0; i < 3; ++i)
+        {
+          CURL *c;
+          CURLcode errornum;
+
+          cenv[i] = c = curl_easy_init ();
+          cbc[i].buf = buf;
+          cbc[i].size = 2048;
+          cbc[i].pos = 0;
+
+          curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
+          curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+          curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc[i]);
+          curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+          curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+          curl_easy_setopt (c, CURLOPT_FORBID_REUSE, 0L);
+          curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+          curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+          // NOTE: use of CONNECTTIMEOUT without also
+          //   setting NOSIGNAL results in really weird
+          //   crashes on my system!
+          curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+          errornum = curl_easy_perform (c);
+          if ( ( (CURLE_OK != errornum) && (i <  2) ) ||
+	       ( (CURLE_OK == errornum) && (i == 2) ) )
+            {
+              int j;
+
+              /* First 2 should succeed */
+              if (i < 2)
+                fprintf (stderr,
+                         "curl_easy_perform failed: `%s'\n",
+                         curl_easy_strerror (errornum));
+
+              /* Last request should have failed */
+              else
+                fprintf (stderr,
+                         "No error on IP address over limit\n");
+
+              for (j = 0; j < i; ++j)
+                curl_easy_cleanup (cenv[j]);
+              MHD_stop_daemon (d);
+              return 32;
+            }
+        }
+
+      /* Cleanup the environments */
+      for (i = 0; i < 3; ++i)
+        curl_easy_cleanup (cenv[i]);
+
+      sleep(2);
+
+      for (i = 0; i < 2; ++i)
+        {
+          if (cbc[i].pos != strlen ("/hello_world"))
+            {
+              MHD_stop_daemon (d);
+              return 64;
+            }
+          if (0 != strncmp ("/hello_world", cbc[i].buf, strlen ("/hello_world")))
+            {
+              MHD_stop_daemon (d);
+              return 128;
+            }
+        }
+
+
+    }
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount |= testMultithreadedGet ();
+  errorCount |= testMultithreadedPoolGet ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_large_put.c b/src/testcurl/test_large_put.c
new file mode 100644
index 0000000..ec0022e
--- /dev/null
+++ b/src/testcurl/test_large_put.c
@@ -0,0 +1,479 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2008 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_large_put.c
+ * @brief  Testcase for libmicrohttpd PUT operations
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+static int oneone;
+
+/**
+ * Do not make this much larger since we will hit the
+ * MHD default buffer limit and the test code is not
+ * written for incremental upload processing...
+ * (larger values will likely cause MHD to generate
+ * an internal server error -- which would be avoided
+ * by writing the putBuffer method in a more general
+ * fashion).
+ */
+#define PUT_SIZE (256 * 1024)
+
+static char *put_buffer;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
+{
+  unsigned int *pos = ptr;
+  unsigned int wrt;
+
+  wrt = size * nmemb;
+  if (wrt > PUT_SIZE - (*pos))
+    wrt = PUT_SIZE - (*pos);
+  memcpy (stream, &put_buffer[*pos], wrt);
+  (*pos) += wrt;
+  return wrt;
+}
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  int *done = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp ("PUT", method))
+    return MHD_NO;              /* unexpected method */
+  if ((*done) == 0)
+    {
+      if (*upload_data_size != PUT_SIZE)
+        {
+#if 0
+          fprintf (stderr,
+                   "Waiting for more data (%u/%u)...\n",
+                   *upload_data_size, PUT_SIZE);
+#endif
+          return MHD_YES;       /* not yet ready */
+        }
+      if (0 == memcmp (upload_data, put_buffer, PUT_SIZE))
+        {
+          *upload_data_size = 0;
+        }
+      else
+        {
+          printf ("Invalid upload data!\n");
+          return MHD_NO;
+        }
+      *done = 1;
+      return MHD_YES;
+    }
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url, 
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+static int
+testInternalPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  CURLcode errornum;
+  char buf[2048];
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1080,
+                        NULL, NULL, &ahc_echo, &done_flag, 
+			MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (1024*1024),
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+  curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+  curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+  curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 4;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 8;
+  return 0;
+}
+
+static int
+testMultithreadedPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  CURLcode errornum;
+  char buf[2048];
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        1081,
+                        NULL, NULL, &ahc_echo, &done_flag, 
+			MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (1024*1024),
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+  curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+  curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+  curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    {
+      fprintf (stderr, "Got invalid response `%.*s'\n", (int)cbc.pos, cbc.buf);
+      return 64;
+    }
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+  return 0;
+}
+
+static int
+testMultithreadedPoolPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  CURLcode errornum;
+  char buf[2048];
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1081,
+                        NULL, NULL, &ahc_echo, &done_flag,
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT,
+			MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (1024*1024),
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+  curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+  curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+  curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    {
+      fprintf (stderr, "Got invalid response `%.*s'\n", (int)cbc.pos, cbc.buf);
+      return 64;
+    }
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+  return 0;
+}
+
+static int
+testExternalPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  char buf[2048];
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  multi = NULL;
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        1082,
+                        NULL, NULL, &ahc_echo, &done_flag,
+                        MHD_OPTION_CONNECTION_MEMORY_LIMIT,
+                        (size_t) (PUT_SIZE * 4), MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+  curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+  curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+  curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  mret = curl_multi_add_handle (multi, c);
+  if (mret != CURLM_OK)
+    {
+      curl_multi_cleanup (multi);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 1024;
+    }
+  start = time (NULL);
+  while ((time (NULL) - start < 5) && (multi != NULL))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      curl_multi_perform (multi, &running);
+      mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 2048;
+        }
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 4096;
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select (max + 1, &rs, &ws, &es, &tv);
+      curl_multi_perform (multi, &running);
+      if (running == 0)
+        {
+          msg = curl_multi_info_read (multi, &running);
+          if (msg == NULL)
+            break;
+          if (msg->msg == CURLMSG_DONE)
+            {
+              if (msg->data.result != CURLE_OK)
+                printf ("%s failed at %s:%d: `%s'\n",
+                        "curl_multi_perform",
+                        __FILE__,
+                        __LINE__, curl_easy_strerror (msg->data.result));
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              c = NULL;
+              multi = NULL;
+            }
+        }
+      MHD_run (d);
+    }
+  if (multi != NULL)
+    {
+      curl_multi_remove_handle (multi, c);
+      curl_easy_cleanup (c);
+      curl_multi_cleanup (multi);
+    }
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    {
+      fprintf (stderr, "Got invalid response `%.*s'\n", (int)cbc.pos, cbc.buf);
+      return 8192;
+    }
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 16384;
+  return 0;
+}
+
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  put_buffer = malloc (PUT_SIZE);
+  if (NULL == put_buffer) return 1;
+  memset (put_buffer, 1, PUT_SIZE);
+  errorCount += testInternalPut ();
+  errorCount += testMultithreadedPut ();
+  errorCount += testMultithreadedPoolPut ();
+  errorCount += testExternalPut ();
+  free (put_buffer);
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_long_header.c b/src/testcurl/test_long_header.c
new file mode 100644
index 0000000..e1e024b
--- /dev/null
+++ b/src/testcurl/test_long_header.c
@@ -0,0 +1,253 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_long_header.c
+ * @brief  Testcase for libmicrohttpd handling of very long headers
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+/**
+ * We will set the memory available per connection to
+ * half of this value, so the actual value does not have
+ * to be big at all...
+ */
+#define VERY_LONG (1024*10)
+
+static int oneone;
+
+static int
+apc_all (void *cls, const struct sockaddr *addr, socklen_t addrlen)
+{
+  return MHD_YES;
+}
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url,
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+static int
+testLongUrlGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  char *url;
+  long code;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
+                        1080,
+                        &apc_all,
+                        NULL,
+                        &ahc_echo,
+                        "GET",
+                        MHD_OPTION_CONNECTION_MEMORY_LIMIT,
+                        (size_t) (VERY_LONG / 2), MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  c = curl_easy_init ();
+  url = malloc (VERY_LONG);
+  if (url == NULL)
+    {
+	MHD_stop_daemon (d);
+ 	return 1;
+    }
+  memset (url, 'a', VERY_LONG);
+  url[VERY_LONG - 1] = '\0';
+  memcpy (url, "http://127.0.0.1:1080/", strlen ("http://127.0.0.1:1080/"));
+  curl_easy_setopt (c, CURLOPT_URL, url);
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK == curl_easy_perform (c))
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      free (url);
+      return 2;
+    }
+  if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code))
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      free (url);
+      return 4;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  free (url);
+  if (code != MHD_HTTP_REQUEST_URI_TOO_LONG)
+    return 8;
+  return 0;
+}
+
+
+static int
+testLongHeaderGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  char *url;
+  long code;
+  struct curl_slist *header = NULL;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
+                        1080,
+                        &apc_all,
+                        NULL,
+                        &ahc_echo,
+                        "GET",
+                        MHD_OPTION_CONNECTION_MEMORY_LIMIT,
+                        (size_t) (VERY_LONG / 2), MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  url = malloc (VERY_LONG);
+  if (url == NULL)
+     {
+	MHD_stop_daemon (d);
+	return 16;
+     }
+  memset (url, 'a', VERY_LONG);
+  url[VERY_LONG - 1] = '\0';
+  url[VERY_LONG / 2] = ':';
+  url[VERY_LONG / 2 + 1] = ' ';
+  header = curl_slist_append (header, url);
+
+  curl_easy_setopt (c, CURLOPT_HTTPHEADER, header);
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK == curl_easy_perform (c))
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      curl_slist_free_all (header);
+      free (url);
+      return 32;
+    }
+  if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code))
+    {
+      curl_slist_free_all (header);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      free (url);
+      return 64;
+    }
+  curl_slist_free_all (header);
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  free (url);
+  if (code != MHD_HTTP_REQUEST_ENTITY_TOO_LARGE)
+    return 128;
+  return 0;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testLongUrlGet ();
+  errorCount += testLongHeaderGet ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_options.c b/src/testcurl/test_options.c
new file mode 100644
index 0000000..4afe3d4
--- /dev/null
+++ b/src/testcurl/test_options.c
@@ -0,0 +1,124 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2007 Christian Grothoff
+
+ libmicrohttpd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ libmicrohttpd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libmicrohttpd; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file mhds_get_test.c
+ * @brief  Testcase for libmicrohttpd HTTPS GET operations
+ * @author Sagie Amir
+ */
+
+#include "platform.h"
+#include "microhttpd.h"
+
+#define MHD_E_MEM "Error: memory error\n"
+#define MHD_E_SERVER_INIT "Error: failed to start server\n"
+
+const int DEBUG_GNUTLS_LOG_LEVEL = 0;
+const char *test_file_name = "https_test_file";
+const char test_file_data[] = "Hello World\n";
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  return 0;
+}
+
+int
+test_wrap (char *test_name, int (*test) (void))
+{
+  int ret;
+
+  fprintf (stdout, "running test: %s ", test_name);
+  ret = test ();
+  if (ret == 0)
+    {
+      fprintf (stdout, "[pass]\n");
+    }
+  else
+    {
+      fprintf (stdout, "[fail]\n");
+    }
+  return ret;
+}
+
+
+/**
+ * Test daemon initialization with the MHD_OPTION_SOCK_ADDR option
+ */
+static int
+test_ip_addr_option ()
+{
+  struct MHD_Daemon *d;
+  struct sockaddr_in daemon_ip_addr;
+#if HAVE_INET6
+  struct sockaddr_in6 daemon_ip_addr6;
+#endif
+
+  memset (&daemon_ip_addr, 0, sizeof (struct sockaddr_in));
+  daemon_ip_addr.sin_family = AF_INET;
+  daemon_ip_addr.sin_port = htons (4233);
+  daemon_ip_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+#if HAVE_INET6
+  memset (&daemon_ip_addr6, 0, sizeof (struct sockaddr_in6));
+  daemon_ip_addr6.sin6_family = AF_INET6;
+  daemon_ip_addr6.sin6_port = htons (4233);
+  daemon_ip_addr6.sin6_addr = in6addr_loopback;
+#endif
+
+  d = MHD_start_daemon (MHD_USE_DEBUG, 4233,
+                        NULL, NULL, &ahc_echo, NULL, MHD_OPTION_SOCK_ADDR,
+                        &daemon_ip_addr, MHD_OPTION_END);
+
+  if (d == 0)
+    return -1;
+
+  MHD_stop_daemon (d);
+
+#if HAVE_INET6
+  d = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_IPv6, 4233,
+                        NULL, NULL, &ahc_echo, NULL, MHD_OPTION_SOCK_ADDR,
+                        &daemon_ip_addr6, MHD_OPTION_END);
+
+  if (d == 0)
+    return -1;
+
+  MHD_stop_daemon (d);
+#endif
+
+  return 0;
+}
+
+/* setup a temporary transfer test file */
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  errorCount += test_wrap ("ip addr option", &test_ip_addr_option);
+
+  return errorCount != 0;
+}
diff --git a/src/testcurl/test_parse_cookies.c b/src/testcurl/test_parse_cookies.c
new file mode 100644
index 0000000..7d70867
--- /dev/null
+++ b/src/testcurl/test_parse_cookies.c
@@ -0,0 +1,250 @@
+
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 3, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_parse_cookies.c
+ * @brief  Testcase for HTTP cookie parsing
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+static int oneone;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+  const char *hdr;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  *unused = NULL;
+  ret = 0;
+
+  hdr = MHD_lookup_connection_value (connection, MHD_COOKIE_KIND, "name1");
+  if ((hdr == NULL) || (0 != strcmp (hdr, "var1")))
+    abort ();
+  hdr = MHD_lookup_connection_value (connection, MHD_COOKIE_KIND, "name2");
+  if ((hdr == NULL) || (0 != strcmp (hdr, "var2")))
+    abort ();
+  hdr = MHD_lookup_connection_value (connection, MHD_COOKIE_KIND, "name3");
+  if ((hdr == NULL) || (0 != strcmp (hdr, "")))
+    abort ();
+  hdr = MHD_lookup_connection_value (connection, MHD_COOKIE_KIND, "name4");
+  if ((hdr == NULL) || (0 != strcmp (hdr, "var4 with spaces")))
+    abort ();
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url,
+					      MHD_RESPMEM_PERSISTENT);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+static int
+testExternalGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        21080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  /* note that the string below intentionally uses the
+     various ways cookies can be specified to exercise the
+     parser! Do not change! */
+  curl_easy_setopt (c, CURLOPT_COOKIE,
+                    "name1=var1; name2=var2,name3 ;name4=\"var4 with spaces\";");
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  mret = curl_multi_add_handle (multi, c);
+  if (mret != CURLM_OK)
+    {
+      curl_multi_cleanup (multi);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 1024;
+    }
+  start = time (NULL);
+  while ((time (NULL) - start < 5) && (multi != NULL))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      curl_multi_perform (multi, &running);
+      mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 2048;
+        }
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 4096;
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select (max + 1, &rs, &ws, &es, &tv);
+      curl_multi_perform (multi, &running);
+      if (running == 0)
+        {
+          msg = curl_multi_info_read (multi, &running);
+          if (msg == NULL)
+            break;
+          if (msg->msg == CURLMSG_DONE)
+            {
+              if (msg->data.result != CURLE_OK)
+                printf ("%s failed at %s:%d: `%s'\n",
+                        "curl_multi_perform",
+                        __FILE__,
+                        __LINE__, curl_easy_strerror (msg->data.result));
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              c = NULL;
+              multi = NULL;
+            }
+        }
+      MHD_run (d);
+    }
+  if (multi != NULL)
+    {
+      curl_multi_remove_handle (multi, c);
+      curl_easy_cleanup (c);
+      curl_multi_cleanup (multi);
+    }
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 8192;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 16384;
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testExternalGet ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_post.c b/src/testcurl/test_post.c
new file mode 100644
index 0000000..49bf30a
--- /dev/null
+++ b/src/testcurl/test_post.c
@@ -0,0 +1,654 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_post.c
+ * @brief  Testcase for libmicrohttpd POST operations using URL-encoding
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+#define POST_DATA "name=daniel&project=curl"
+
+static int oneone;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+
+static void
+completed_cb (void *cls,
+	      struct MHD_Connection *connection,
+	      void **con_cls,
+	      enum MHD_RequestTerminationCode toe)
+{
+  struct MHD_PostProcessor *pp = *con_cls;
+
+  if (NULL != pp)
+    MHD_destroy_post_processor (pp);   
+  *con_cls = NULL;
+}
+
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+
+/**
+ * Note that this post_iterator is not perfect
+ * in that it fails to support incremental processing.
+ * (to be fixed in the future)
+ */
+static int
+post_iterator (void *cls,
+               enum MHD_ValueKind kind,
+               const char *key,
+               const char *filename,
+               const char *content_type,
+               const char *transfer_encoding,
+               const char *value, uint64_t off, size_t size)
+{
+  int *eok = cls;
+
+  if ((0 == strcmp (key, "name")) &&
+      (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size)))
+    (*eok) |= 1;
+  if ((0 == strcmp (key, "project")) &&
+      (size == strlen ("curl")) && (0 == strncmp (value, "curl", size)))
+    (*eok) |= 2;
+  return MHD_YES;
+}
+
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int eok;
+  struct MHD_Response *response;
+  struct MHD_PostProcessor *pp;
+  int ret;
+
+  if (0 != strcmp ("POST", method))
+    {
+      printf ("METHOD: %s\n", method);
+      return MHD_NO;            /* unexpected method */
+    }
+  pp = *unused;
+  if (pp == NULL)
+    {
+      eok = 0;
+      pp = MHD_create_post_processor (connection, 1024, &post_iterator, &eok);
+      *unused = pp;
+    }
+  MHD_post_process (pp, upload_data, *upload_data_size);
+  if ((eok == 3) && (0 == *upload_data_size))
+    {
+      response = MHD_create_response_from_buffer (strlen (url),
+						  (void *) url,
+						  MHD_RESPMEM_MUST_COPY);
+      ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+      MHD_destroy_response (response);
+      MHD_destroy_post_processor (pp);
+      *unused = NULL;
+      return ret;
+    }
+  *upload_data_size = 0;
+  return MHD_YES;
+}
+
+
+static int
+testInternalPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1080, NULL, NULL, &ahc_echo, NULL, 
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,			
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
+  curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
+  curl_easy_setopt (c, CURLOPT_POST, 1L);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 4;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 8;
+  return 0;
+}
+
+static int
+testMultithreadedPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        1081, NULL, NULL, &ahc_echo, NULL, 
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,			
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
+  curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
+  curl_easy_setopt (c, CURLOPT_POST, 1L);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 64;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+  return 0;
+}
+
+static int
+testMultithreadedPoolPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1081, NULL, NULL, &ahc_echo, NULL,
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT,
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,			
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
+  curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
+  curl_easy_setopt (c, CURLOPT_POST, 1L);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 64;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+  return 0;
+}
+
+static int
+testExternalPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        1082, NULL, NULL, &ahc_echo, NULL, 
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,			
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
+  curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
+  curl_easy_setopt (c, CURLOPT_POST, 1L);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  mret = curl_multi_add_handle (multi, c);
+  if (mret != CURLM_OK)
+    {
+      curl_multi_cleanup (multi);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 1024;
+    }
+  start = time (NULL);
+  while ((time (NULL) - start < 5) && (multi != NULL))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      curl_multi_perform (multi, &running);
+      mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 2048;
+        }
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 4096;
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select (max + 1, &rs, &ws, &es, &tv);
+      curl_multi_perform (multi, &running);
+      if (running == 0)
+        {
+          msg = curl_multi_info_read (multi, &running);
+          if (msg == NULL)
+            break;
+          if (msg->msg == CURLMSG_DONE)
+            {
+              if (msg->data.result != CURLE_OK)
+                printf ("%s failed at %s:%d: `%s'\n",
+                        "curl_multi_perform",
+                        __FILE__,
+                        __LINE__, curl_easy_strerror (msg->data.result));
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              c = NULL;
+              multi = NULL;
+            }
+        }
+      MHD_run (d);
+    }
+  if (multi != NULL)
+    {
+      curl_multi_remove_handle (multi, c);
+      curl_easy_cleanup (c);
+      curl_multi_cleanup (multi);
+    }
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 8192;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 16384;
+  return 0;
+}
+
+
+static int
+ahc_cancel (void *cls,
+	    struct MHD_Connection *connection,
+	    const char *url,
+	    const char *method,
+	    const char *version,
+	    const char *upload_data, size_t *upload_data_size,
+	    void **unused)
+{
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp ("POST", method))
+    {
+      fprintf (stderr,
+	       "Unexpected method `%s'\n", method);
+      return MHD_NO; 
+    }
+
+  if (*unused == NULL)
+    {
+      *unused = "wibble";
+      /* We don't want the body. Send a 500. */
+      response = MHD_create_response_from_buffer (0, NULL, 
+						  MHD_RESPMEM_PERSISTENT);
+      ret = MHD_queue_response(connection, 500, response);
+      if (ret != MHD_YES)
+	fprintf(stderr, "Failed to queue response\n");
+      MHD_destroy_response(response);
+      return ret;
+    }
+  else
+    {
+      fprintf(stderr, 
+	      "In ahc_cancel again. This should not happen.\n");
+      return MHD_NO;
+    }
+}
+
+struct CRBC
+{
+  const char *buffer;
+  size_t size;
+  size_t pos;
+};
+
+
+static size_t 
+readBuffer(void *p, size_t size, size_t nmemb, void *opaque)
+{
+  struct CRBC *data = opaque;
+  size_t required = size * nmemb;
+  size_t left = data->size - data->pos;
+  
+  if (required > left)
+    required = left;
+  
+  memcpy(p, data->buffer + data->pos, required);
+  data->pos += required;
+  
+  return required/size;
+}
+
+
+static size_t 
+slowReadBuffer(void *p, size_t size, size_t nmemb, void *opaque)
+{
+  sleep(1);
+  return readBuffer(p, size, nmemb, opaque);
+}
+
+
+#define FLAG_EXPECT_CONTINUE 1
+#define FLAG_CHUNKED 2
+#define FLAG_FORM_DATA 4
+#define FLAG_SLOW_READ 8
+#define FLAG_COUNT 16
+
+
+static int
+testMultithreadedPostCancelPart(int flags)
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+  struct curl_slist *headers = NULL;
+  long response_code;
+  CURLcode cc;
+  int result = 0;
+  struct CRBC crbc;
+
+  /* Don't test features that aren't available with HTTP/1.0 in
+   * HTTP/1.0 mode. */
+  if (!oneone && (flags & (FLAG_EXPECT_CONTINUE | FLAG_CHUNKED)))
+    return 0;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        1081, NULL, NULL, &ahc_cancel, NULL, 
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 32768;
+
+  crbc.buffer = "Test content";
+  crbc.size = strlen(crbc.buffer);
+  crbc.pos = 0;
+  
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, (flags & FLAG_SLOW_READ) ? &slowReadBuffer : &readBuffer);
+  curl_easy_setopt (c, CURLOPT_READDATA, &crbc);
+  curl_easy_setopt (c, CURLOPT_POSTFIELDS, NULL);
+  curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, crbc.size);
+  curl_easy_setopt (c, CURLOPT_POST, 1L);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+  if (flags & FLAG_CHUNKED)
+      headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
+  if (!(flags & FLAG_FORM_DATA))
+  headers = curl_slist_append(headers, "Content-Type: application/octet-stream");
+  if (flags & FLAG_EXPECT_CONTINUE)
+      headers = curl_slist_append(headers, "Expect: 100-Continue");
+  curl_easy_setopt(c, CURLOPT_HTTPHEADER, headers);
+  
+  if (CURLE_HTTP_RETURNED_ERROR != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "flibbet curl_easy_perform didn't fail as expected: `%s' %d\n",
+               curl_easy_strerror (errornum), errornum);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      curl_slist_free_all(headers);
+      return 65536;
+    }
+  
+  if (CURLE_OK != (cc = curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &response_code)))
+    {
+      fprintf(stderr, "curl_easy_getinfo failed: '%s'\n", curl_easy_strerror(errornum));
+      result = 65536;
+    }
+  
+  if (!result && (response_code != 500))
+    {
+      fprintf(stderr, "Unexpected response code: %ld\n", response_code);
+      result = 131072;
+    }
+  
+  if (!result && (cbc.pos != 0))
+    result = 262144;
+
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  curl_slist_free_all(headers);
+  return result;
+}
+
+
+static int
+testMultithreadedPostCancel()
+{
+  int result = 0;
+  int flags;
+  for(flags = 0; flags < FLAG_COUNT; ++flags)
+    result |= testMultithreadedPostCancelPart(flags);  
+  return result;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testMultithreadedPostCancel ();
+  errorCount += testInternalPost ();
+  errorCount += testMultithreadedPost ();
+  errorCount += testMultithreadedPoolPost ();
+  errorCount += testExternalPost ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_post_loop.c b/src/testcurl/test_post_loop.c
new file mode 100644
index 0000000..fe8d25c
--- /dev/null
+++ b/src/testcurl/test_post_loop.c
@@ -0,0 +1,531 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file daemontest_post_loop.c
+ * @brief  Testcase for libmicrohttpd POST operations using URL-encoding
+ * @author Christian Grothoff (inspired by bug report #1296)
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <gauger.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+#define POST_DATA "<?xml version='1.0' ?>\n<xml>\n<data-id>1</data-id>\n</xml>\n"
+
+#define LOOPCOUNT 1000
+
+static int oneone;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **mptr)
+{
+  static int marker;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp ("POST", method))
+    {
+      printf ("METHOD: %s\n", method);
+      return MHD_NO;            /* unexpected method */
+    }
+  if ((*mptr != NULL) && (0 == *upload_data_size))
+    {
+      if (*mptr != &marker)
+        abort ();
+      response = MHD_create_response_from_buffer (2, "OK", 
+						  MHD_RESPMEM_PERSISTENT);
+      ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+      MHD_destroy_response (response);
+      *mptr = NULL;
+      return ret;
+    }
+  if (strlen (POST_DATA) != *upload_data_size)
+    return MHD_YES;
+  *upload_data_size = 0;
+  *mptr = &marker;
+  return MHD_YES;
+}
+
+
+static int
+testInternalPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+  int i;
+  char url[1024];
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1080, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  for (i = 0; i < LOOPCOUNT; i++)
+    {
+      if (99 == i % 100)
+        fprintf (stderr, ".");
+      c = curl_easy_init ();
+      cbc.pos = 0;
+      buf[0] = '\0';
+      sprintf (url, "http://127.0.0.1:1080/hw%d", i);
+      curl_easy_setopt (c, CURLOPT_URL, url);
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
+      curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
+      curl_easy_setopt (c, CURLOPT_POST, 1L);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      if (CURLE_OK != (errornum = curl_easy_perform (c)))
+        {
+          fprintf (stderr,
+                   "curl_easy_perform failed: `%s'\n",
+                   curl_easy_strerror (errornum));
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 2;
+        }
+      curl_easy_cleanup (c);
+      if ((buf[0] != 'O') || (buf[1] != 'K'))
+        {
+          MHD_stop_daemon (d);
+          return 4;
+        }
+    }
+  MHD_stop_daemon (d);
+  if (LOOPCOUNT >= 99)
+    fprintf (stderr, "\n");
+  return 0;
+}
+
+static int
+testMultithreadedPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+  int i;
+  char url[1024];
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        1081, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  for (i = 0; i < LOOPCOUNT; i++)
+    {
+      if (99 == i % 100)
+        fprintf (stderr, ".");
+      c = curl_easy_init ();
+      cbc.pos = 0;
+      buf[0] = '\0';
+      sprintf (url, "http://127.0.0.1:1081/hw%d", i);
+      curl_easy_setopt (c, CURLOPT_URL, url);
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
+      curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
+      curl_easy_setopt (c, CURLOPT_POST, 1L);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      if (CURLE_OK != (errornum = curl_easy_perform (c)))
+        {
+          fprintf (stderr,
+                   "curl_easy_perform failed: `%s'\n",
+                   curl_easy_strerror (errornum));
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 32;
+        }
+      curl_easy_cleanup (c);
+      if ((buf[0] != 'O') || (buf[1] != 'K'))
+        {
+          MHD_stop_daemon (d);
+          return 64;
+        }
+    }
+  MHD_stop_daemon (d);
+  if (LOOPCOUNT >= 99)
+    fprintf (stderr, "\n");
+  return 0;
+}
+
+static int
+testMultithreadedPoolPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+  int i;
+  char url[1024];
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1081, NULL, NULL, &ahc_echo, NULL,
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  for (i = 0; i < LOOPCOUNT; i++)
+    {
+      if (99 == i % 100)
+        fprintf (stderr, ".");
+      c = curl_easy_init ();
+      cbc.pos = 0;
+      buf[0] = '\0';
+      sprintf (url, "http://127.0.0.1:1081/hw%d", i);
+      curl_easy_setopt (c, CURLOPT_URL, url);
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
+      curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
+      curl_easy_setopt (c, CURLOPT_POST, 1L);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      if (CURLE_OK != (errornum = curl_easy_perform (c)))
+        {
+          fprintf (stderr,
+                   "curl_easy_perform failed: `%s'\n",
+                   curl_easy_strerror (errornum));
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 32;
+        }
+      curl_easy_cleanup (c);
+      if ((buf[0] != 'O') || (buf[1] != 'K'))
+        {
+          MHD_stop_daemon (d);
+          return 64;
+        }
+    }
+  MHD_stop_daemon (d);
+  if (LOOPCOUNT >= 99)
+    fprintf (stderr, "\n");
+  return 0;
+}
+
+static int
+testExternalPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+  int i;
+  unsigned long long timeout;
+  long ctimeout;
+  char url[1024];
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        1082, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  for (i = 0; i < LOOPCOUNT; i++)
+    {
+      if (99 == i % 100)
+	fprintf (stderr, ".");
+      c = curl_easy_init ();
+      cbc.pos = 0;
+      buf[0] = '\0';
+      sprintf (url, "http://127.0.0.1:1082/hw%d", i);
+      curl_easy_setopt (c, CURLOPT_URL, url);
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
+      curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
+      curl_easy_setopt (c, CURLOPT_POST, 1L);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      mret = curl_multi_add_handle (multi, c);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 1024;
+        }
+      start = time (NULL);
+      while ((time (NULL) - start < 5) && (multi != NULL))
+        {
+          max = 0;
+          FD_ZERO (&rs);
+          FD_ZERO (&ws);
+          FD_ZERO (&es);
+          while (CURLM_CALL_MULTI_PERFORM ==
+                 curl_multi_perform (multi, &running));
+          mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+          if (mret != CURLM_OK)
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              MHD_stop_daemon (d);
+              return 2048;
+            }
+          if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              MHD_stop_daemon (d);
+              return 4096;
+            }
+          if (MHD_NO == MHD_get_timeout (d, &timeout))
+            timeout = 100;      /* 100ms == INFTY -- CURL bug... */
+          if ((CURLM_OK == curl_multi_timeout (multi, &ctimeout)) &&
+              (ctimeout < timeout) && (ctimeout >= 0))
+            timeout = ctimeout;
+	  if ( (c == NULL) || (running == 0) )
+	    timeout = 0; /* terminate quickly... */
+          tv.tv_sec = timeout / 1000;
+          tv.tv_usec = (timeout % 1000) * 1000;
+          if (-1 == select (max + 1, &rs, &ws, &es, &tv))
+	    {
+	      if (EINTR == errno)
+		continue;
+	      fprintf (stderr,
+		       "select failed: %s\n",
+		       strerror (errno));
+	      break;	      
+	    }
+          while (CURLM_CALL_MULTI_PERFORM ==
+                 curl_multi_perform (multi, &running));
+          if (running == 0)
+            {
+              msg = curl_multi_info_read (multi, &running);
+              if (msg == NULL)
+                break;
+              if (msg->msg == CURLMSG_DONE)
+                {
+                  if (msg->data.result != CURLE_OK)
+                    printf ("%s failed at %s:%d: `%s'\n",
+                            "curl_multi_perform",
+                            __FILE__,
+                            __LINE__, curl_easy_strerror (msg->data.result));
+                  curl_multi_remove_handle (multi, c);
+                  curl_easy_cleanup (c);
+                  c = NULL;
+                }
+            }
+          MHD_run (d);
+        }
+      if (c != NULL)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_easy_cleanup (c);
+        }
+      if ((buf[0] != 'O') || (buf[1] != 'K'))
+        {
+          curl_multi_cleanup (multi);
+          MHD_stop_daemon (d);
+          return 8192;
+        }
+    }
+  curl_multi_cleanup (multi);
+  MHD_stop_daemon (d);
+  if (LOOPCOUNT >= 99)
+    fprintf (stderr, "\n");
+  return 0;
+}
+
+
+/**
+ * Time this round was started.
+ */
+static unsigned long long start_time;
+
+
+/**
+ * Get the current timestamp 
+ *
+ * @return current time in ms
+ */
+static unsigned long long 
+now ()
+{
+  struct timeval tv;
+
+  gettimeofday (&tv, NULL);
+  return (((unsigned long long) tv.tv_sec * 1000LL) +
+	  ((unsigned long long) tv.tv_usec / 1000LL));
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  start_time = now();
+  errorCount += testInternalPost ();
+  fprintf (stderr,
+	   oneone ? "%s: Sequential POSTs (http/1.1) %f/s\n" : "%s: Sequential POSTs (http/1.0) %f/s\n",
+	   "internal select",
+	   (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0));
+  GAUGER ("internal select",
+	  oneone ? "Sequential POSTs (http/1.1)" : "Sequential POSTs (http/1.0)",
+	  (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0),
+	  "requests/s");
+  start_time = now();
+  errorCount += testMultithreadedPost ();
+  fprintf (stderr,
+	   oneone ? "%s: Sequential POSTs (http/1.1) %f/s\n" : "%s: Sequential POSTs (http/1.0) %f/s\n",
+	   "multithreaded post",
+	   (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0));
+  GAUGER ("Multithreaded select",
+	  oneone ? "Sequential POSTs (http/1.1)" : "Sequential POSTs (http/1.0)",
+	  (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0),
+	  "requests/s");
+  start_time = now();
+  errorCount += testMultithreadedPoolPost ();
+  fprintf (stderr,
+	   oneone ? "%s: Sequential POSTs (http/1.1) %f/s\n" : "%s: Sequential POSTs (http/1.0) %f/s\n",
+	   "thread with pool",
+	   (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0));
+  GAUGER ("thread with pool",
+	  oneone ? "Sequential POSTs (http/1.1)" : "Sequential POSTs (http/1.0)",
+	  (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0),
+	  "requests/s");
+  start_time = now();
+  errorCount += testExternalPost ();
+  fprintf (stderr,
+	   oneone ? "%s: Sequential POSTs (http/1.1) %f/s\n" : "%s: Sequential POSTs (http/1.0) %f/s\n",
+	   "external select",
+	   (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0));
+  GAUGER ("external select",
+	  oneone ? "Sequential POSTs (http/1.1)" : "Sequential POSTs (http/1.0)",
+	  (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0),
+	  "requests/s");
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_postform.c b/src/testcurl/test_postform.c
new file mode 100644
index 0000000..450eb6f
--- /dev/null
+++ b/src/testcurl/test_postform.c
@@ -0,0 +1,498 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_postform.c
+ * @brief  Testcase for libmicrohttpd POST operations using multipart/postform data
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef HAVE_GCRYPT_H
+#include <gcrypt.h>
+#endif
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+static int oneone;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+
+static void
+completed_cb (void *cls,
+	      struct MHD_Connection *connection,
+	      void **con_cls,
+	      enum MHD_RequestTerminationCode toe)
+{
+  struct MHD_PostProcessor *pp = *con_cls;
+
+  if (NULL != pp)
+    MHD_destroy_post_processor (pp);
+  *con_cls = NULL;
+}
+
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+
+/**
+ * Note that this post_iterator is not perfect
+ * in that it fails to support incremental processing.
+ * (to be fixed in the future)
+ */
+static int
+post_iterator (void *cls,
+               enum MHD_ValueKind kind,
+               const char *key,
+               const char *filename,
+               const char *content_type,
+               const char *transfer_encoding,
+               const char *value, uint64_t off, size_t size)
+{
+  int *eok = cls;
+
+#if 0
+  fprintf (stderr, "PI sees %s-%.*s\n", key, size, value);
+#endif
+  if ((0 == strcmp (key, "name")) &&
+      (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size)))
+    (*eok) |= 1;
+  if ((0 == strcmp (key, "project")) &&
+      (size == strlen ("curl")) && (0 == strncmp (value, "curl", size)))
+    (*eok) |= 2;
+  return MHD_YES;
+}
+
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int eok;
+  struct MHD_Response *response;
+  struct MHD_PostProcessor *pp;
+  int ret;
+
+  if (0 != strcmp ("POST", method))
+    {
+      printf ("METHOD: %s\n", method);
+      return MHD_NO;            /* unexpected method */
+    }
+  pp = *unused;
+  if (pp == NULL)
+    {
+      eok = 0;
+      pp = MHD_create_post_processor (connection, 1024, &post_iterator, &eok);
+      if (pp == NULL)
+        abort ();
+      *unused = pp;
+    }
+  MHD_post_process (pp, upload_data, *upload_data_size);
+  if ((eok == 3) && (0 == *upload_data_size))
+    {
+      response = MHD_create_response_from_buffer (strlen (url),
+						  (void *) url,
+						  MHD_RESPMEM_MUST_COPY);
+      ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+      MHD_destroy_response (response);
+      MHD_destroy_post_processor (pp);
+      *unused = NULL;
+      return ret;
+    }
+  *upload_data_size = 0;
+  return MHD_YES;
+}
+
+static struct curl_httppost *
+make_form ()
+{
+  struct curl_httppost *post = NULL;
+  struct curl_httppost *last = NULL;
+
+  curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
+                CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END);
+  curl_formadd (&post, &last, CURLFORM_COPYNAME, "project",
+                CURLFORM_COPYCONTENTS, "curl", CURLFORM_END);
+  return post;
+}
+
+
+static int
+testInternalPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+  struct curl_httppost *pd;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1080, NULL, NULL, &ahc_echo, NULL, 
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,			
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  pd = make_form ();
+  curl_easy_setopt (c, CURLOPT_HTTPPOST, pd);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      curl_formfree (pd);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+  curl_easy_cleanup (c);
+  curl_formfree (pd);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 4;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 8;
+  return 0;
+}
+
+static int
+testMultithreadedPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+  struct curl_httppost *pd;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        1081, NULL, NULL, &ahc_echo, NULL, 
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,			
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  pd = make_form ();
+  curl_easy_setopt (c, CURLOPT_HTTPPOST, pd);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      curl_formfree (pd);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  curl_formfree (pd);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 64;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+  return 0;
+}
+
+static int
+testMultithreadedPoolPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+  struct curl_httppost *pd;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1081, NULL, NULL, &ahc_echo, NULL,
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT,
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,			
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  pd = make_form ();
+  curl_easy_setopt (c, CURLOPT_HTTPPOST, pd);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      curl_formfree (pd);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  curl_formfree (pd);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 64;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+  return 0;
+}
+
+static int
+testExternalPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+  struct curl_httppost *pd;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        1082, NULL, NULL, &ahc_echo, NULL, 
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,			
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  pd = make_form ();
+  curl_easy_setopt (c, CURLOPT_HTTPPOST, pd);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      curl_easy_cleanup (c);
+      curl_formfree (pd);
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  mret = curl_multi_add_handle (multi, c);
+  if (mret != CURLM_OK)
+    {
+      curl_multi_cleanup (multi);
+      curl_formfree (pd);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 1024;
+    }
+  start = time (NULL);
+  while ((time (NULL) - start < 5) && (multi != NULL))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      curl_multi_perform (multi, &running);
+      mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          curl_formfree (pd);
+          return 2048;
+        }
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          curl_formfree (pd);
+          MHD_stop_daemon (d);
+          return 4096;
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select (max + 1, &rs, &ws, &es, &tv);
+      curl_multi_perform (multi, &running);
+      if (running == 0)
+        {
+          msg = curl_multi_info_read (multi, &running);
+          if (msg == NULL)
+            break;
+          if (msg->msg == CURLMSG_DONE)
+            {
+              if (msg->data.result != CURLE_OK)
+                printf ("%s failed at %s:%d: `%s'\n",
+                        "curl_multi_perform",
+                        __FILE__,
+                        __LINE__, curl_easy_strerror (msg->data.result));
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              c = NULL;
+              multi = NULL;
+            }
+        }
+      MHD_run (d);
+    }
+  if (multi != NULL)
+    {
+      curl_multi_remove_handle (multi, c);
+      curl_easy_cleanup (c);
+      curl_multi_cleanup (multi);
+    }
+  curl_formfree (pd);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 8192;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 16384;
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+#ifdef HAVE_GCRYPT_H
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+#endif
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testInternalPost ();
+  errorCount += testMultithreadedPost ();
+  errorCount += testMultithreadedPoolPost ();
+  errorCount += testExternalPost ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_process_arguments.c b/src/testcurl/test_process_arguments.c
new file mode 100644
index 0000000..a3e5841
--- /dev/null
+++ b/src/testcurl/test_process_arguments.c
@@ -0,0 +1,249 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2013 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 3, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_process_arguments.c
+ * @brief  Testcase for HTTP URI arguments
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+static int oneone;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+  const char *hdr;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  *unused = NULL;
+  hdr = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "k");
+  if ((hdr == NULL) || (0 != strcmp (hdr, "v x")))
+    abort ();
+  hdr = MHD_lookup_connection_value (connection,
+                                     MHD_GET_ARGUMENT_KIND, "hash");
+  if ((hdr == NULL) || (0 != strcmp (hdr, "#foo")))
+    abort ();
+  hdr = MHD_lookup_connection_value (connection,
+                                     MHD_GET_ARGUMENT_KIND, "space");
+  if ((hdr == NULL) || (0 != strcmp (hdr, "\240bar")))
+    abort ();
+  if (3 != MHD_get_connection_values (connection,
+				      MHD_GET_ARGUMENT_KIND,
+				      NULL, NULL))
+    abort ();
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url,
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+
+static int
+testExternalGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        21080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL,
+                    "http://127.0.0.1:21080/hello+world?k=v+x&hash=%23foo&space=%A0bar");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  mret = curl_multi_add_handle (multi, c);
+  if (mret != CURLM_OK)
+    {
+      curl_multi_cleanup (multi);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 1024;
+    }
+  start = time (NULL);
+  while ((time (NULL) - start < 5) && (multi != NULL))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      curl_multi_perform (multi, &running);
+      mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 2048;
+        }
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 4096;
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select (max + 1, &rs, &ws, &es, &tv);
+      curl_multi_perform (multi, &running);
+      if (running == 0)
+        {
+          msg = curl_multi_info_read (multi, &running);
+          if (msg == NULL)
+            break;
+          if (msg->msg == CURLMSG_DONE)
+            {
+              if (msg->data.result != CURLE_OK)
+                printf ("%s failed at %s:%d: `%s'\n",
+                        "curl_multi_perform",
+                        __FILE__,
+                        __LINE__, curl_easy_strerror (msg->data.result));
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              c = NULL;
+              multi = NULL;
+            }
+        }
+      MHD_run (d);
+    }
+  if (multi != NULL)
+    {
+      curl_multi_remove_handle (multi, c);
+      curl_easy_cleanup (c);
+      curl_multi_cleanup (multi);
+    }
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello+world"))
+    return 8192;
+  if (0 != strncmp ("/hello+world", cbc.buf, strlen ("/hello+world")))
+    return 16384;
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testExternalGet ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_process_headers.c b/src/testcurl/test_process_headers.c
new file mode 100644
index 0000000..4a219fb
--- /dev/null
+++ b/src/testcurl/test_process_headers.c
@@ -0,0 +1,434 @@
+
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_process_headers.c
+ * @brief  Testcase for HTTP header access
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+static int oneone;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+kv_cb (void *cls, enum MHD_ValueKind kind, const char *key, const char *value)
+{
+  if ((0 == strcmp (key, MHD_HTTP_HEADER_HOST)) &&
+      (0 == strcmp (value, "127.0.0.1:21080")) && (kind == MHD_HEADER_KIND))
+    {
+      *((int *) cls) = 1;
+      return MHD_NO;
+    }
+  return MHD_YES;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+  const char *hdr;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  *unused = NULL;
+  ret = 0;
+  MHD_get_connection_values (connection, MHD_HEADER_KIND, &kv_cb, &ret);
+  if (ret != 1)
+    abort ();
+  hdr = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "NotFound");
+  if (hdr != NULL)
+    abort ();
+  hdr = MHD_lookup_connection_value (connection,
+                                     MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT);
+  if ((hdr == NULL) || (0 != strcmp (hdr, "*/*")))
+    abort ();
+  hdr = MHD_lookup_connection_value (connection,
+                                     MHD_HEADER_KIND, MHD_HTTP_HEADER_HOST);
+  if ((hdr == NULL) || (0 != strcmp (hdr, "127.0.0.1:21080")))
+    abort ();
+  MHD_set_connection_value (connection,
+                            MHD_HEADER_KIND, "FakeHeader", "NowPresent");
+  hdr = MHD_lookup_connection_value (connection,
+                                     MHD_HEADER_KIND, "FakeHeader");
+  if ((hdr == NULL) || (0 != strcmp (hdr, "NowPresent")))
+    abort ();
+
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url,
+					      MHD_RESPMEM_MUST_COPY);
+  MHD_add_response_header (response, "MyHeader", "MyValue");
+  hdr = MHD_get_response_header (response, "MyHeader");
+  if (0 != strcmp ("MyValue", hdr))
+    abort ();
+  MHD_add_response_header (response, "MyHeader", "MyValueToo");
+  if (MHD_YES != MHD_del_response_header (response, "MyHeader", "MyValue"))
+    abort ();
+  hdr = MHD_get_response_header (response, "MyHeader");
+  if (0 != strcmp ("MyValueToo", hdr))
+    abort ();
+  if (1 != MHD_get_response_headers (response, NULL, NULL))
+    abort ();
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+
+static int
+testInternalGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        21080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 4;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 8;
+  return 0;
+}
+
+static int
+testMultithreadedGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        21080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 64;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+  return 0;
+}
+
+static int
+testMultithreadedPoolGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        21080, NULL, NULL, &ahc_echo, "GET",
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 64;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+  return 0;
+}
+
+static int
+testExternalGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        21080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system! */
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  mret = curl_multi_add_handle (multi, c);
+  if (mret != CURLM_OK)
+    {
+      curl_multi_cleanup (multi);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 1024;
+    }
+  start = time (NULL);
+  while ((time (NULL) - start < 5) && (multi != NULL))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      curl_multi_perform (multi, &running);
+      mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 2048;
+        }
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 4096;
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select (max + 1, &rs, &ws, &es, &tv);
+      curl_multi_perform (multi, &running);
+      if (running == 0)
+        {
+          msg = curl_multi_info_read (multi, &running);
+          if (msg == NULL)
+            break;
+          if (msg->msg == CURLMSG_DONE)
+            {
+              if (msg->data.result != CURLE_OK)
+                printf ("%s failed at %s:%d: `%s'\n",
+                        "curl_multi_perform",
+                        __FILE__,
+                        __LINE__, curl_easy_strerror (msg->data.result));
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              c = NULL;
+              multi = NULL;
+            }
+        }
+      MHD_run (d);
+    }
+  if (multi != NULL)
+    {
+      curl_multi_remove_handle (multi, c);
+      curl_easy_cleanup (c);
+      curl_multi_cleanup (multi);
+    }
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 8192;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 16384;
+  return 0;
+}
+
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  errorCount += testMultithreadedGet ();
+  errorCount += testMultithreadedPoolGet ();
+  errorCount += testExternalGet ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_put.c b/src/testcurl/test_put.c
new file mode 100644
index 0000000..fe1d8db
--- /dev/null
+++ b/src/testcurl/test_put.c
@@ -0,0 +1,440 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file daemontest_put.c
+ * @brief  Testcase for libmicrohttpd PUT operations
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+static int oneone;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
+{
+  unsigned int *pos = ptr;
+  unsigned int wrt;
+
+  wrt = size * nmemb;
+  if (wrt > 8 - (*pos))
+    wrt = 8 - (*pos);
+  memcpy (stream, &("Hello123"[*pos]), wrt);
+  (*pos) += wrt;
+  return wrt;
+}
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  int *done = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp ("PUT", method))
+    return MHD_NO;              /* unexpected method */
+  if ((*done) == 0)
+    {
+      if (*upload_data_size != 8)
+        return MHD_YES;         /* not yet ready */
+      if (0 == memcmp (upload_data, "Hello123", 8))
+        {
+          *upload_data_size = 0;
+        }
+      else
+        {
+          printf ("Invalid upload data `%8s'!\n", upload_data);
+          return MHD_NO;
+        }
+      *done = 1;
+      return MHD_YES;
+    }
+  response = MHD_create_response_from_buffer (strlen (url), (void*) url,
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+static int
+testInternalPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1080,
+                        NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+  curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+  curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+  curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 4;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 8;
+  return 0;
+}
+
+static int
+testMultithreadedPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        1081,
+                        NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+  curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+  curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+  curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 64;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+
+  return 0;
+}
+
+static int
+testMultithreadedPoolPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1081,
+                        NULL, NULL, &ahc_echo, &done_flag,
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+  curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+  curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+  curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 64;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+
+  return 0;
+}
+
+
+static int
+testExternalPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+  unsigned int pos = 0;
+  int done_flag = 0;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        1082,
+                        NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+  curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+  curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+  curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  mret = curl_multi_add_handle (multi, c);
+  if (mret != CURLM_OK)
+    {
+      curl_multi_cleanup (multi);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 1024;
+    }
+  start = time (NULL);
+  while ((time (NULL) - start < 5) && (multi != NULL))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      curl_multi_perform (multi, &running);
+      mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 2048;
+        }
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 4096;
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select (max + 1, &rs, &ws, &es, &tv);
+      curl_multi_perform (multi, &running);
+      if (running == 0)
+        {
+          msg = curl_multi_info_read (multi, &running);
+          if (msg == NULL)
+            break;
+          if (msg->msg == CURLMSG_DONE)
+            {
+              if (msg->data.result != CURLE_OK)
+                printf ("%s failed at %s:%d: `%s'\n",
+                        "curl_multi_perform",
+                        __FILE__,
+                        __LINE__, curl_easy_strerror (msg->data.result));
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              c = NULL;
+              multi = NULL;
+            }
+        }
+      MHD_run (d);
+    }
+  if (multi != NULL)
+    {
+      curl_multi_remove_handle (multi, c);
+      curl_easy_cleanup (c);
+      curl_multi_cleanup (multi);
+    }
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 8192;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 16384;
+  return 0;
+}
+
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testInternalPut ();
+  errorCount += testMultithreadedPut ();
+  errorCount += testMultithreadedPoolPut ();
+  errorCount += testExternalPut ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_put_chunked.c b/src/testcurl/test_put_chunked.c
new file mode 100644
index 0000000..b2fa322
--- /dev/null
+++ b/src/testcurl/test_put_chunked.c
@@ -0,0 +1,448 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file daemontest_put_chunked.c
+ * @brief Testcase for libmicrohttpd PUT operations with chunked encoding
+ *        for the upload data
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
+{
+  unsigned int *pos = ptr;
+  unsigned int wrt;
+
+  wrt = size * nmemb;
+  if (wrt > 8 - (*pos))
+    wrt = 8 - (*pos);
+  if (wrt > 4)
+    wrt = 4;                    /* only send half at first => force multiple chunks! */
+  memcpy (stream, &("Hello123"[*pos]), wrt);
+  (*pos) += wrt;
+  return wrt;
+}
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  int *done = cls;
+  struct MHD_Response *response;
+  int ret;
+  int have;
+
+  if (0 != strcmp ("PUT", method))
+    return MHD_NO;              /* unexpected method */
+  if ((*done) < 8)
+    {
+      have = *upload_data_size;
+      if (have + *done > 8)
+        {
+          printf ("Invalid upload data `%8s'!\n", upload_data);
+          return MHD_NO;
+        }
+      if (0 == memcmp (upload_data, &"Hello123"[*done], have))
+        {
+          *done += have;
+          *upload_data_size = 0;
+        }
+      else
+        {
+          printf ("Invalid upload data `%8s'!\n", upload_data);
+          return MHD_NO;
+        }
+#if 0
+      fprintf (stderr, "Not ready for response: %u/%u\n", *done, 8);
+#endif
+      return MHD_YES;
+    }
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url,
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+static int
+testInternalPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        11080,
+                        NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+  curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+  curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+  /*
+     // by not giving the file size, we force chunking!
+     curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+   */
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 4;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 8;
+  return 0;
+}
+
+static int
+testMultithreadedPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        11081,
+                        NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+  curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+  curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+  /*
+     // by not giving the file size, we force chunking!
+     curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+   */
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 64;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+
+  return 0;
+}
+
+static int
+testMultithreadedPoolPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        11081,
+                        NULL, NULL, &ahc_echo, &done_flag,
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+  curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+  curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+  /*
+     // by not giving the file size, we force chunking!
+     curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+   */
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 64;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+
+  return 0;
+}
+
+
+static int
+testExternalPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+  unsigned int pos = 0;
+  int done_flag = 0;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        11082,
+                        NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11082/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+  curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+  curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+  /*
+     // by not giving the file size, we force chunking!
+     curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+   */
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  mret = curl_multi_add_handle (multi, c);
+  if (mret != CURLM_OK)
+    {
+      curl_multi_cleanup (multi);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 1024;
+    }
+  start = time (NULL);
+  while ((time (NULL) - start < 5) && (multi != NULL))
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      curl_multi_perform (multi, &running);
+      mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 2048;
+        }
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          MHD_stop_daemon (d);
+          return 4096;
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      select (max + 1, &rs, &ws, &es, &tv);
+      curl_multi_perform (multi, &running);
+      if (running == 0)
+        {
+          msg = curl_multi_info_read (multi, &running);
+          if (msg == NULL)
+            break;
+          if (msg->msg == CURLMSG_DONE)
+            {
+              if (msg->data.result != CURLE_OK)
+                printf ("%s failed at %s:%d: `%s'\n",
+                        "curl_multi_perform",
+                        __FILE__,
+                        __LINE__, curl_easy_strerror (msg->data.result));
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              c = NULL;
+              multi = NULL;
+            }
+        }
+      MHD_run (d);
+    }
+  if (multi != NULL)
+    {
+      curl_multi_remove_handle (multi, c);
+      curl_easy_cleanup (c);
+      curl_multi_cleanup (multi);
+    }
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 8192;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 16384;
+  return 0;
+}
+
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testInternalPut ();
+  errorCount += testMultithreadedPut ();
+  errorCount += testMultithreadedPoolPut ();
+  errorCount += testExternalPut ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_quiesce.c b/src/testcurl/test_quiesce.c
new file mode 100644
index 0000000..17adcb2
--- /dev/null
+++ b/src/testcurl/test_quiesce.c
@@ -0,0 +1,457 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2013, 2015 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+/**
+ * @file test_quiesce.c
+ * @brief  Testcase for libmicrohttpd quiescing
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include "platform_interface.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <pthread.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#include <sys/socket.h>
+#endif
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  *unused = NULL;
+  response = MHD_create_response_from_buffer (strlen (url),
+  				      (void *) url,
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+
+static void
+request_completed (void *cls, struct MHD_Connection *connection,
+		   void **con_cls, enum MHD_RequestTerminationCode code)
+{
+  int *done = (int *)cls;
+  *done = 1;
+}
+
+
+static void *
+ServeOneRequest(void *param)
+{
+  struct MHD_Daemon *d;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket fd, max;
+  time_t start;
+  struct timeval tv;
+  int done = 0;
+
+  fd = (MHD_socket) (intptr_t) param;
+
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        1082, NULL, NULL, &ahc_echo, "GET",
+                        MHD_OPTION_LISTEN_SOCKET, fd,
+                        MHD_OPTION_NOTIFY_COMPLETED, &request_completed, &done,
+                        MHD_OPTION_END);
+  if (d == NULL)
+    return "MHD_start_daemon() failed";
+
+  start = time (NULL);
+  while ((time (NULL) - start < 5) && done == 0)
+    {
+      max = 0;
+      FD_ZERO (&rs);
+      FD_ZERO (&ws);
+      FD_ZERO (&es);
+      if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+        {
+          MHD_stop_daemon (d);
+          MHD_socket_close_(fd);
+          return "MHD_get_fdset() failed";
+        }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000;
+      MHD_SYS_select_ (max + 1, &rs, &ws, &es, &tv);
+      MHD_run (d);
+    }
+  MHD_stop_daemon (d);
+  MHD_socket_close_(fd);
+  return NULL;
+}
+
+
+static CURL *
+setupCURL (void *cbc)
+{
+  CURL *c;
+
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, 150L);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system!*/
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+  return c;
+}
+
+
+static int
+testGet (int type, int pool_count, int poll_flag)
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+  MHD_socket fd;
+  pthread_t thrd;
+  const char *thrdRet;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  if (pool_count > 0) {
+    d = MHD_start_daemon (type | MHD_USE_DEBUG | MHD_USE_PIPE_FOR_SHUTDOWN | poll_flag,
+                          11080, NULL, NULL, &ahc_echo, "GET",
+                          MHD_OPTION_THREAD_POOL_SIZE, pool_count, MHD_OPTION_END);
+
+  } else {
+    d = MHD_start_daemon (type | MHD_USE_DEBUG | MHD_USE_PIPE_FOR_SHUTDOWN | poll_flag,
+                          11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  }
+  if (d == NULL)
+    return 1;
+
+  c = setupCURL(&cbc);
+
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+
+  if (cbc.pos != strlen ("/hello_world")) {
+    curl_easy_cleanup (c);
+    MHD_stop_daemon (d);
+    return 4;
+  }
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) {
+    curl_easy_cleanup (c);
+    MHD_stop_daemon (d);
+    return 8;
+  }
+
+  fd = MHD_quiesce_daemon (d);
+  if (0 != pthread_create(&thrd, NULL, &ServeOneRequest, (void*)(intptr_t) fd))
+    {
+      fprintf (stderr, "pthread_create failed\n");
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 16;
+    }
+
+  cbc.pos = 0;
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+
+  if (0 != pthread_join(thrd, (void**)&thrdRet))
+    {
+      fprintf (stderr, "pthread_join failed\n");
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 16;
+    }
+  if (NULL != thrdRet)
+    {
+      fprintf (stderr, "ServeOneRequest() error: %s\n", thrdRet);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 16;
+    }
+
+  if (cbc.pos != strlen ("/hello_world"))
+    {
+      fprintf(stderr, "%s\n", cbc.buf);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      MHD_socket_close_(fd);
+      return 4;
+    }
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    {
+      fprintf(stderr, "%s\n", cbc.buf);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      MHD_socket_close_(fd);
+      return 8;
+    }
+
+  /* at this point, the forked server quit, and the new
+   * server has quiesced, so new requests should fail
+   */
+  if (CURLE_OK == (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr, "curl_easy_perform should fail\n");
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      MHD_socket_close_(fd);
+      return 2;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  MHD_socket_close_(fd);
+
+  return 0;
+}
+
+
+static int
+testExternalGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  MHD_socket max;
+  int running;
+  struct CURLMsg *msg;
+  time_t start;
+  struct timeval tv;
+  int i;
+  MHD_socket fd;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  c = setupCURL(&cbc);
+
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  mret = curl_multi_add_handle (multi, c);
+  if (mret != CURLM_OK)
+    {
+      curl_multi_cleanup (multi);
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 1024;
+    }
+
+  for (i = 0; i < 2; i++) {
+    start = time (NULL);
+    while ((time (NULL) - start < 5) && (multi != NULL))
+      {
+        max = 0;
+        FD_ZERO (&rs);
+        FD_ZERO (&ws);
+        FD_ZERO (&es);
+        curl_multi_perform (multi, &running);
+        mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+        if (mret != CURLM_OK)
+          {
+            curl_multi_remove_handle (multi, c);
+            curl_multi_cleanup (multi);
+            curl_easy_cleanup (c);
+            MHD_stop_daemon (d);
+            return 2048;
+          }
+        if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+          {
+            curl_multi_remove_handle (multi, c);
+            curl_multi_cleanup (multi);
+            curl_easy_cleanup (c);
+            MHD_stop_daemon (d);
+            return 4096;
+          }
+        tv.tv_sec = 0;
+        tv.tv_usec = 1000;
+        select (max + 1, &rs, &ws, &es, &tv);
+        curl_multi_perform (multi, &running);
+        if (running == 0)
+          {
+            msg = curl_multi_info_read (multi, &running);
+            if (msg == NULL)
+              break;
+            if (msg->msg == CURLMSG_DONE)
+              {
+                if (i == 0 && msg->data.result != CURLE_OK)
+                  printf ("%s failed at %s:%d: `%s'\n",
+                          "curl_multi_perform",
+                          __FILE__,
+                          __LINE__, curl_easy_strerror (msg->data.result));
+                else if (i == 1 && msg->data.result == CURLE_OK)
+                  printf ("%s should have failed at %s:%d\n",
+                          "curl_multi_perform",
+                          __FILE__,
+                          __LINE__);
+                curl_multi_remove_handle (multi, c);
+                curl_multi_cleanup (multi);
+                curl_easy_cleanup (c);
+                c = NULL;
+                multi = NULL;
+              }
+          }
+        MHD_run (d);
+      }
+
+      if (i == 0) {
+        /* quiesce the daemon on the 1st iteration, so the 2nd should fail */
+        fd = MHD_quiesce_daemon(d);
+	if (MHD_INVALID_SOCKET == fd)
+	  abort ();
+	MHD_socket_close_ (fd);
+        c = setupCURL (&cbc);
+        multi = curl_multi_init ();
+        mret = curl_multi_add_handle (multi, c);
+      }
+    }
+  if (multi != NULL)
+    {
+      curl_multi_remove_handle (multi, c);
+      curl_easy_cleanup (c);
+      curl_multi_cleanup (multi);
+    }
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 8192;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 16384;
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testGet (MHD_USE_SELECT_INTERNALLY, 0, 0);
+  errorCount += testGet (MHD_USE_THREAD_PER_CONNECTION, 0, 0);
+  errorCount += testGet (MHD_USE_SELECT_INTERNALLY, CPU_COUNT, 0);
+  errorCount += testExternalGet ();
+  if (MHD_YES == MHD_is_feature_supported(MHD_FEATURE_POLL))
+    {
+      errorCount += testGet(MHD_USE_SELECT_INTERNALLY, 0, MHD_USE_POLL);
+      errorCount += testGet (MHD_USE_THREAD_PER_CONNECTION, 0, MHD_USE_POLL);
+      errorCount += testGet (MHD_USE_SELECT_INTERNALLY, CPU_COUNT, MHD_USE_POLL);
+    }
+  if (MHD_YES == MHD_is_feature_supported(MHD_FEATURE_EPOLL))
+    {
+      errorCount += testGet (MHD_USE_SELECT_INTERNALLY, 0, MHD_USE_EPOLL_LINUX_ONLY);
+      errorCount += testGet (MHD_USE_SELECT_INTERNALLY, CPU_COUNT, MHD_USE_EPOLL_LINUX_ONLY);
+    }
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_start_stop.c b/src/testcurl/test_start_stop.c
new file mode 100644
index 0000000..a708341
--- /dev/null
+++ b/src/testcurl/test_start_stop.c
@@ -0,0 +1,129 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2011 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_start_stop.c
+ * @brief  test for #1901 (start+stop)
+ * @author Christian Grothoff
+ */
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+
+#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
+#undef CPU_COUNT
+#endif
+#if !defined(CPU_COUNT)
+#define CPU_COUNT 2
+#endif
+
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  return MHD_NO;
+}
+
+
+static int
+testInternalGet (int poll_flag)
+{
+  struct MHD_Daemon *d;
+
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag,
+                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+static int
+testMultithreadedGet (int poll_flag)
+{
+  struct MHD_Daemon *d;
+
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG  | poll_flag,
+                        1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 2;
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+static int
+testMultithreadedPoolGet (int poll_flag)
+{
+  struct MHD_Daemon *d;
+
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | poll_flag,
+                        1081, NULL, NULL, &ahc_echo, "GET",
+                        MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
+  if (d == NULL)
+    return 4;
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+static int
+testExternalGet ()
+{
+  struct MHD_Daemon *d;
+
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        1082, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 8;
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  errorCount += testInternalGet (0);
+  errorCount += testMultithreadedGet (0);
+  errorCount += testMultithreadedPoolGet (0);
+  errorCount += testExternalGet ();
+  if (MHD_YES == MHD_is_feature_supported(MHD_FEATURE_POLL))
+    {
+      errorCount += testInternalGet(MHD_USE_POLL);
+      errorCount += testMultithreadedGet(MHD_USE_POLL);
+      errorCount += testMultithreadedPoolGet(MHD_USE_POLL);
+    }
+  if (MHD_YES == MHD_is_feature_supported(MHD_FEATURE_EPOLL))
+    {
+      errorCount += testInternalGet(MHD_USE_EPOLL_LINUX_ONLY);
+      errorCount += testMultithreadedPoolGet(MHD_USE_EPOLL_LINUX_ONLY);
+    }
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_termination.c b/src/testcurl/test_termination.c
new file mode 100644
index 0000000..25f9e61
--- /dev/null
+++ b/src/testcurl/test_termination.c
@@ -0,0 +1,125 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2009 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file daemontest_termination.c
+ * @brief  Testcase for libmicrohttpd tolerating client not closing immediately
+ * @author hollosig
+ */
+#define PORT	12345
+
+#include "platform.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <microhttpd.h>
+#include <unistd.h>
+#include <curl/curl.h>
+
+#ifndef __MINGW32__
+#include <sys/select.h>
+#include <sys/socket.h>
+#endif
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif
+
+static int
+connection_handler (void *cls,
+                    struct MHD_Connection *connection,
+                    const char *url,
+                    const char *method,
+                    const char *version,
+                    const char *upload_data, size_t * upload_data_size,
+                    void **ptr)
+{
+  static int i;
+
+  if (*ptr == NULL)
+    {
+      *ptr = &i;
+      return MHD_YES;
+    }
+
+  if (*upload_data_size != 0)
+    {
+      (*upload_data_size) = 0;
+      return MHD_YES;
+    }
+
+  struct MHD_Response *response =
+    MHD_create_response_from_buffer (strlen ("Response"), "Response",
+				     MHD_RESPMEM_PERSISTENT);
+  int ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+
+  return ret;
+}
+
+static size_t
+write_data (void *ptr, size_t size, size_t nmemb, void *stream)
+{
+  return size * nmemb;
+}
+
+int
+main ()
+{
+  struct MHD_Daemon *daemon;
+
+  daemon = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                             PORT,
+                             NULL,
+                             NULL, connection_handler, NULL, MHD_OPTION_END);
+
+  if (daemon == NULL)
+    {
+      fprintf (stderr, "Daemon cannot be started!");
+      exit (1);
+    }
+
+  CURL *curl = curl_easy_init ();
+  //curl_easy_setopt(curl, CURLOPT_POST, 1L);
+  char url[255];
+  sprintf (url, "http://127.0.0.1:%d", PORT);
+  curl_easy_setopt (curl, CURLOPT_URL, url);
+  curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_data);
+
+  CURLcode success = curl_easy_perform (curl);
+  if (success != 0)
+    {
+      fprintf (stderr, "CURL Error");
+      exit (1);
+    }
+  /* CPU used to go crazy here */
+  sleep (1);
+
+  curl_easy_cleanup (curl);
+  MHD_stop_daemon (daemon);
+
+  return 0;
+}
diff --git a/src/testcurl/test_timeout.c b/src/testcurl/test_timeout.c
new file mode 100644
index 0000000..006c1a8
--- /dev/null
+++ b/src/testcurl/test_timeout.c
@@ -0,0 +1,294 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_timeout.c
+ * @brief  Testcase for libmicrohttpd PUT operations
+ * @author Matthias Wachs
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+static int oneone;
+
+static int withTimeout = 1;
+
+static int withoutTimeout = 1;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+
+static void 
+termination_cb (void *cls, 
+		struct MHD_Connection *connection, 
+		void **con_cls, 
+		enum MHD_RequestTerminationCode toe)
+{
+  int *test = cls;
+
+  switch (toe)
+    {
+    case MHD_REQUEST_TERMINATED_COMPLETED_OK :
+      if (test == &withoutTimeout)
+	{
+	  withoutTimeout = 0;
+	}
+      break;
+    case MHD_REQUEST_TERMINATED_WITH_ERROR :
+    case MHD_REQUEST_TERMINATED_READ_ERROR :
+      break;
+    case MHD_REQUEST_TERMINATED_TIMEOUT_REACHED :
+      if (test == &withTimeout)
+	{
+	  withTimeout = 0;
+	}
+      break;
+    case MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN:
+      break;
+    case MHD_REQUEST_TERMINATED_CLIENT_ABORT:
+      break;
+    }
+}
+
+
+static size_t
+putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
+{
+  unsigned int *pos = ptr;
+  unsigned int wrt;
+
+  wrt = size * nmemb;
+  if (wrt > 8 - (*pos))
+	wrt = 8 - (*pos);
+  memcpy (stream, &("Hello123"[*pos]), wrt);
+  (*pos) += wrt;
+  return wrt;
+}
+
+
+static size_t
+putBuffer_fail (void *stream, size_t size, size_t nmemb, void *ptr)
+{
+  return 0;
+}
+
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  int *done = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp ("PUT", method))
+    return MHD_NO;              /* unexpected method */
+  if ((*done) == 0)
+    {
+      if (*upload_data_size != 8)
+        return MHD_YES;         /* not yet ready */
+      if (0 == memcmp (upload_data, "Hello123", 8))
+        {
+          *upload_data_size = 0;
+        }
+      else
+        {
+          printf ("Invalid upload data `%8s'!\n", upload_data);
+          return MHD_NO;
+        }
+      *done = 1;
+      return MHD_YES;
+    }
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url, 
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+static int
+testWithoutTimeout ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1080,
+                        NULL, NULL, &ahc_echo, &done_flag,
+                        MHD_OPTION_CONNECTION_TIMEOUT, 2,
+                        MHD_OPTION_NOTIFY_COMPLETED, &termination_cb, &withTimeout,
+                        MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+  curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+  curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+  curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 4;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 8;
+  return 0;
+}
+
+static int
+testWithTimeout ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  int done_flag = 0;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        1080,
+                        NULL, NULL, &ahc_echo, &done_flag,
+                        MHD_OPTION_CONNECTION_TIMEOUT, 2,
+                        MHD_OPTION_NOTIFY_COMPLETED, &termination_cb, &withoutTimeout,
+                        MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer_fail);
+  curl_easy_setopt (c, CURLOPT_READDATA, &testWithTimeout);
+  curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+  curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      if (errornum == CURLE_GOT_NOTHING)
+    	  /* mhd had the timeout */
+    	  return 0;
+      else
+    	  /* curl had the timeout first */
+    	  return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  return 64;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 16;
+  errorCount += testWithoutTimeout ();
+  errorCount += testWithTimeout ();
+  if (errorCount != 0)
+    fprintf (stderr, 
+	     "Error during test execution (code: %u)\n",
+	     errorCount);
+  curl_global_cleanup ();
+  if ((withTimeout == 0) && (withoutTimeout == 0))
+    return 0;
+  else
+    return errorCount;       /* 0 == pass */
+}
diff --git a/src/testcurl/test_urlparse.c b/src/testcurl/test_urlparse.c
new file mode 100644
index 0000000..c48bff2
--- /dev/null
+++ b/src/testcurl/test_urlparse.c
@@ -0,0 +1,191 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2009, 2011 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file daemontest_urlparse.c
+ * @brief  Testcase for libmicrohttpd url parsing
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif
+
+#ifndef WINDOWS
+#include <unistd.h>
+#include <sys/socket.h>
+#endif
+
+static int oneone;
+
+static int matches;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int 
+test_values (void *cls,
+	     enum MHD_ValueKind kind,
+	     const char *key,
+	     const char *value)
+{
+  if ( (0 == strcmp (key, "a")) &&
+       (0 == strcmp (value, "b")) )
+    matches += 1;
+  if ( (0 == strcmp (key, "c")) &&
+       (0 == strcmp (value, "")) )
+    matches += 2;
+  if ( (0 == strcmp (key, "d")) &&
+       (NULL == value) )
+    matches += 4;
+  return MHD_YES;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  MHD_get_connection_values (connection,
+			     MHD_GET_ARGUMENT_KIND,
+			     &test_values,
+			     NULL);
+  *unused = NULL;
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url,
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+
+static int
+testInternalGet (int poll_flag)
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG  | poll_flag,
+                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11080/hello_world?a=b&c=&d");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
+  if (oneone)
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  else
+    curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+  /* NOTE: use of CONNECTTIMEOUT without also
+     setting NOSIGNAL results in really weird
+     crashes on my system!*/
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 2;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 4;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 8;
+  if (matches != 7)
+    return 16;
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testInternalGet (0);
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testspdy/Makefile.am b/src/testspdy/Makefile.am
new file mode 100644
index 0000000..9d78bec
--- /dev/null
+++ b/src/testspdy/Makefile.am
@@ -0,0 +1,115 @@
+# This Makefile.am is in the public domain
+SUBDIRS  = .
+
+AM_CFLAGS = -DDATA_DIR=\"$(top_srcdir)/src/datadir/\"
+
+if USE_COVERAGE
+  AM_CFLAGS += -fprofile-arcs -ftest-coverage
+endif
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/src/include \
+ -I$(top_srcdir)/src/applicationlayer \
+ $(LIBCURL_CPPFLAGS)
+
+if !HAVE_W32
+PERF_GET_CONCURRENT=perf_get_concurrent
+endif
+
+if ENABLE_SPDY
+if HAVE_OPENSSL
+check_PROGRAMS = \
+  test_daemon_start_stop \
+  test_daemon_start_stop_many \
+  test_struct_namevalue
+
+if HAVE_SPDYLAY  
+check_PROGRAMS += \
+  test_new_connection  \
+  test_request_response \
+  test_notls \
+  test_request_response_with_callback \
+  test_misc \
+  test_session_timeout
+  #test_requests_with_assets 
+if HAVE_CURL_BINARY
+check_PROGRAMS +=  \
+  test_proxies
+endif 
+endif
+endif 
+endif 
+
+
+TESTS = $(check_PROGRAMS)
+
+
+SPDY_SOURCES= \
+ common.h common.c
+
+SPDY_LDADD=  \
+ $(top_builddir)/src/microspdy/libmicrospdy.la \
+ -lz
+
+test_daemon_start_stop_SOURCES = \
+ test_daemon_start_stop.c \
+ $(SPDY_SOURCES) 
+test_daemon_start_stop_LDADD =  $(SPDY_LDADD)
+
+test_daemon_start_stop_many_SOURCES = \
+ test_daemon_start_stop_many.c  \
+ $(SPDY_SOURCES) 
+test_daemon_start_stop_many_LDADD = $(SPDY_LDADD)
+
+test_struct_namevalue_SOURCES = \
+ test_struct_namevalue.c  \
+ $(SPDY_SOURCES) 
+test_struct_namevalue_LDADD = $(SPDY_LDADD)
+
+if HAVE_SPDYLAY
+test_new_connection_SOURCES = \
+ test_new_connection.c  \
+ $(SPDY_SOURCES) 
+test_new_connection_LDADD = $(SPDY_LDADD) \
+ -lspdylay
+
+test_request_response_SOURCES = \
+ test_request_response.c  \
+ $(SPDY_SOURCES) 
+test_request_response_LDADD = $(SPDY_LDADD) \
+ -lspdylay
+
+test_notls_SOURCES = \
+ test_notls.c  \
+ $(SPDY_SOURCES) 
+test_notls_LDADD = $(SPDY_LDADD) \
+ -lspdylay
+
+test_request_response_with_callback_SOURCES = \
+ test_request_response_with_callback.c  \
+ $(SPDY_SOURCES) 
+test_request_response_with_callback_LDADD = $(SPDY_LDADD)
+
+#test_requests_with_assets_SOURCES = \
+# test_requests_with_assets.c  \
+# $(SPDY_SOURCES) 
+#test_requests_with_assets_LDADD = $(SPDY_LDADD)
+
+test_misc_SOURCES = \
+ test_misc.c  \
+ $(SPDY_SOURCES) 
+test_misc_LDADD = $(SPDY_LDADD)
+
+test_session_timeout_SOURCES = \
+ test_session_timeout.c  \
+ $(SPDY_SOURCES) 
+test_session_timeout_LDADD = $(SPDY_LDADD)
+
+
+test_proxies_SOURCES = \
+ test_proxies.c \
+ $(SPDY_SOURCES) 
+test_proxies_LDADD = $(SPDY_LDADD)
+
+endif
diff --git a/src/testspdy/Makefile.in b/src/testspdy/Makefile.in
new file mode 100644
index 0000000..937e793
--- /dev/null
+++ b/src/testspdy/Makefile.in
@@ -0,0 +1,1402 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 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 = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+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@
+@USE_COVERAGE_TRUE@am__append_1 = -fprofile-arcs -ftest-coverage
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@check_PROGRAMS = test_daemon_start_stop$(EXEEXT) \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@	test_daemon_start_stop_many$(EXEEXT) \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@	test_struct_namevalue$(EXEEXT) \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@	$(am__EXEEXT_1) \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@	$(am__EXEEXT_2)
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@am__append_2 = \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@  test_new_connection  \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@  test_request_response \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@  test_notls \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@  test_request_response_with_callback \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@  test_misc \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@  test_session_timeout
+
+@ENABLE_SPDY_TRUE@@HAVE_CURL_BINARY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@am__append_3 = \
+@ENABLE_SPDY_TRUE@@HAVE_CURL_BINARY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@  test_proxies
+
+subdir = src/testspdy
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp $(top_srcdir)/test-driver
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_compile_flags.m4 \
+	$(top_srcdir)/m4/ax_append_flag.m4 \
+	$(top_srcdir)/m4/ax_check_compile_flag.m4 \
+	$(top_srcdir)/m4/ax_check_link_flag.m4 \
+	$(top_srcdir)/m4/ax_check_openssl.m4 \
+	$(top_srcdir)/m4/ax_count_cpus.m4 \
+	$(top_srcdir)/m4/ax_have_epoll.m4 \
+	$(top_srcdir)/m4/ax_pthread.m4 \
+	$(top_srcdir)/m4/ax_require_defined.m4 \
+	$(top_srcdir)/m4/libcurl.m4 $(top_srcdir)/m4/libgcrypt.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)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/MHD_config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@am__EXEEXT_1 = test_new_connection$(EXEEXT) \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@	test_request_response$(EXEEXT) \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@	test_notls$(EXEEXT) \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@	test_request_response_with_callback$(EXEEXT) \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@	test_misc$(EXEEXT) \
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@	test_session_timeout$(EXEEXT)
+@ENABLE_SPDY_TRUE@@HAVE_CURL_BINARY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@am__EXEEXT_2 = test_proxies$(EXEEXT)
+am__objects_1 = common.$(OBJEXT)
+am_test_daemon_start_stop_OBJECTS = test_daemon_start_stop.$(OBJEXT) \
+	$(am__objects_1)
+test_daemon_start_stop_OBJECTS = $(am_test_daemon_start_stop_OBJECTS)
+am__DEPENDENCIES_1 = $(top_builddir)/src/microspdy/libmicrospdy.la
+test_daemon_start_stop_DEPENDENCIES = $(am__DEPENDENCIES_1)
+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_test_daemon_start_stop_many_OBJECTS =  \
+	test_daemon_start_stop_many.$(OBJEXT) $(am__objects_1)
+test_daemon_start_stop_many_OBJECTS =  \
+	$(am_test_daemon_start_stop_many_OBJECTS)
+test_daemon_start_stop_many_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am__test_misc_SOURCES_DIST = test_misc.c common.h common.c
+@HAVE_SPDYLAY_TRUE@am_test_misc_OBJECTS = test_misc.$(OBJEXT) \
+@HAVE_SPDYLAY_TRUE@	$(am__objects_1)
+test_misc_OBJECTS = $(am_test_misc_OBJECTS)
+@HAVE_SPDYLAY_TRUE@test_misc_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am__test_new_connection_SOURCES_DIST = test_new_connection.c common.h \
+	common.c
+@HAVE_SPDYLAY_TRUE@am_test_new_connection_OBJECTS =  \
+@HAVE_SPDYLAY_TRUE@	test_new_connection.$(OBJEXT) \
+@HAVE_SPDYLAY_TRUE@	$(am__objects_1)
+test_new_connection_OBJECTS = $(am_test_new_connection_OBJECTS)
+@HAVE_SPDYLAY_TRUE@test_new_connection_DEPENDENCIES =  \
+@HAVE_SPDYLAY_TRUE@	$(am__DEPENDENCIES_1)
+am__test_notls_SOURCES_DIST = test_notls.c common.h common.c
+@HAVE_SPDYLAY_TRUE@am_test_notls_OBJECTS = test_notls.$(OBJEXT) \
+@HAVE_SPDYLAY_TRUE@	$(am__objects_1)
+test_notls_OBJECTS = $(am_test_notls_OBJECTS)
+@HAVE_SPDYLAY_TRUE@test_notls_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am__test_proxies_SOURCES_DIST = test_proxies.c common.h common.c
+@HAVE_SPDYLAY_TRUE@am_test_proxies_OBJECTS = test_proxies.$(OBJEXT) \
+@HAVE_SPDYLAY_TRUE@	$(am__objects_1)
+test_proxies_OBJECTS = $(am_test_proxies_OBJECTS)
+@HAVE_SPDYLAY_TRUE@test_proxies_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am__test_request_response_SOURCES_DIST = test_request_response.c \
+	common.h common.c
+@HAVE_SPDYLAY_TRUE@am_test_request_response_OBJECTS =  \
+@HAVE_SPDYLAY_TRUE@	test_request_response.$(OBJEXT) \
+@HAVE_SPDYLAY_TRUE@	$(am__objects_1)
+test_request_response_OBJECTS = $(am_test_request_response_OBJECTS)
+@HAVE_SPDYLAY_TRUE@test_request_response_DEPENDENCIES =  \
+@HAVE_SPDYLAY_TRUE@	$(am__DEPENDENCIES_1)
+am__test_request_response_with_callback_SOURCES_DIST =  \
+	test_request_response_with_callback.c common.h common.c
+@HAVE_SPDYLAY_TRUE@am_test_request_response_with_callback_OBJECTS = test_request_response_with_callback.$(OBJEXT) \
+@HAVE_SPDYLAY_TRUE@	$(am__objects_1)
+test_request_response_with_callback_OBJECTS =  \
+	$(am_test_request_response_with_callback_OBJECTS)
+@HAVE_SPDYLAY_TRUE@test_request_response_with_callback_DEPENDENCIES =  \
+@HAVE_SPDYLAY_TRUE@	$(am__DEPENDENCIES_1)
+am__test_session_timeout_SOURCES_DIST = test_session_timeout.c \
+	common.h common.c
+@HAVE_SPDYLAY_TRUE@am_test_session_timeout_OBJECTS =  \
+@HAVE_SPDYLAY_TRUE@	test_session_timeout.$(OBJEXT) \
+@HAVE_SPDYLAY_TRUE@	$(am__objects_1)
+test_session_timeout_OBJECTS = $(am_test_session_timeout_OBJECTS)
+@HAVE_SPDYLAY_TRUE@test_session_timeout_DEPENDENCIES =  \
+@HAVE_SPDYLAY_TRUE@	$(am__DEPENDENCIES_1)
+am_test_struct_namevalue_OBJECTS = test_struct_namevalue.$(OBJEXT) \
+	$(am__objects_1)
+test_struct_namevalue_OBJECTS = $(am_test_struct_namevalue_OBJECTS)
+test_struct_namevalue_DEPENDENCIES = $(am__DEPENDENCIES_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__depfiles_maybe = depfiles
+am__mv = mv -f
+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 = $(test_daemon_start_stop_SOURCES) \
+	$(test_daemon_start_stop_many_SOURCES) $(test_misc_SOURCES) \
+	$(test_new_connection_SOURCES) $(test_notls_SOURCES) \
+	$(test_proxies_SOURCES) $(test_request_response_SOURCES) \
+	$(test_request_response_with_callback_SOURCES) \
+	$(test_session_timeout_SOURCES) \
+	$(test_struct_namevalue_SOURCES)
+DIST_SOURCES = $(test_daemon_start_stop_SOURCES) \
+	$(test_daemon_start_stop_many_SOURCES) \
+	$(am__test_misc_SOURCES_DIST) \
+	$(am__test_new_connection_SOURCES_DIST) \
+	$(am__test_notls_SOURCES_DIST) \
+	$(am__test_proxies_SOURCES_DIST) \
+	$(am__test_request_response_SOURCES_DIST) \
+	$(am__test_request_response_with_callback_SOURCES_DIST) \
+	$(am__test_session_timeout_SOURCES_DIST) \
+	$(test_struct_namevalue_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-recursive dvi-recursive html-recursive info-recursive \
+	install-data-recursive install-dvi-recursive \
+	install-exec-recursive install-html-recursive \
+	install-info-recursive install-pdf-recursive \
+	install-ps-recursive install-recursive installcheck-recursive \
+	installdirs-recursive pdf-recursive ps-recursive \
+	tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	check recheck distdir
+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__tty_colors_dummy = \
+  mgn= red= grn= lgn= blu= brg= std=; \
+  am__color_tests=no
+am__tty_colors = { \
+  $(am__tty_colors_dummy); \
+  if test "X$(AM_COLOR_TESTS)" = Xno; then \
+    am__color_tests=no; \
+  elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+    am__color_tests=yes; \
+  elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+    am__color_tests=yes; \
+  fi; \
+  if test $$am__color_tests = yes; then \
+    red=''; \
+    grn=''; \
+    lgn=''; \
+    blu=''; \
+    mgn=''; \
+    brg=''; \
+    std=''; \
+  fi; \
+}
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__recheck_rx = ^[ 	]*:recheck:[ 	]*
+am__global_test_result_rx = ^[ 	]*:global-test-result:[ 	]*
+am__copy_in_global_log_rx = ^[ 	]*:copy-in-global-log:[ 	]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+  recheck = 1; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+        { \
+          if ((getline line2 < ($$0 ".log")) < 0) \
+	    recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+        { \
+          recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+        { \
+          break; \
+        } \
+    }; \
+  if (recheck) \
+    print $$0; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+  print "fatal: making $@: " msg | "cat >&2"; \
+  exit 1; \
+} \
+function rst_section(header) \
+{ \
+  print header; \
+  len = length(header); \
+  for (i = 1; i <= len; i = i + 1) \
+    printf "="; \
+  printf "\n\n"; \
+} \
+{ \
+  copy_in_global_log = 1; \
+  global_test_result = "RUN"; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+         fatal("failed to read from " $$0 ".trs"); \
+      if (line ~ /$(am__global_test_result_rx)/) \
+        { \
+          sub("$(am__global_test_result_rx)", "", line); \
+          sub("[ 	]*$$", "", line); \
+          global_test_result = line; \
+        } \
+      else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+        copy_in_global_log = 0; \
+    }; \
+  if (copy_in_global_log) \
+    { \
+      rst_section(global_test_result ": " $$0); \
+      while ((rc = (getline line < ($$0 ".log"))) != 0) \
+      { \
+        if (rc < 0) \
+          fatal("failed to read from " $$0 ".log"); \
+        print line; \
+      }; \
+      printf "\n"; \
+    }; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/   &   /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this.  Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+  --color-tests "$$am__color_tests" \
+  --enable-hard-errors "$$am__enable_hard_errors" \
+  --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test.  Creates the
+# directory for the log if needed.  Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log.  Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT.  Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup);					\
+$(am__vpath_adj_setup) $(am__vpath_adj)			\
+$(am__tty_colors);					\
+srcdir=$(srcdir); export srcdir;			\
+case "$@" in						\
+  */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;;	\
+    *) am__odir=.;; 					\
+esac;							\
+test "x$$am__odir" = x"." || test -d "$$am__odir" 	\
+  || $(MKDIR_P) "$$am__odir" || exit $$?;		\
+if test -f "./$$f"; then dir=./;			\
+elif test -f "$$f"; then dir=;				\
+else dir="$(srcdir)/"; fi;				\
+tst=$$dir$$f; log='$@'; 				\
+if test -n '$(DISABLE_HARD_ERRORS)'; then		\
+  am__enable_hard_errors=no; 				\
+else							\
+  am__enable_hard_errors=yes; 				\
+fi; 							\
+case " $(XFAIL_TESTS) " in				\
+  *[\ \	]$$f[\ \	]* | *[\ \	]$$dir$$f[\ \	]*) \
+    am__expect_failure=yes;;				\
+  *)							\
+    am__expect_failure=no;;				\
+esac; 							\
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed).  The result is saved in the shell variable
+# '$bases'.  This honors runtime overriding of TESTS and TEST_LOGS.  Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+  bases='$(TEST_LOGS)'; \
+  bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+  bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+  case '$@' in \
+    */*) \
+      case '$*' in \
+        */*) b='$*';; \
+          *) b=`echo '$@' | sed 's/\.log$$//'`; \
+       esac;; \
+    *) \
+      b='$*';; \
+  esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+	$(TEST_LOG_FLAGS)
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPU_COUNT = @CPU_COUNT@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_CPPFLAGS = @GNUTLS_CPPFLAGS@
+GNUTLS_LDFLAGS = @GNUTLS_LDFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+HAVE_CURL_BINARY = @HAVE_CURL_BINARY@
+HAVE_MAKEINFO_BINARY = @HAVE_MAKEINFO_BINARY@
+HIDDEN_VISIBILITY_CFLAGS = @HIDDEN_VISIBILITY_CFLAGS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSPDY_VERSION_AGE = @LIBSPDY_VERSION_AGE@
+LIBSPDY_VERSION_CURRENT = @LIBSPDY_VERSION_CURRENT@
+LIBSPDY_VERSION_REVISION = @LIBSPDY_VERSION_REVISION@
+LIBTOOL = @LIBTOOL@
+LIB_VERSION_AGE = @LIB_VERSION_AGE@
+LIB_VERSION_CURRENT = @LIB_VERSION_CURRENT@
+LIB_VERSION_REVISION = @LIB_VERSION_REVISION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MHD_LIBDEPS = @MHD_LIBDEPS@
+MHD_LIBDEPS_PKGCFG = @MHD_LIBDEPS_PKGCFG@
+MHD_LIB_CFLAGS = @MHD_LIB_CFLAGS@
+MHD_LIB_CPPFLAGS = @MHD_LIB_CPPFLAGS@
+MHD_LIB_LDFLAGS = @MHD_LIB_LDFLAGS@
+MHD_REQ_PRIVATE = @MHD_REQ_PRIVATE@
+MKDIR_P = @MKDIR_P@
+MS_LIB_TOOL = @MS_LIB_TOOL@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_INCLUDES = @OPENSSL_INCLUDES@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@
+PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@
+PACKAGE_VERSION_SUBMINOR = @PACKAGE_VERSION_SUBMINOR@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPDY_LIBDEPS = @SPDY_LIBDEPS@
+SPDY_LIB_CFLAGS = @SPDY_LIB_CFLAGS@
+SPDY_LIB_CPPFLAGS = @SPDY_LIB_CPPFLAGS@
+SPDY_LIB_LDFLAGS = @SPDY_LIB_LDFLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+_libcurl_config = @_libcurl_config@
+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_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@
+ax_pthread_config = @ax_pthread_config@
+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@
+have_socat = @have_socat@
+have_zzuf = @have_zzuf@
+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@
+lt_cv_objdir = @lt_cv_objdir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# This Makefile.am is in the public domain
+SUBDIRS = .
+AM_CFLAGS = -DDATA_DIR=\"$(top_srcdir)/src/datadir/\" $(am__append_1)
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/src/include \
+ -I$(top_srcdir)/src/applicationlayer \
+ $(LIBCURL_CPPFLAGS)
+
+@HAVE_W32_FALSE@PERF_GET_CONCURRENT = perf_get_concurrent
+TESTS = $(check_PROGRAMS)
+SPDY_SOURCES = \
+ common.h common.c
+
+SPDY_LDADD = \
+ $(top_builddir)/src/microspdy/libmicrospdy.la \
+ -lz
+
+test_daemon_start_stop_SOURCES = \
+ test_daemon_start_stop.c \
+ $(SPDY_SOURCES) 
+
+test_daemon_start_stop_LDADD = $(SPDY_LDADD)
+test_daemon_start_stop_many_SOURCES = \
+ test_daemon_start_stop_many.c  \
+ $(SPDY_SOURCES) 
+
+test_daemon_start_stop_many_LDADD = $(SPDY_LDADD)
+test_struct_namevalue_SOURCES = \
+ test_struct_namevalue.c  \
+ $(SPDY_SOURCES) 
+
+test_struct_namevalue_LDADD = $(SPDY_LDADD)
+@HAVE_SPDYLAY_TRUE@test_new_connection_SOURCES = \
+@HAVE_SPDYLAY_TRUE@ test_new_connection.c  \
+@HAVE_SPDYLAY_TRUE@ $(SPDY_SOURCES) 
+
+@HAVE_SPDYLAY_TRUE@test_new_connection_LDADD = $(SPDY_LDADD) \
+@HAVE_SPDYLAY_TRUE@ -lspdylay
+
+@HAVE_SPDYLAY_TRUE@test_request_response_SOURCES = \
+@HAVE_SPDYLAY_TRUE@ test_request_response.c  \
+@HAVE_SPDYLAY_TRUE@ $(SPDY_SOURCES) 
+
+@HAVE_SPDYLAY_TRUE@test_request_response_LDADD = $(SPDY_LDADD) \
+@HAVE_SPDYLAY_TRUE@ -lspdylay
+
+@HAVE_SPDYLAY_TRUE@test_notls_SOURCES = \
+@HAVE_SPDYLAY_TRUE@ test_notls.c  \
+@HAVE_SPDYLAY_TRUE@ $(SPDY_SOURCES) 
+
+@HAVE_SPDYLAY_TRUE@test_notls_LDADD = $(SPDY_LDADD) \
+@HAVE_SPDYLAY_TRUE@ -lspdylay
+
+@HAVE_SPDYLAY_TRUE@test_request_response_with_callback_SOURCES = \
+@HAVE_SPDYLAY_TRUE@ test_request_response_with_callback.c  \
+@HAVE_SPDYLAY_TRUE@ $(SPDY_SOURCES) 
+
+@HAVE_SPDYLAY_TRUE@test_request_response_with_callback_LDADD = $(SPDY_LDADD)
+
+#test_requests_with_assets_SOURCES = \
+# test_requests_with_assets.c  \
+# $(SPDY_SOURCES) 
+#test_requests_with_assets_LDADD = $(SPDY_LDADD)
+@HAVE_SPDYLAY_TRUE@test_misc_SOURCES = \
+@HAVE_SPDYLAY_TRUE@ test_misc.c  \
+@HAVE_SPDYLAY_TRUE@ $(SPDY_SOURCES) 
+
+@HAVE_SPDYLAY_TRUE@test_misc_LDADD = $(SPDY_LDADD)
+@HAVE_SPDYLAY_TRUE@test_session_timeout_SOURCES = \
+@HAVE_SPDYLAY_TRUE@ test_session_timeout.c  \
+@HAVE_SPDYLAY_TRUE@ $(SPDY_SOURCES) 
+
+@HAVE_SPDYLAY_TRUE@test_session_timeout_LDADD = $(SPDY_LDADD)
+@HAVE_SPDYLAY_TRUE@test_proxies_SOURCES = \
+@HAVE_SPDYLAY_TRUE@ test_proxies.c \
+@HAVE_SPDYLAY_TRUE@ $(SPDY_SOURCES) 
+
+@HAVE_SPDYLAY_TRUE@test_proxies_LDADD = $(SPDY_LDADD)
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
+$(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) --gnu src/testspdy/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/testspdy/Makefile
+.PRECIOUS: 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__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	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-checkPROGRAMS:
+	@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+
+test_daemon_start_stop$(EXEEXT): $(test_daemon_start_stop_OBJECTS) $(test_daemon_start_stop_DEPENDENCIES) $(EXTRA_test_daemon_start_stop_DEPENDENCIES) 
+	@rm -f test_daemon_start_stop$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_daemon_start_stop_OBJECTS) $(test_daemon_start_stop_LDADD) $(LIBS)
+
+test_daemon_start_stop_many$(EXEEXT): $(test_daemon_start_stop_many_OBJECTS) $(test_daemon_start_stop_many_DEPENDENCIES) $(EXTRA_test_daemon_start_stop_many_DEPENDENCIES) 
+	@rm -f test_daemon_start_stop_many$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_daemon_start_stop_many_OBJECTS) $(test_daemon_start_stop_many_LDADD) $(LIBS)
+
+test_misc$(EXEEXT): $(test_misc_OBJECTS) $(test_misc_DEPENDENCIES) $(EXTRA_test_misc_DEPENDENCIES) 
+	@rm -f test_misc$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_misc_OBJECTS) $(test_misc_LDADD) $(LIBS)
+
+test_new_connection$(EXEEXT): $(test_new_connection_OBJECTS) $(test_new_connection_DEPENDENCIES) $(EXTRA_test_new_connection_DEPENDENCIES) 
+	@rm -f test_new_connection$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_new_connection_OBJECTS) $(test_new_connection_LDADD) $(LIBS)
+
+test_notls$(EXEEXT): $(test_notls_OBJECTS) $(test_notls_DEPENDENCIES) $(EXTRA_test_notls_DEPENDENCIES) 
+	@rm -f test_notls$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_notls_OBJECTS) $(test_notls_LDADD) $(LIBS)
+
+test_proxies$(EXEEXT): $(test_proxies_OBJECTS) $(test_proxies_DEPENDENCIES) $(EXTRA_test_proxies_DEPENDENCIES) 
+	@rm -f test_proxies$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_proxies_OBJECTS) $(test_proxies_LDADD) $(LIBS)
+
+test_request_response$(EXEEXT): $(test_request_response_OBJECTS) $(test_request_response_DEPENDENCIES) $(EXTRA_test_request_response_DEPENDENCIES) 
+	@rm -f test_request_response$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_request_response_OBJECTS) $(test_request_response_LDADD) $(LIBS)
+
+test_request_response_with_callback$(EXEEXT): $(test_request_response_with_callback_OBJECTS) $(test_request_response_with_callback_DEPENDENCIES) $(EXTRA_test_request_response_with_callback_DEPENDENCIES) 
+	@rm -f test_request_response_with_callback$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_request_response_with_callback_OBJECTS) $(test_request_response_with_callback_LDADD) $(LIBS)
+
+test_session_timeout$(EXEEXT): $(test_session_timeout_OBJECTS) $(test_session_timeout_DEPENDENCIES) $(EXTRA_test_session_timeout_DEPENDENCIES) 
+	@rm -f test_session_timeout$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_session_timeout_OBJECTS) $(test_session_timeout_LDADD) $(LIBS)
+
+test_struct_namevalue$(EXEEXT): $(test_struct_namevalue_OBJECTS) $(test_struct_namevalue_DEPENDENCIES) $(EXTRA_test_struct_namevalue_DEPENDENCIES) 
+	@rm -f test_struct_namevalue$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_struct_namevalue_OBJECTS) $(test_struct_namevalue_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_daemon_start_stop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_daemon_start_stop_many.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_misc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_new_connection.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_notls.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_proxies.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_request_response.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_request_response_with_callback.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_session_timeout.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_struct_namevalue.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+#     (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'.  Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+	rm -f $< $@
+	$(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+	@:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+	@$(am__set_TESTS_bases); \
+	am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+	redo_bases=`for i in $$bases; do \
+	              am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+	            done`; \
+	if test -n "$$redo_bases"; then \
+	  redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+	  redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+	  if $(am__make_dryrun); then :; else \
+	    rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
+	  fi; \
+	fi; \
+	if test -n "$$am__remaking_logs"; then \
+	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+	       "recursion detected" >&2; \
+	else \
+	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+	fi; \
+	if $(am__make_dryrun); then :; else \
+	  st=0;  \
+	  errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+	  for i in $$redo_bases; do \
+	    test -f $$i.trs && test -r $$i.trs \
+	      || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+	    test -f $$i.log && test -r $$i.log \
+	      || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+	  done; \
+	  test $$st -eq 0 || exit 1; \
+	fi
+	@$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+	ws='[ 	]'; \
+	results=`for b in $$bases; do echo $$b.trs; done`; \
+	test -n "$$results" || results=/dev/null; \
+	all=`  grep "^$$ws*:test-result:"           $$results | wc -l`; \
+	pass=` grep "^$$ws*:test-result:$$ws*PASS"  $$results | wc -l`; \
+	fail=` grep "^$$ws*:test-result:$$ws*FAIL"  $$results | wc -l`; \
+	skip=` grep "^$$ws*:test-result:$$ws*SKIP"  $$results | wc -l`; \
+	xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+	xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+	error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+	if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+	  success=true; \
+	else \
+	  success=false; \
+	fi; \
+	br='==================='; br=$$br$$br$$br$$br; \
+	result_count () \
+	{ \
+	    if test x"$$1" = x"--maybe-color"; then \
+	      maybe_colorize=yes; \
+	    elif test x"$$1" = x"--no-color"; then \
+	      maybe_colorize=no; \
+	    else \
+	      echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+	    fi; \
+	    shift; \
+	    desc=$$1 count=$$2; \
+	    if test $$maybe_colorize = yes && test $$count -gt 0; then \
+	      color_start=$$3 color_end=$$std; \
+	    else \
+	      color_start= color_end=; \
+	    fi; \
+	    echo "$${color_start}# $$desc $$count$${color_end}"; \
+	}; \
+	create_testsuite_report () \
+	{ \
+	  result_count $$1 "TOTAL:" $$all   "$$brg"; \
+	  result_count $$1 "PASS: " $$pass  "$$grn"; \
+	  result_count $$1 "SKIP: " $$skip  "$$blu"; \
+	  result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+	  result_count $$1 "FAIL: " $$fail  "$$red"; \
+	  result_count $$1 "XPASS:" $$xpass "$$red"; \
+	  result_count $$1 "ERROR:" $$error "$$mgn"; \
+	}; \
+	{								\
+	  echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" |	\
+	    $(am__rst_title);						\
+	  create_testsuite_report --no-color;				\
+	  echo;								\
+	  echo ".. contents:: :depth: 2";				\
+	  echo;								\
+	  for b in $$bases; do echo $$b; done				\
+	    | $(am__create_global_log);					\
+	} >$(TEST_SUITE_LOG).tmp || exit 1;				\
+	mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG);			\
+	if $$success; then						\
+	  col="$$grn";							\
+	 else								\
+	  col="$$red";							\
+	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
+	fi;								\
+	echo "$${col}$$br$${std}"; 					\
+	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
+	echo "$${col}$$br$${std}"; 					\
+	create_testsuite_report --maybe-color;				\
+	echo "$$col$$br$$std";						\
+	if $$success; then :; else					\
+	  echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}";		\
+	  if test -n "$(PACKAGE_BUGREPORT)"; then			\
+	    echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}";	\
+	  fi;								\
+	  echo "$$col$$br$$std";					\
+	fi;								\
+	$$success || exit 1
+
+check-TESTS:
+	@list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
+	@list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+	log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+	exit $$?;
+recheck: all $(check_PROGRAMS)
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	bases=`for i in $$bases; do echo $$i; done \
+	         | $(am__list_recheck_tests)` || exit 1; \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	log_list=`echo $$log_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+	        am__force_recheck=am--force-recheck \
+	        TEST_LOGS="$$log_list"; \
+	exit $$?
+test_daemon_start_stop.log: test_daemon_start_stop$(EXEEXT)
+	@p='test_daemon_start_stop$(EXEEXT)'; \
+	b='test_daemon_start_stop'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_daemon_start_stop_many.log: test_daemon_start_stop_many$(EXEEXT)
+	@p='test_daemon_start_stop_many$(EXEEXT)'; \
+	b='test_daemon_start_stop_many'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_struct_namevalue.log: test_struct_namevalue$(EXEEXT)
+	@p='test_struct_namevalue$(EXEEXT)'; \
+	b='test_struct_namevalue'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_new_connection.log: test_new_connection$(EXEEXT)
+	@p='test_new_connection$(EXEEXT)'; \
+	b='test_new_connection'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_request_response.log: test_request_response$(EXEEXT)
+	@p='test_request_response$(EXEEXT)'; \
+	b='test_request_response'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_notls.log: test_notls$(EXEEXT)
+	@p='test_notls$(EXEEXT)'; \
+	b='test_notls'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_request_response_with_callback.log: test_request_response_with_callback$(EXEEXT)
+	@p='test_request_response_with_callback$(EXEEXT)'; \
+	b='test_request_response_with_callback'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_misc.log: test_misc$(EXEEXT)
+	@p='test_misc$(EXEEXT)'; \
+	b='test_misc'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_session_timeout.log: test_session_timeout$(EXEEXT)
+	@p='test_session_timeout$(EXEEXT)'; \
+	b='test_session_timeout'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_proxies.log: test_proxies$(EXEEXT)
+	@p='test_proxies$(EXEEXT)'; \
+	b='test_proxies'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+	@p='$<'; \
+	$(am__set_b); \
+	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@	@p='$<'; \
+@am__EXEEXT_TRUE@	$(am__set_b); \
+@am__EXEEXT_TRUE@	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@	--log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@	"$$tst" $(AM_TESTS_FD_REDIRECT)
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    $(am__make_dryrun) \
+	      || test -d "$(distdir)/$$subdir" \
+	      || $(MKDIR_P) "$(distdir)/$$subdir" \
+	      || exit 1; \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+	-test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+	-test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+	-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-checkPROGRAMS clean-generic clean-libtool \
+	mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) check-am install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+	check-TESTS check-am clean clean-checkPROGRAMS clean-generic \
+	clean-libtool 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 \
+	installdirs-am maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \
+	uninstall uninstall-am
+
+@ENABLE_SPDY_TRUE@@HAVE_OPENSSL_TRUE@@HAVE_SPDYLAY_TRUE@  #test_requests_with_assets 
+
+# 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/src/testspdy/common.c b/src/testspdy/common.c
new file mode 100644
index 0000000..e150209
--- /dev/null
+++ b/src/testspdy/common.c
@@ -0,0 +1,59 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file common.c
+ * @brief  Common functions used by the tests.
+ * @author Andrey Uzunov
+ */
+ 
+
+#include "common.h"
+#include <sys/time.h>
+
+#ifdef __GNUC__
+#define FUNC_CONSTRUCTOR(f) static void __attribute__ ((constructor)) f
+#define FUNC_DESTRUCTOR(f) static void __attribute__ ((destructor)) f
+#else  // !__GNUC__
+#define FUNC_CONSTRUCTOR(f) _MHD_EXTERN void f
+#define FUNC_DESTRUCTOR(f) _MHD_EXTERN void f
+#endif  // __GNUC__
+
+FUNC_CONSTRUCTOR (constructor)()
+{
+	printf("\nTEST START -------------------------------------------------------\n");
+}
+
+FUNC_DESTRUCTOR (destructor)()
+{
+	printf("------------------------------------------------------- TEST END\n");
+}
+
+uint16_t
+get_port(uint16_t min)
+{
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+	if(2 > min) min=2;
+	uint16_t port =  min + (tv.tv_usec+10) % ((1 << 16)  - min);
+	
+	//port = 12345;
+	printf("Port used: %i\n", port);
+	
+	return port;
+}
diff --git a/src/testspdy/common.h b/src/testspdy/common.h
new file mode 100644
index 0000000..70ee261
--- /dev/null
+++ b/src/testspdy/common.h
@@ -0,0 +1,38 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file common.h
+ * @brief  Common functions used by the tests.
+ * @author Andrey Uzunov
+ */
+ 
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#define FAIL_TEST(msg) do{\
+	printf("%i:%s\n", __LINE__, msg);\
+	fflush(stdout);\
+	exit(-10);\
+	}\
+	while(0)
+	
+uint16_t
+get_port(uint16_t min);
diff --git a/src/testspdy/test_daemon_start_stop.c b/src/testspdy/test_daemon_start_stop.c
new file mode 100644
index 0000000..d3aec6a
--- /dev/null
+++ b/src/testspdy/test_daemon_start_stop.c
@@ -0,0 +1,49 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file daemon_start_stop.c
+ * @brief  starts and stops a SPDY daemon
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include "microspdy.h"
+#include "common.h"
+
+int
+main()
+{
+	SPDY_init();
+	
+	struct SPDY_Daemon *daemon = SPDY_start_daemon(get_port(16123),
+	 DATA_DIR "cert-and-key.pem",
+	 DATA_DIR "cert-and-key.pem",
+	NULL,NULL,NULL,NULL,NULL,SPDY_DAEMON_OPTION_END);
+	
+	if(NULL==daemon){
+		printf("no daemon\n");
+		return 1;
+	}
+	
+	SPDY_stop_daemon(daemon);
+	
+	SPDY_deinit();
+	
+	return 0;
+}
diff --git a/src/testspdy/test_daemon_start_stop_many.c b/src/testspdy/test_daemon_start_stop_many.c
new file mode 100644
index 0000000..b8788de
--- /dev/null
+++ b/src/testspdy/test_daemon_start_stop_many.c
@@ -0,0 +1,66 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file daemon_start_stop_many.c
+ * @brief  starts and stops several SPDY daemons, reusing port numbers
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include "microspdy.h"
+#include "common.h"
+
+int
+main()
+{
+	int i;
+	int j;
+	int num_daemons = 3;
+	int num_tries = 5;
+	int port = get_port(15123);
+	struct SPDY_Daemon *daemon[num_daemons];
+	
+	SPDY_init();
+	
+	for(i=0; i<num_tries; ++i)
+	{
+		for(j=0;j<num_daemons;++j)
+		{
+			daemon[j] = SPDY_start_daemon(port + j,
+			DATA_DIR "cert-and-key.pem",
+			DATA_DIR "cert-and-key.pem",
+			NULL,NULL,NULL,NULL,NULL,SPDY_DAEMON_OPTION_END);
+	
+			if(NULL==daemon[j]){
+				printf("no daemon\n");
+				return 1;
+			}
+		}
+		
+		
+		for(j=0;j<num_daemons;++j)
+		{
+			SPDY_stop_daemon(daemon[j]);
+		}
+	}
+	
+	SPDY_deinit();
+	
+	return 0;
+}
diff --git a/src/testspdy/test_misc.c b/src/testspdy/test_misc.c
new file mode 100644
index 0000000..39b672b
--- /dev/null
+++ b/src/testspdy/test_misc.c
@@ -0,0 +1,289 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file misc.c
+ * @brief  tests a lot of small calls and callbacks. TODO mention what
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include "microspdy.h"
+#include "stdio.h"
+#include <sys/wait.h>
+#include "common.h"
+
+int port;
+
+#define HTML "<html><head>\
+<link href=\"main.css\" rel=\"stylesheet\" type=\"text/css\" />\
+</head><body>This is libmicrospdy</body></html>"
+
+#define CSS "body{font-size:15px}"
+
+#define SESSION_CLS "1234567890"
+
+#define REQUEST_CLS "1234567890REQ"
+
+pid_t parent;
+pid_t child;
+
+struct SPDY_Session *session1;
+struct SPDY_Session *session2;
+
+void
+killchild()
+{
+	kill(child, SIGKILL);
+	exit(1);
+}
+
+void
+killparent()
+{
+	kill(parent, SIGKILL);
+	_exit(1);
+}
+
+
+void
+create_child()
+{
+	parent = getpid();
+
+	child = fork();
+	if (-1 == child)
+	{
+		fprintf(stderr, "can't fork, error %d\n", errno);
+		exit(EXIT_FAILURE);
+	}
+
+	if (child == 0)
+	{
+		int devnull;
+		char *uri;
+		fflush(stdout);
+		devnull = open("/dev/null", O_WRONLY);
+                if (-1 == devnull)
+                  abort ();
+		if (1 != devnull)
+		{
+			dup2(devnull, 1);
+			close(devnull);
+		}
+		asprintf(&uri,"https://127.0.0.1:%i/",port);
+		execlp("spdycat", "spdycat","-anv",uri,NULL );
+		printf("execlp failed\n");
+		killparent();
+	}
+}
+
+void
+response_done_callback(void *cls,
+								struct SPDY_Response * response,
+								struct SPDY_Request * request,
+								enum SPDY_RESPONSE_RESULT status,
+						bool streamopened)
+{
+  (void)status;
+  (void)streamopened;
+
+	if(strcmp(cls,"/main.css"))
+	{
+		session1 = SPDY_get_session_for_request(request);
+		if(NULL == session1)
+		{
+			printf("SPDY_get_session_for_request failed\n");
+			killchild();
+		}
+
+		char *session_cls = strdup(SESSION_CLS);
+		SPDY_set_cls_to_session(session1,session_cls);
+	}
+	else
+	{
+		session2 = SPDY_get_session_for_request(request);
+		if(session1 != session2)
+		{
+			printf("SPDY_get_session_for_request failed the second time\n");
+			killchild();
+		}
+		printf("SPDY_get_session_for_request tested...\n");
+
+		void *session_cls = SPDY_get_cls_from_session(session2);
+		if(NULL == session_cls || strcmp(session_cls, SESSION_CLS))
+		{
+			printf("SPDY_get_cls_from_session failed\n");
+			killchild();
+		}
+		printf("SPDY_set_cls_to_session tested...\n");
+		printf("SPDY_get_cls_from_session tested...\n");
+
+		void *request_cls = SPDY_get_cls_from_request(request);
+		if(NULL == request_cls || strcmp(request_cls, REQUEST_CLS))
+		{
+			printf("SPDY_get_cls_from_request failed\n");
+			killchild();
+		}
+		printf("SPDY_set_cls_to_request tested...\n");
+		printf("SPDY_get_cls_from_request tested...\n");
+	}
+
+	SPDY_destroy_request(request);
+	SPDY_destroy_response(response);
+	free(cls);
+}
+
+void
+standard_request_handler(void *cls,
+						struct SPDY_Request * request,
+						uint8_t priority,
+                        const char *method,
+                        const char *path,
+                        const char *version,
+                        const char *host,
+                        const char *scheme,
+						struct SPDY_NameValue * headers,
+            bool more)
+{
+	(void)cls;
+	(void)request;
+	(void)priority;
+	(void)host;
+	(void)scheme;
+	(void)headers;
+	(void)method;
+	(void)version;
+	(void)more;
+
+	struct SPDY_Response *response=NULL;
+	char *cls_path = strdup(path);
+
+	if(strcmp(path,"/main.css")==0)
+	{
+		char *request_cls = strdup(REQUEST_CLS);
+		SPDY_set_cls_to_request(request,request_cls);
+		response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,CSS,strlen(CSS));
+	}
+	else
+	{
+		response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,HTML,strlen(HTML));
+	}
+
+	if(NULL==response){
+		fprintf(stdout,"no response obj\n");
+		killchild();
+	}
+
+	if(SPDY_queue_response(request,response,true,false,&response_done_callback,cls_path)!=SPDY_YES)
+	{
+		fprintf(stdout,"queue\n");
+		killchild();
+	}
+}
+
+int
+parentproc()
+{
+	int childstatus;
+	unsigned long long timeoutlong=0;
+	struct timeval timeout;
+	int ret;
+	fd_set read_fd_set;
+	fd_set write_fd_set;
+	fd_set except_fd_set;
+	int maxfd = -1;
+	struct SPDY_Daemon *daemon;
+
+	daemon = SPDY_start_daemon(port,
+								DATA_DIR "cert-and-key.pem",
+								DATA_DIR "cert-and-key.pem",
+								NULL,
+								NULL,
+								&standard_request_handler,
+								NULL,
+								NULL,
+								SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
+								1800,
+								SPDY_DAEMON_OPTION_END);
+
+	if(NULL==daemon){
+		printf("no daemon\n");
+		return 1;
+	}
+
+	create_child();
+
+	do
+	{
+		FD_ZERO(&read_fd_set);
+		FD_ZERO(&write_fd_set);
+		FD_ZERO(&except_fd_set);
+
+		ret = SPDY_get_timeout(daemon, &timeoutlong);
+		if(SPDY_NO == ret || timeoutlong > 1000)
+		{
+			timeout.tv_sec = 1;
+      timeout.tv_usec = 0;
+		}
+		else
+		{
+			timeout.tv_sec = timeoutlong / 1000;
+			timeout.tv_usec = (timeoutlong % 1000) * 1000;
+		}
+
+		maxfd = SPDY_get_fdset (daemon,
+								&read_fd_set,
+								&write_fd_set,
+								&except_fd_set);
+
+		ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
+
+		switch(ret) {
+			case -1:
+				printf("select error: %i\n", errno);
+				break;
+			case 0:
+
+				break;
+			default:
+				SPDY_run(daemon);
+
+			break;
+		}
+	}
+	while(waitpid(child,&childstatus,WNOHANG) != child);
+
+	SPDY_stop_daemon(daemon);
+
+	return WEXITSTATUS(childstatus);
+}
+
+
+int
+main()
+{
+	port = get_port(13123);
+	SPDY_init();
+
+	int ret = parentproc();
+
+	SPDY_deinit();
+
+	return ret;
+}
diff --git a/src/testspdy/test_new_connection.c b/src/testspdy/test_new_connection.c
new file mode 100644
index 0000000..b848572
--- /dev/null
+++ b/src/testspdy/test_new_connection.c
@@ -0,0 +1,1009 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file request_response.c
+ * @brief  tests new connection callback. spdycli.c
+ * 			code is reused here
+ * @author Andrey Uzunov
+ * @author Tatsuhiro Tsujikawa
+ */
+
+//TODO child exits with ret val 1 sometimes
+
+#include "platform.h"
+#include "microspdy.h"
+#include <sys/wait.h>
+#include "common.h"
+
+#define RESPONSE_BODY "<html><body><b>Hi, this is libmicrospdy!</b></body></html>"
+
+#define CLS "anything"
+
+int port;
+int loop = 1;
+
+pid_t parent;
+pid_t child;
+
+int
+spdylay_printf(const char *format, ...)
+{
+  (void)format;
+
+	return 0;
+}
+
+int
+spdylay_fprintf(FILE *stream, const char *format, ...)
+{
+  (void)stream;
+  (void)format;
+
+	return 0;
+}
+
+void
+killchild(int pid, char *message)
+{
+	printf("%s\n",message);
+	kill(pid, SIGKILL);
+	exit(1);
+}
+
+void
+killparent(int pid, char *message)
+{
+	printf("%s\n",message);
+	kill(pid, SIGKILL);
+	_exit(2);
+}
+
+
+/*****
+ * start of code needed to utilize spdylay
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <spdylay/spdylay.h>
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+enum {
+  IO_NONE,
+  WANT_READ,
+  WANT_WRITE
+};
+
+struct Connection {
+  SSL *ssl;
+  spdylay_session *session;
+  /* WANT_READ if SSL connection needs more input; or WANT_WRITE if it
+     needs more output; or IO_NONE. This is necessary because SSL/TLS
+     re-negotiation is possible at any time. Spdylay API offers
+     similar functions like spdylay_session_want_read() and
+     spdylay_session_want_write() but they do not take into account
+     SSL connection. */
+  int want_io;
+};
+
+struct Request {
+  char *host;
+  uint16_t port;
+  /* In this program, path contains query component as well. */
+  char *path;
+  /* This is the concatenation of host and port with ":" in
+     between. */
+  char *hostport;
+  /* Stream ID for this request. */
+  int32_t stream_id;
+  /* The gzip stream inflater for the compressed response. */
+  spdylay_gzip *inflater;
+};
+
+struct URI {
+  const char *host;
+  size_t hostlen;
+  uint16_t port;
+  /* In this program, path contains query component as well. */
+  const char *path;
+  size_t pathlen;
+  const char *hostport;
+  size_t hostportlen;
+};
+
+/*
+ * Returns copy of string |s| with the length |len|. The returned
+ * string is NULL-terminated.
+ */
+static char* strcopy(const char *s, size_t len)
+{
+  char *dst;
+  dst = malloc(len+1);
+  if (NULL == dst)
+    abort ();
+  memcpy(dst, s, len);
+  dst[len] = '\0';
+  return dst;
+}
+
+/*
+ * Prints error message |msg| and exit.
+ */
+static void die(const char *msg)
+{
+  fprintf(stderr, "FATAL: %s\n", msg);
+  exit(EXIT_FAILURE);
+}
+
+/*
+ * Prints error containing the function name |func| and message |msg|
+ * and exit.
+ */
+static void dief(const char *func, const char *msg)
+{
+  fprintf(stderr, "FATAL: %s: %s\n", func, msg);
+  exit(EXIT_FAILURE);
+}
+
+/*
+ * Prints error containing the function name |func| and error code
+ * |error_code| and exit.
+ */
+static void diec(const char *func, int error_code)
+{
+  fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
+          spdylay_strerror(error_code));
+  exit(EXIT_FAILURE);
+}
+
+/*
+ * Check response is content-encoding: gzip. We need this because SPDY
+ * client is required to support gzip.
+ */
+static void check_gzip(struct Request *req, char **nv)
+{
+  int gzip = 0;
+  size_t i;
+  for(i = 0; nv[i]; i += 2) {
+    if(strcmp("content-encoding", nv[i]) == 0) {
+      gzip = strcmp("gzip", nv[i+1]) == 0;
+      break;
+    }
+  }
+  if(gzip) {
+    int rv;
+    if(req->inflater) {
+      return;
+    }
+    rv = spdylay_gzip_inflate_new(&req->inflater);
+    if(rv != 0) {
+      die("Can't allocate inflate stream.");
+    }
+  }
+}
+
+/*
+ * The implementation of spdylay_send_callback type. Here we write
+ * |data| with size |length| to the network and return the number of
+ * bytes actually written. See the documentation of
+ * spdylay_send_callback for the details.
+ */
+static ssize_t send_callback(spdylay_session *session,
+                             const uint8_t *data, size_t length, int flags,
+                             void *user_data)
+{
+  (void)session;
+  (void)flags;
+
+  struct Connection *connection;
+  ssize_t rv;
+  connection = (struct Connection*)user_data;
+  connection->want_io = IO_NONE;
+  ERR_clear_error();
+  rv = SSL_write(connection->ssl, data, length);
+  if(rv < 0) {
+    int err = SSL_get_error(connection->ssl, rv);
+    if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
+      connection->want_io = (err == SSL_ERROR_WANT_READ ?
+                             WANT_READ : WANT_WRITE);
+      rv = SPDYLAY_ERR_WOULDBLOCK;
+    } else {
+      rv = SPDYLAY_ERR_CALLBACK_FAILURE;
+    }
+  }
+  return rv;
+}
+
+/*
+ * The implementation of spdylay_recv_callback type. Here we read data
+ * from the network and write them in |buf|. The capacity of |buf| is
+ * |length| bytes. Returns the number of bytes stored in |buf|. See
+ * the documentation of spdylay_recv_callback for the details.
+ */
+static ssize_t recv_callback(spdylay_session *session,
+                             uint8_t *buf, size_t length, int flags,
+                             void *user_data)
+{
+  (void)session;
+  (void)flags;
+
+  struct Connection *connection;
+  ssize_t rv;
+  connection = (struct Connection*)user_data;
+  connection->want_io = IO_NONE;
+  ERR_clear_error();
+  rv = SSL_read(connection->ssl, buf, length);
+  if(rv < 0) {
+    int err = SSL_get_error(connection->ssl, rv);
+    if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
+      connection->want_io = (err == SSL_ERROR_WANT_READ ?
+                             WANT_READ : WANT_WRITE);
+      rv = SPDYLAY_ERR_WOULDBLOCK;
+    } else {
+      rv = SPDYLAY_ERR_CALLBACK_FAILURE;
+    }
+  } else if(rv == 0) {
+    rv = SPDYLAY_ERR_EOF;
+  }
+  return rv;
+}
+
+/*
+ * The implementation of spdylay_before_ctrl_send_callback type.  We
+ * use this function to get stream ID of the request. This is because
+ * stream ID is not known when we submit the request
+ * (spdylay_submit_request).
+ */
+static void before_ctrl_send_callback(spdylay_session *session,
+                                      spdylay_frame_type type,
+                                      spdylay_frame *frame,
+                                      void *user_data)
+{
+  (void)user_data;
+
+  if(type == SPDYLAY_SYN_STREAM) {
+    struct Request *req;
+    int stream_id = frame->syn_stream.stream_id;
+    req = spdylay_session_get_stream_user_data(session, stream_id);
+    if(req && req->stream_id == -1) {
+      req->stream_id = stream_id;
+      spdylay_printf("[INFO] Stream ID = %d\n", stream_id);
+    }
+  }
+}
+
+static void on_ctrl_send_callback(spdylay_session *session,
+                                  spdylay_frame_type type,
+                                  spdylay_frame *frame, void *user_data)
+{
+  (void)user_data;
+
+  char **nv;
+  const char *name = NULL;
+  int32_t stream_id;
+  size_t i;
+  switch(type) {
+  case SPDYLAY_SYN_STREAM:
+    nv = frame->syn_stream.nv;
+    name = "SYN_STREAM";
+    stream_id = frame->syn_stream.stream_id;
+    break;
+  default:
+    break;
+  }
+  if(name && spdylay_session_get_stream_user_data(session, stream_id)) {
+    spdylay_printf("[INFO] C ----------------------------> S (%s)\n", name);
+    for(i = 0; nv[i]; i += 2) {
+      spdylay_printf("       %s: %s\n", nv[i], nv[i+1]);
+    }
+  }
+}
+
+static void on_ctrl_recv_callback(spdylay_session *session,
+                                  spdylay_frame_type type,
+                                  spdylay_frame *frame, void *user_data)
+{
+  (void)user_data;
+
+  struct Request *req;
+  char **nv;
+  const char *name = NULL;
+  int32_t stream_id;
+  size_t i;
+  switch(type) {
+  case SPDYLAY_SYN_REPLY:
+    nv = frame->syn_reply.nv;
+    name = "SYN_REPLY";
+    stream_id = frame->syn_reply.stream_id;
+    break;
+  case SPDYLAY_HEADERS:
+    nv = frame->headers.nv;
+    name = "HEADERS";
+    stream_id = frame->headers.stream_id;
+    break;
+  default:
+    break;
+  }
+  if(!name) {
+    return;
+  }
+  req = spdylay_session_get_stream_user_data(session, stream_id);
+  if(req) {
+    check_gzip(req, nv);
+    spdylay_printf("[INFO] C <---------------------------- S (%s)\n", name);
+    for(i = 0; nv[i]; i += 2) {
+      spdylay_printf("       %s: %s\n", nv[i], nv[i+1]);
+    }
+  }
+}
+
+/*
+ * The implementation of spdylay_on_stream_close_callback type. We use
+ * this function to know the response is fully received. Since we just
+ * fetch 1 resource in this program, after reception of the response,
+ * we submit GOAWAY and close the session.
+ */
+static void on_stream_close_callback(spdylay_session *session,
+                                     int32_t stream_id,
+                                     spdylay_status_code status_code,
+                                     void *user_data)
+{
+  (void)user_data;
+  (void)status_code;
+  struct Request *req;
+  req = spdylay_session_get_stream_user_data(session, stream_id);
+  if(req) {
+    int rv;
+    rv = spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);
+    if(rv != 0) {
+      diec("spdylay_submit_goaway", rv);
+    }
+  }
+}
+
+#define MAX_OUTLEN 4096
+
+/*
+ * The implementation of spdylay_on_data_chunk_recv_callback type. We
+ * use this function to print the received response body.
+ */
+static void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags,
+                                        int32_t stream_id,
+                                        const uint8_t *data, size_t len,
+                                        void *user_data)
+{
+  (void)user_data;
+  (void)flags;
+
+  struct Request *req;
+  req = spdylay_session_get_stream_user_data(session, stream_id);
+  if(req) {
+    spdylay_printf("[INFO] C <---------------------------- S (DATA)\n");
+    spdylay_printf("       %lu bytes\n", (unsigned long int)len);
+    if(req->inflater) {
+      while(len > 0) {
+        uint8_t out[MAX_OUTLEN];
+        size_t outlen = MAX_OUTLEN;
+        size_t tlen = len;
+        int rv;
+        rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen);
+        if(rv == -1) {
+          spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR);
+          break;
+        }
+        fwrite(out, 1, outlen, stdout);
+        data += tlen;
+        len -= tlen;
+      }
+    } else {
+      /* TODO add support gzip */
+      fwrite(data, 1, len, stdout);
+
+      //check if the data is correct
+      if(strcmp(RESPONSE_BODY, (char *)data) != 0)
+		killparent(parent, "\nreceived data is not the same");
+    }
+    spdylay_printf("\n");
+  }
+}
+
+/*
+ * Setup callback functions. Spdylay API offers many callback
+ * functions, but most of them are optional. The send_callback is
+ * always required. Since we use spdylay_session_recv(), the
+ * recv_callback is also required.
+ */
+static void setup_spdylay_callbacks(spdylay_session_callbacks *callbacks)
+{
+  memset(callbacks, 0, sizeof(spdylay_session_callbacks));
+  callbacks->send_callback = send_callback;
+  callbacks->recv_callback = recv_callback;
+  callbacks->before_ctrl_send_callback = before_ctrl_send_callback;
+  callbacks->on_ctrl_send_callback = on_ctrl_send_callback;
+  callbacks->on_ctrl_recv_callback = on_ctrl_recv_callback;
+  callbacks->on_stream_close_callback = on_stream_close_callback;
+  callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback;
+}
+
+/*
+ * Callback function for SSL/TLS NPN. Since this program only supports
+ * SPDY protocol, if server does not offer SPDY protocol the Spdylay
+ * library supports, we terminate program.
+ */
+static int select_next_proto_cb(SSL* ssl,
+                                unsigned char **out, unsigned char *outlen,
+                                const unsigned char *in, unsigned int inlen,
+                                void *arg)
+{
+  (void)ssl;
+
+  int rv;
+  uint16_t *spdy_proto_version;
+  /* spdylay_select_next_protocol() selects SPDY protocol version the
+     Spdylay library supports. */
+  rv = spdylay_select_next_protocol(out, outlen, in, inlen);
+  if(rv <= 0) {
+    die("Server did not advertise spdy/2 or spdy/3 protocol.");
+  }
+  spdy_proto_version = (uint16_t*)arg;
+  *spdy_proto_version = rv;
+  return SSL_TLSEXT_ERR_OK;
+}
+
+/*
+ * Setup SSL context. We pass |spdy_proto_version| to get negotiated
+ * SPDY protocol version in NPN callback.
+ */
+static void init_ssl_ctx(SSL_CTX *ssl_ctx, uint16_t *spdy_proto_version)
+{
+  /* Disable SSLv2 and enable all workarounds for buggy servers */
+  SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
+  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
+  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
+  /* Set NPN callback */
+  SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb,
+                                   spdy_proto_version);
+}
+
+static void ssl_handshake(SSL *ssl, int fd)
+{
+  int rv;
+  if(SSL_set_fd(ssl, fd) == 0) {
+    dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
+  }
+  ERR_clear_error();
+  rv = SSL_connect(ssl);
+  if(rv <= 0) {
+    dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL));
+  }
+}
+
+/*
+ * Connects to the host |host| and port |port|.  This function returns
+ * the file descriptor of the client socket.
+ */
+static int connect_to(const char *host, uint16_t port)
+{
+  struct addrinfo hints;
+  int fd = -1;
+  int rv;
+  char service[NI_MAXSERV];
+  struct addrinfo *res, *rp;
+  snprintf(service, sizeof(service), "%u", port);
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  rv = getaddrinfo(host, service, &hints, &res);
+  if(rv != 0) {
+    dief("getaddrinfo", gai_strerror(rv));
+  }
+  for(rp = res; rp; rp = rp->ai_next) {
+    fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+    if(fd == -1) {
+      continue;
+    }
+    while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
+          errno == EINTR);
+    if(rv == 0) {
+      break;
+    }
+    close(fd);
+    fd = -1;
+  }
+  freeaddrinfo(res);
+  return fd;
+}
+
+static void make_non_block(int fd)
+{
+  int flags, rv;
+  while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
+  if(flags == -1) {
+    dief("fcntl", strerror(errno));
+  }
+  while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
+  if(rv == -1) {
+    dief("fcntl", strerror(errno));
+  }
+}
+
+/*
+ * Setting TCP_NODELAY is not mandatory for the SPDY protocol.
+ */
+static void set_tcp_nodelay(int fd)
+{
+  int val = 1;
+  int rv;
+  rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
+  if(rv == -1) {
+    dief("setsockopt", strerror(errno));
+  }
+}
+
+/*
+ * Update |pollfd| based on the state of |connection|.
+ */
+static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
+{
+  pollfd->events = 0;
+  if(spdylay_session_want_read(connection->session) ||
+     connection->want_io == WANT_READ) {
+    pollfd->events |= POLLIN;
+  }
+  if(spdylay_session_want_write(connection->session) ||
+     connection->want_io == WANT_WRITE) {
+    pollfd->events |= POLLOUT;
+  }
+}
+
+/*
+ * Submits the request |req| to the connection |connection|.  This
+ * function does not send packets; just append the request to the
+ * internal queue in |connection->session|.
+ */
+static void submit_request(struct Connection *connection, struct Request *req)
+{
+  int pri = 0;
+  int rv;
+  const char *nv[15];
+  /* We always use SPDY/3 style header even if the negotiated protocol
+     version is SPDY/2. The library translates the header name as
+     necessary. Make sure that the last item is NULL! */
+  nv[0] = ":method";     nv[1] = "GET";
+  nv[2] = ":path";       nv[3] = req->path;
+  nv[4] = ":version";    nv[5] = "HTTP/1.1";
+  nv[6] = ":scheme";     nv[7] = "https";
+  nv[8] = ":host";       nv[9] = req->hostport;
+  nv[10] = "accept";     nv[11] = "*/*";
+  nv[12] = "user-agent"; nv[13] = "spdylay/"SPDYLAY_VERSION;
+  nv[14] = NULL;
+  rv = spdylay_submit_request(connection->session, pri, nv, NULL, req);
+  if(rv != 0) {
+    diec("spdylay_submit_request", rv);
+  }
+}
+
+/*
+ * Performs the network I/O.
+ */
+static void exec_io(struct Connection *connection)
+{
+  int rv;
+  rv = spdylay_session_recv(connection->session);
+  if(rv != 0) {
+    diec("spdylay_session_recv", rv);
+  }
+  rv = spdylay_session_send(connection->session);
+  if(rv != 0) {
+    diec("spdylay_session_send", rv);
+  }
+}
+
+static void request_init(struct Request *req, const struct URI *uri)
+{
+  req->host = strcopy(uri->host, uri->hostlen);
+  req->port = uri->port;
+  req->path = strcopy(uri->path, uri->pathlen);
+  req->hostport = strcopy(uri->hostport, uri->hostportlen);
+  req->stream_id = -1;
+  req->inflater = NULL;
+}
+
+static void request_free(struct Request *req)
+{
+  free(req->host);
+  free(req->path);
+  free(req->hostport);
+  spdylay_gzip_inflate_del(req->inflater);
+}
+
+/*
+ * Fetches the resource denoted by |uri|.
+ */
+static void fetch_uri(const struct URI *uri)
+{
+  spdylay_session_callbacks callbacks;
+  int fd;
+  SSL_CTX *ssl_ctx;
+  SSL *ssl;
+  struct Request req;
+  struct Connection connection;
+  int rv;
+  nfds_t npollfds = 1;
+  struct pollfd pollfds[1];
+  uint16_t spdy_proto_version;
+
+  request_init(&req, uri);
+
+  setup_spdylay_callbacks(&callbacks);
+
+  /* Establish connection and setup SSL */
+  fd = connect_to(req.host, req.port);
+  if (-1 == fd)
+    abort ();
+  ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+  if(ssl_ctx == NULL) {
+    dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL));
+  }
+  init_ssl_ctx(ssl_ctx, &spdy_proto_version);
+  ssl = SSL_new(ssl_ctx);
+  if(ssl == NULL) {
+    dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
+  }
+  /* To simplify the program, we perform SSL/TLS handshake in blocking
+     I/O. */
+  ssl_handshake(ssl, fd);
+
+  connection.ssl = ssl;
+  connection.want_io = IO_NONE;
+
+  /* Here make file descriptor non-block */
+  make_non_block(fd);
+  set_tcp_nodelay(fd);
+
+  spdylay_printf("[INFO] SPDY protocol version = %d\n", spdy_proto_version);
+  rv = spdylay_session_client_new(&connection.session, spdy_proto_version,
+                                  &callbacks, &connection);
+  if(rv != 0) {
+    diec("spdylay_session_client_new", rv);
+  }
+
+  /* Submit the HTTP request to the outbound queue. */
+  submit_request(&connection, &req);
+
+  pollfds[0].fd = fd;
+  ctl_poll(pollfds, &connection);
+
+  /* Event loop */
+  while(spdylay_session_want_read(connection.session) ||
+        spdylay_session_want_write(connection.session)) {
+    int nfds = poll(pollfds, npollfds, -1);
+    if(nfds == -1) {
+      dief("poll", strerror(errno));
+    }
+    if(pollfds[0].revents & (POLLIN | POLLOUT)) {
+      exec_io(&connection);
+    }
+    if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
+      die("Connection error");
+    }
+    ctl_poll(pollfds, &connection);
+  }
+
+  /* Resource cleanup */
+  spdylay_session_del(connection.session);
+  SSL_shutdown(ssl);
+  SSL_free(ssl);
+  SSL_CTX_free(ssl_ctx);
+  shutdown(fd, SHUT_WR);
+  close(fd);
+  request_free(&req);
+}
+
+static int parse_uri(struct URI *res, const char *uri)
+{
+  /* We only interested in https */
+  size_t len, i, offset;
+  memset(res, 0, sizeof(struct URI));
+  len = strlen(uri);
+  if(len < 9 || memcmp("https://", uri, 8) != 0) {
+    return -1;
+  }
+  offset = 8;
+  res->host = res->hostport = &uri[offset];
+  res->hostlen = 0;
+  if(uri[offset] == '[') {
+    /* IPv6 literal address */
+    ++offset;
+    ++res->host;
+    for(i = offset; i < len; ++i) {
+      if(uri[i] == ']') {
+        res->hostlen = i-offset;
+        offset = i+1;
+        break;
+      }
+    }
+  } else {
+    const char delims[] = ":/?#";
+    for(i = offset; i < len; ++i) {
+      if(strchr(delims, uri[i]) != NULL) {
+        break;
+      }
+    }
+    res->hostlen = i-offset;
+    offset = i;
+  }
+  if(res->hostlen == 0) {
+    return -1;
+  }
+  /* Assuming https */
+  res->port = 443;
+  if(offset < len) {
+    if(uri[offset] == ':') {
+      /* port */
+      const char delims[] = "/?#";
+      int port = 0;
+      ++offset;
+      for(i = offset; i < len; ++i) {
+        if(strchr(delims, uri[i]) != NULL) {
+          break;
+        }
+        if('0' <= uri[i] && uri[i] <= '9') {
+          port *= 10;
+          port += uri[i]-'0';
+          if(port > 65535) {
+            return -1;
+          }
+        } else {
+          return -1;
+        }
+      }
+      if(port == 0) {
+        return -1;
+      }
+      offset = i;
+      res->port = port;
+    }
+  }
+  res->hostportlen = uri+offset-res->host;
+  for(i = offset; i < len; ++i) {
+    if(uri[i] == '#') {
+      break;
+    }
+  }
+  if(i-offset == 0) {
+    res->path = "/";
+    res->pathlen = 1;
+  } else {
+    res->path = &uri[offset];
+    res->pathlen = i-offset;
+  }
+  return 0;
+}
+
+
+/*****
+ * end of code needed to utilize spdylay
+ */
+
+
+/*****
+ * start of code needed to utilize microspdy
+ */
+
+void
+new_session_callback (void *cls,
+						struct SPDY_Session * session)
+{
+	char ipstr[1024];
+
+	struct sockaddr *addr;
+	socklen_t addr_len = SPDY_get_remote_addr(session, &addr);
+
+	if(!addr_len)
+	{
+		printf("SPDY_get_remote_addr");
+		abort();
+	}
+
+	if(strcmp(CLS,cls)!=0)
+	{
+		killchild(child,"wrong cls");
+	}
+
+	if(AF_INET == addr->sa_family)
+	{
+		struct sockaddr_in * addr4 = (struct sockaddr_in *) addr;
+		if(NULL == inet_ntop(AF_INET, &(addr4->sin_addr), ipstr, sizeof(ipstr)))
+		{
+			killchild(child,"inet_ntop");
+		}
+		printf("New connection from: %s:%i\n", ipstr, ntohs(addr4->sin_port));
+
+		loop = 0;
+	}
+#if HAVE_INET6
+	else if(AF_INET6 == addr->sa_family)
+	{
+		struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *) addr;
+		if(NULL == inet_ntop(AF_INET6, &(addr6->sin6_addr), ipstr, sizeof(ipstr)))
+		{
+			killchild(child,"inet_ntop");
+		}
+		printf("New connection from: %s:%i\n", ipstr, ntohs(addr6->sin6_port));
+
+		loop = 0;
+	}
+#endif
+	else
+	{
+		killchild(child,"wrong family");
+	}
+}
+
+/*****
+ * end of code needed to utilize microspdy
+ */
+
+//child process
+void
+childproc(int parent)
+{
+  struct URI uri;
+  struct sigaction act;
+  int rv;
+  char *uristr;
+
+  memset(&act, 0, sizeof(struct sigaction));
+  act.sa_handler = SIG_IGN;
+  sigaction(SIGPIPE, &act, 0);
+
+	asprintf(&uristr, "https://127.0.0.1:%i/",port);
+
+  SSL_load_error_strings();
+  SSL_library_init();
+
+  rv = parse_uri(&uri, uristr);
+  if(rv != 0) {
+    killparent(parent,"parse_uri failed");
+  }
+  fetch_uri(&uri);
+}
+
+//parent proc
+int
+parentproc(int child)
+{
+	int childstatus = 0;
+	unsigned long long timeoutlong=0;
+	struct timeval timeout;
+	int ret;
+	fd_set read_fd_set;
+	fd_set write_fd_set;
+	fd_set except_fd_set;
+	int maxfd = -1;
+	struct SPDY_Daemon *daemon;
+
+	SPDY_init();
+
+	daemon = SPDY_start_daemon(port,
+								DATA_DIR "cert-and-key.pem",
+								DATA_DIR "cert-and-key.pem",
+								&new_session_callback,NULL,NULL,NULL,CLS,SPDY_DAEMON_OPTION_END);
+
+	if(NULL==daemon){
+		printf("no daemon\n");
+		return 1;
+	}
+
+	do
+	{
+		FD_ZERO(&read_fd_set);
+		FD_ZERO(&write_fd_set);
+		FD_ZERO(&except_fd_set);
+
+		ret = SPDY_get_timeout(daemon, &timeoutlong);
+		if(SPDY_NO == ret || timeoutlong > 1000)
+		{
+			timeout.tv_sec = 1;
+      timeout.tv_usec = 0;
+		}
+		else
+		{
+			timeout.tv_sec = timeoutlong / 1000;
+			timeout.tv_usec = (timeoutlong % 1000) * 1000;
+		}
+
+		maxfd = SPDY_get_fdset (daemon,
+								&read_fd_set,
+								&write_fd_set,
+								&except_fd_set);
+
+		ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
+
+		switch(ret) {
+			case -1:
+				printf("select error: %i\n", errno);
+				killchild(child, "select error");
+				break;
+			case 0:
+
+				break;
+			default:
+				SPDY_run(daemon);
+
+			break;
+		}
+	}
+	while(loop && waitpid(child,&childstatus,WNOHANG) != child);
+
+	SPDY_stop_daemon(daemon);
+
+	SPDY_deinit();
+
+	if(loop)
+		return WEXITSTATUS(childstatus);
+	if(waitpid(child,&childstatus,WNOHANG) == child)
+		return WEXITSTATUS(childstatus);
+
+	kill(child,SIGKILL);
+
+	waitpid(child,&childstatus,0);
+
+	return 0;
+}
+
+int main()
+{
+	port = get_port(14123);
+	parent = getpid();
+
+   child = fork();
+   if (child == -1)
+   {
+      fprintf(stderr, "can't fork, error %d\n", errno);
+      exit(EXIT_FAILURE);
+   }
+
+   if (child == 0)
+   {
+      childproc(parent);
+      _exit(0);
+   }
+   else
+   {
+	   int ret = parentproc(child);
+      exit(ret);
+   }
+   return 1;
+}
diff --git a/src/testspdy/test_notls.c b/src/testspdy/test_notls.c
new file mode 100644
index 0000000..3f44d96
--- /dev/null
+++ b/src/testspdy/test_notls.c
@@ -0,0 +1,973 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file request_response.c
+ * @brief  tests receiving request and sending response. spdycli.c (spdylay)
+ * 			code is reused here
+ * @author Andrey Uzunov
+ * @author Tatsuhiro Tsujikawa
+ */
+
+#include "platform.h"
+#include "microspdy.h"
+#include <sys/wait.h>
+#include "common.h"
+
+#define RESPONSE_BODY "<html><body><b>Hi, this is libmicrospdy!</b></body></html>"
+
+#define CLS "anything"
+
+pid_t parent;
+pid_t child;
+char *rcvbuf;
+int rcvbuf_c = 0;
+
+int session_closed_called = 0;
+
+void
+killchild(int pid, char *message)
+{
+	printf("%s\n",message);
+	kill(pid, SIGKILL);
+	exit(1);
+}
+
+void
+killparent(int pid, char *message)
+{
+	printf("%s\n",message);
+	kill(pid, SIGKILL);
+	_exit(1);
+}
+
+
+/*****
+ * start of code needed to utilize spdylay
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <spdylay/spdylay.h>
+
+enum {
+  IO_NONE,
+  WANT_READ,
+  WANT_WRITE
+};
+
+struct Connection {
+  spdylay_session *session;
+  /* WANT_READ if SSL connection needs more input; or WANT_WRITE if it
+     needs more output; or IO_NONE. This is necessary because SSL/TLS
+     re-negotiation is possible at any time. Spdylay API offers
+     similar functions like spdylay_session_want_read() and
+     spdylay_session_want_write() but they do not take into account
+     SSL connection. */
+  int want_io;
+  int fd;
+};
+
+struct Request {
+  char *host;
+  uint16_t port;
+  /* In this program, path contains query component as well. */
+  char *path;
+  /* This is the concatenation of host and port with ":" in
+     between. */
+  char *hostport;
+  /* Stream ID for this request. */
+  int32_t stream_id;
+  /* The gzip stream inflater for the compressed response. */
+  spdylay_gzip *inflater;
+};
+
+struct URI {
+  const char *host;
+  size_t hostlen;
+  uint16_t port;
+  /* In this program, path contains query component as well. */
+  const char *path;
+  size_t pathlen;
+  const char *hostport;
+  size_t hostportlen;
+};
+
+/*
+ * Returns copy of string |s| with the length |len|. The returned
+ * string is NULL-terminated.
+ */
+static char* strcopy(const char *s, size_t len)
+{
+  char *dst;
+  dst = malloc(len+1);
+  if (NULL == dst)
+    abort ();
+  memcpy(dst, s, len);
+  dst[len] = '\0';
+  return dst;
+}
+
+/*
+ * Prints error message |msg| and exit.
+ */
+static void die(const char *msg)
+{
+  fprintf(stderr, "FATAL: %s\n", msg);
+  exit(EXIT_FAILURE);
+}
+
+/*
+ * Prints error containing the function name |func| and message |msg|
+ * and exit.
+ */
+static void dief(const char *func, const char *msg)
+{
+  fprintf(stderr, "FATAL: %s: %s\n", func, msg);
+  exit(EXIT_FAILURE);
+}
+
+/*
+ * Prints error containing the function name |func| and error code
+ * |error_code| and exit.
+ */
+static void diec(const char *func, int error_code)
+{
+  fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
+          spdylay_strerror(error_code));
+  exit(EXIT_FAILURE);
+}
+
+/*
+ * Check response is content-encoding: gzip. We need this because SPDY
+ * client is required to support gzip.
+ */
+static void check_gzip(struct Request *req, char **nv)
+{
+  int gzip = 0;
+  size_t i;
+  for(i = 0; nv[i]; i += 2) {
+    if(strcmp("content-encoding", nv[i]) == 0) {
+      gzip = strcmp("gzip", nv[i+1]) == 0;
+      break;
+    }
+  }
+  if(gzip) {
+    int rv;
+    if(req->inflater) {
+      return;
+    }
+    rv = spdylay_gzip_inflate_new(&req->inflater);
+    if(rv != 0) {
+      die("Can't allocate inflate stream.");
+    }
+  }
+}
+
+/*
+ * The implementation of spdylay_send_callback type. Here we write
+ * |data| with size |length| to the network and return the number of
+ * bytes actually written. See the documentation of
+ * spdylay_send_callback for the details.
+ */
+static ssize_t send_callback(spdylay_session *session,
+                             const uint8_t *data, size_t length, int flags,
+                             void *user_data)
+{
+  (void)session;
+  (void)flags;
+
+  struct Connection *connection;
+  ssize_t rv;
+  connection = (struct Connection*)user_data;
+  connection->want_io = IO_NONE;
+
+    rv = write(connection->fd,
+            data,
+            length);
+
+    if (rv < 0)
+    {
+      switch(errno)
+      {
+        case EAGAIN:
+  #if EAGAIN != EWOULDBLOCK
+        case EWOULDBLOCK:
+  #endif
+          connection->want_io = WANT_WRITE;
+          rv = SPDYLAY_ERR_WOULDBLOCK;
+          break;
+
+        default:
+          rv = SPDYLAY_ERR_CALLBACK_FAILURE;
+      }
+    }
+  return rv;
+}
+
+/*
+ * The implementation of spdylay_recv_callback type. Here we read data
+ * from the network and write them in |buf|. The capacity of |buf| is
+ * |length| bytes. Returns the number of bytes stored in |buf|. See
+ * the documentation of spdylay_recv_callback for the details.
+ */
+static ssize_t recv_callback(spdylay_session *session,
+                             uint8_t *buf, size_t length, int flags,
+                             void *user_data)
+{
+  (void)session;
+  (void)flags;
+
+  struct Connection *connection;
+  ssize_t rv;
+  connection = (struct Connection*)user_data;
+  connection->want_io = IO_NONE;
+
+    rv = read(connection->fd,
+            buf,
+            length);
+
+    if (rv < 0)
+    {
+      switch(errno)
+      {
+        case EAGAIN:
+  #if EAGAIN != EWOULDBLOCK
+        case EWOULDBLOCK:
+  #endif
+          connection->want_io = WANT_READ;
+          rv = SPDYLAY_ERR_WOULDBLOCK;
+          break;
+
+        default:
+          rv = SPDYLAY_ERR_CALLBACK_FAILURE;
+      }
+    }
+    else if(rv == 0)
+      rv = SPDYLAY_ERR_EOF;
+  return rv;
+}
+
+/*
+ * The implementation of spdylay_before_ctrl_send_callback type.  We
+ * use this function to get stream ID of the request. This is because
+ * stream ID is not known when we submit the request
+ * (spdylay_submit_request).
+ */
+static void before_ctrl_send_callback(spdylay_session *session,
+                                      spdylay_frame_type type,
+                                      spdylay_frame *frame,
+                                      void *user_data)
+{
+  (void)user_data;
+
+  if(type == SPDYLAY_SYN_STREAM) {
+    struct Request *req;
+    int stream_id = frame->syn_stream.stream_id;
+    req = spdylay_session_get_stream_user_data(session, stream_id);
+    if(req && req->stream_id == -1) {
+      req->stream_id = stream_id;
+      printf("[INFO] Stream ID = %d\n", stream_id);
+    }
+  }
+}
+
+static void on_ctrl_send_callback(spdylay_session *session,
+                                  spdylay_frame_type type,
+                                  spdylay_frame *frame, void *user_data)
+{
+  (void)user_data;
+
+  char **nv;
+  const char *name = NULL;
+  int32_t stream_id;
+  size_t i;
+  switch(type) {
+  case SPDYLAY_SYN_STREAM:
+    nv = frame->syn_stream.nv;
+    name = "SYN_STREAM";
+    stream_id = frame->syn_stream.stream_id;
+    break;
+  default:
+    break;
+  }
+  if(name && spdylay_session_get_stream_user_data(session, stream_id)) {
+    printf("[INFO] C ----------------------------> S (%s)\n", name);
+    for(i = 0; nv[i]; i += 2) {
+      printf("       %s: %s\n", nv[i], nv[i+1]);
+    }
+  }
+}
+
+static void on_ctrl_recv_callback(spdylay_session *session,
+                                  spdylay_frame_type type,
+                                  spdylay_frame *frame, void *user_data)
+{
+  (void)user_data;
+
+  struct Request *req;
+  char **nv;
+  const char *name = NULL;
+  int32_t stream_id;
+  size_t i;
+  switch(type) {
+  case SPDYLAY_SYN_REPLY:
+    nv = frame->syn_reply.nv;
+    name = "SYN_REPLY";
+    stream_id = frame->syn_reply.stream_id;
+    break;
+  case SPDYLAY_HEADERS:
+    nv = frame->headers.nv;
+    name = "HEADERS";
+    stream_id = frame->headers.stream_id;
+    break;
+  default:
+    break;
+  }
+  if(!name) {
+    return;
+  }
+  req = spdylay_session_get_stream_user_data(session, stream_id);
+  if(req) {
+    check_gzip(req, nv);
+    printf("[INFO] C <---------------------------- S (%s)\n", name);
+    for(i = 0; nv[i]; i += 2) {
+      printf("       %s: %s\n", nv[i], nv[i+1]);
+    }
+  }
+}
+
+/*
+ * The implementation of spdylay_on_stream_close_callback type. We use
+ * this function to know the response is fully received. Since we just
+ * fetch 1 resource in this program, after reception of the response,
+ * we submit GOAWAY and close the session.
+ */
+static void on_stream_close_callback(spdylay_session *session,
+                                     int32_t stream_id,
+                                     spdylay_status_code status_code,
+                                     void *user_data)
+{
+  (void)status_code;
+  (void)user_data;
+
+  struct Request *req;
+  req = spdylay_session_get_stream_user_data(session, stream_id);
+  if(req) {
+    int rv;
+    rv = spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);
+    if(rv != 0) {
+      diec("spdylay_submit_goaway", rv);
+    }
+  }
+}
+
+#define MAX_OUTLEN 4096
+
+/*
+ * The implementation of spdylay_on_data_chunk_recv_callback type. We
+ * use this function to print the received response body.
+ */
+static void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags,
+                                        int32_t stream_id,
+                                        const uint8_t *data, size_t len,
+                                        void *user_data)
+{
+  (void)flags;
+  (void)user_data;
+
+  struct Request *req;
+  req = spdylay_session_get_stream_user_data(session, stream_id);
+  if(req) {
+    printf("[INFO] C <---------------------------- S (DATA)\n");
+    printf("       %lu bytes\n", (unsigned long int)len);
+    if(req->inflater) {
+      while(len > 0) {
+        uint8_t out[MAX_OUTLEN];
+        size_t outlen = MAX_OUTLEN;
+        size_t tlen = len;
+        int rv;
+        rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen);
+        if(rv == -1) {
+          spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR);
+          break;
+        }
+        fwrite(out, 1, outlen, stdout);
+        data += tlen;
+        len -= tlen;
+      }
+    } else {
+      /* TODO add support gzip */
+      fwrite(data, 1, len, stdout);
+
+      //check if the data is correct
+      //if(strcmp(RESPONSE_BODY, data) != 0)
+		//killparent(parent, "\nreceived data is not the same");
+      if(len + rcvbuf_c > strlen(RESPONSE_BODY))
+		killparent(parent, "\nreceived data is not the same");
+
+		strcpy(rcvbuf + rcvbuf_c,(char*)data);
+		rcvbuf_c+=len;
+    }
+    printf("\n");
+  }
+}
+
+/*
+ * Setup callback functions. Spdylay API offers many callback
+ * functions, but most of them are optional. The send_callback is
+ * always required. Since we use spdylay_session_recv(), the
+ * recv_callback is also required.
+ */
+static void setup_spdylay_callbacks(spdylay_session_callbacks *callbacks)
+{
+  memset(callbacks, 0, sizeof(spdylay_session_callbacks));
+  callbacks->send_callback = send_callback;
+  callbacks->recv_callback = recv_callback;
+  callbacks->before_ctrl_send_callback = before_ctrl_send_callback;
+  callbacks->on_ctrl_send_callback = on_ctrl_send_callback;
+  callbacks->on_ctrl_recv_callback = on_ctrl_recv_callback;
+  callbacks->on_stream_close_callback = on_stream_close_callback;
+  callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback;
+}
+
+
+/*
+ * Connects to the host |host| and port |port|.  This function returns
+ * the file descriptor of the client socket.
+ */
+static int connect_to(const char *host, uint16_t port)
+{
+  struct addrinfo hints;
+  int fd = -1;
+  int rv;
+  char service[NI_MAXSERV];
+  struct addrinfo *res, *rp;
+  snprintf(service, sizeof(service), "%u", port);
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  rv = getaddrinfo(host, service, &hints, &res);
+  if(rv != 0) {
+    dief("getaddrinfo", gai_strerror(rv));
+  }
+  for(rp = res; rp; rp = rp->ai_next) {
+    fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+    if(fd == -1) {
+      continue;
+    }
+    while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
+          errno == EINTR);
+    if(rv == 0) {
+      break;
+    }
+    close(fd);
+    fd = -1;
+    dief("connect", strerror(errno));
+  }
+  freeaddrinfo(res);
+  return fd;
+}
+
+static void make_non_block(int fd)
+{
+  int flags, rv;
+  while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
+  if(flags == -1) {
+    dief("fcntl1", strerror(errno));
+  }
+  while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
+  if(rv == -1) {
+    dief("fcntl2", strerror(errno));
+  }
+}
+
+/*
+ * Setting TCP_NODELAY is not mandatory for the SPDY protocol.
+ */
+static void set_tcp_nodelay(int fd)
+{
+  int val = 1;
+  int rv;
+  rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
+  if(rv == -1) {
+    dief("setsockopt", strerror(errno));
+  }
+}
+
+/*
+ * Update |pollfd| based on the state of |connection|.
+ */
+static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
+{
+  pollfd->events = 0;
+  if(spdylay_session_want_read(connection->session) ||
+     connection->want_io == WANT_READ) {
+    pollfd->events |= POLLIN;
+  }
+  if(spdylay_session_want_write(connection->session) ||
+     connection->want_io == WANT_WRITE) {
+    pollfd->events |= POLLOUT;
+  }
+}
+
+/*
+ * Submits the request |req| to the connection |connection|.  This
+ * function does not send packets; just append the request to the
+ * internal queue in |connection->session|.
+ */
+static void submit_request(struct Connection *connection, struct Request *req)
+{
+  int pri = 0;
+  int rv;
+  const char *nv[15];
+  /* We always use SPDY/3 style header even if the negotiated protocol
+     version is SPDY/2. The library translates the header name as
+     necessary. Make sure that the last item is NULL! */
+  nv[0] = ":method";     nv[1] = "GET";
+  nv[2] = ":path";       nv[3] = req->path;
+  nv[4] = ":version";    nv[5] = "HTTP/1.1";
+  nv[6] = ":scheme";     nv[7] = "https";
+  nv[8] = ":host";       nv[9] = req->hostport;
+  nv[10] = "accept";     nv[11] = "*/*";
+  nv[12] = "user-agent"; nv[13] = "spdylay/"SPDYLAY_VERSION;
+  nv[14] = NULL;
+  rv = spdylay_submit_request(connection->session, pri, nv, NULL, req);
+  if(rv != 0) {
+    diec("spdylay_submit_request", rv);
+  }
+}
+
+/*
+ * Performs the network I/O.
+ */
+static void exec_io(struct Connection *connection)
+{
+  int rv;
+  rv = spdylay_session_recv(connection->session);
+  if(rv != 0) {
+    diec("spdylay_session_recv", rv);
+  }
+  rv = spdylay_session_send(connection->session);
+  if(rv != 0) {
+    diec("spdylay_session_send", rv);
+  }
+}
+
+static void request_init(struct Request *req, const struct URI *uri)
+{
+  req->host = strcopy(uri->host, uri->hostlen);
+  req->port = uri->port;
+  req->path = strcopy(uri->path, uri->pathlen);
+  req->hostport = strcopy(uri->hostport, uri->hostportlen);
+  req->stream_id = -1;
+  req->inflater = NULL;
+}
+
+static void request_free(struct Request *req)
+{
+  free(req->host);
+  free(req->path);
+  free(req->hostport);
+  spdylay_gzip_inflate_del(req->inflater);
+}
+
+/*
+ * Fetches the resource denoted by |uri|.
+ */
+static void fetch_uri(const struct URI *uri)
+{
+  spdylay_session_callbacks callbacks;
+  int fd;
+  struct Request req;
+  struct Connection connection;
+  int rv;
+  nfds_t npollfds = 1;
+  struct pollfd pollfds[1];
+  uint16_t spdy_proto_version = 3;
+
+  request_init(&req, uri);
+
+  setup_spdylay_callbacks(&callbacks);
+
+  /* Establish connection and setup SSL */
+  fd = connect_to(req.host, req.port);
+  if (-1 == fd)
+    abort ();
+
+  connection.fd = fd;
+  connection.want_io = IO_NONE;
+
+  /* Here make file descriptor non-block */
+  make_non_block(fd);
+  set_tcp_nodelay(fd);
+
+  printf("[INFO] SPDY protocol version = %d\n", spdy_proto_version);
+  rv = spdylay_session_client_new(&connection.session, spdy_proto_version,
+                                  &callbacks, &connection);
+  if(rv != 0) {
+    diec("spdylay_session_client_new", rv);
+  }
+
+  /* Submit the HTTP request to the outbound queue. */
+  submit_request(&connection, &req);
+
+  pollfds[0].fd = fd;
+  ctl_poll(pollfds, &connection);
+
+  /* Event loop */
+  while(spdylay_session_want_read(connection.session) ||
+        spdylay_session_want_write(connection.session)) {
+    int nfds = poll(pollfds, npollfds, -1);
+    if(nfds == -1) {
+      dief("poll", strerror(errno));
+    }
+    if(pollfds[0].revents & (POLLIN | POLLOUT)) {
+      exec_io(&connection);
+    }
+    if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
+      die("Connection error");
+    }
+    ctl_poll(pollfds, &connection);
+  }
+
+  /* Resource cleanup */
+  spdylay_session_del(connection.session);
+  shutdown(fd, SHUT_WR);
+  close(fd);
+  request_free(&req);
+}
+
+static int parse_uri(struct URI *res, const char *uri)
+{
+  /* We only interested in https */
+  size_t len, i, offset;
+  memset(res, 0, sizeof(struct URI));
+  len = strlen(uri);
+  if(len < 9 || memcmp("https://", uri, 8) != 0) {
+    return -1;
+  }
+  offset = 8;
+  res->host = res->hostport = &uri[offset];
+  res->hostlen = 0;
+  if(uri[offset] == '[') {
+    /* IPv6 literal address */
+    ++offset;
+    ++res->host;
+    for(i = offset; i < len; ++i) {
+      if(uri[i] == ']') {
+        res->hostlen = i-offset;
+        offset = i+1;
+        break;
+      }
+    }
+  } else {
+    const char delims[] = ":/?#";
+    for(i = offset; i < len; ++i) {
+      if(strchr(delims, uri[i]) != NULL) {
+        break;
+      }
+    }
+    res->hostlen = i-offset;
+    offset = i;
+  }
+  if(res->hostlen == 0) {
+    return -1;
+  }
+  /* Assuming https */
+  res->port = 443;
+  if(offset < len) {
+    if(uri[offset] == ':') {
+      /* port */
+      const char delims[] = "/?#";
+      int port = 0;
+      ++offset;
+      for(i = offset; i < len; ++i) {
+        if(strchr(delims, uri[i]) != NULL) {
+          break;
+        }
+        if('0' <= uri[i] && uri[i] <= '9') {
+          port *= 10;
+          port += uri[i]-'0';
+          if(port > 65535) {
+            return -1;
+          }
+        } else {
+          return -1;
+        }
+      }
+      if(port == 0) {
+        return -1;
+      }
+      offset = i;
+      res->port = port;
+    }
+  }
+  res->hostportlen = uri+offset-res->host;
+  for(i = offset; i < len; ++i) {
+    if(uri[i] == '#') {
+      break;
+    }
+  }
+  if(i-offset == 0) {
+    res->path = "/";
+    res->pathlen = 1;
+  } else {
+    res->path = &uri[offset];
+    res->pathlen = i-offset;
+  }
+  return 0;
+}
+
+
+/*****
+ * end of code needed to utilize spdylay
+ */
+
+
+/*****
+ * start of code needed to utilize microspdy
+ */
+
+
+void
+standard_request_handler(void *cls,
+						struct SPDY_Request * request,
+						uint8_t priority,
+                        const char *method,
+                        const char *path,
+                        const char *version,
+                        const char *host,
+                        const char *scheme,
+						struct SPDY_NameValue * headers,
+            bool more)
+{
+	(void)cls;
+	(void)request;
+	(void)priority;
+	(void)host;
+	(void)scheme;
+	(void)headers;
+	(void)method;
+	(void)version;
+	(void)more;
+
+	struct SPDY_Response *response=NULL;
+
+	if(strcmp(CLS,cls)!=0)
+	{
+		killchild(child,"wrong cls");
+	}
+
+	response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,RESPONSE_BODY,strlen(RESPONSE_BODY));
+
+	if(NULL==response){
+		fprintf(stdout,"no response obj\n");
+		exit(3);
+	}
+
+	if(SPDY_queue_response(request,response,true,false,NULL,(void*)strdup(path))!=SPDY_YES)
+	{
+		fprintf(stdout,"queue\n");
+		exit(4);
+	}
+}
+
+void
+session_closed_handler (void *cls,
+						struct SPDY_Session * session,
+						int by_client)
+{
+	printf("session_closed_handler called\n");
+
+	if(strcmp(CLS,cls)!=0)
+	{
+		killchild(child,"wrong cls");
+	}
+
+	if(SPDY_YES != by_client)
+	{
+		//killchild(child,"wrong by_client");
+		printf("session closed by server\n");
+	}
+	else
+	{
+		printf("session closed by client\n");
+	}
+
+	if(NULL == session)
+	{
+		killchild(child,"session is NULL");
+	}
+
+	session_closed_called = 1;
+}
+
+
+/*****
+ * end of code needed to utilize microspdy
+ */
+
+//child process
+void
+childproc(int port)
+{
+  struct URI uri;
+  struct sigaction act;
+  int rv;
+  char *uristr;
+
+  memset(&act, 0, sizeof(struct sigaction));
+  act.sa_handler = SIG_IGN;
+  sigaction(SIGPIPE, &act, 0);
+
+	usleep(10000);
+	asprintf(&uristr, "https://127.0.0.1:%i/",port);
+	if(NULL == (rcvbuf = malloc(strlen(RESPONSE_BODY)+1)))
+		killparent(parent,"no memory");
+
+  rv = parse_uri(&uri, uristr);
+  if(rv != 0) {
+    killparent(parent,"parse_uri failed");
+  }
+  fetch_uri(&uri);
+
+  if(strcmp(rcvbuf, RESPONSE_BODY))
+    killparent(parent,"received data is different");
+}
+
+//parent proc
+int
+parentproc( int port)
+{
+	int childstatus;
+	unsigned long long timeoutlong=0;
+	struct timeval timeout;
+	int ret;
+	fd_set read_fd_set;
+	fd_set write_fd_set;
+	fd_set except_fd_set;
+	int maxfd = -1;
+	struct SPDY_Daemon *daemon;
+
+	SPDY_init();
+
+	daemon = SPDY_start_daemon(port,
+								NULL,
+								NULL,
+								NULL,&session_closed_handler,&standard_request_handler,NULL,CLS,
+                SPDY_DAEMON_OPTION_IO_SUBSYSTEM, SPDY_IO_SUBSYSTEM_RAW,
+                SPDY_DAEMON_OPTION_FLAGS, SPDY_DAEMON_FLAG_NO_DELAY,
+                SPDY_DAEMON_OPTION_END);
+
+	if(NULL==daemon){
+		printf("no daemon\n");
+		return 1;
+	}
+
+	do
+	{
+		FD_ZERO(&read_fd_set);
+		FD_ZERO(&write_fd_set);
+		FD_ZERO(&except_fd_set);
+
+		ret = SPDY_get_timeout(daemon, &timeoutlong);
+		if(SPDY_NO == ret || timeoutlong > 1000)
+		{
+			timeout.tv_sec = 1;
+      timeout.tv_usec = 0;
+		}
+		else
+		{
+			timeout.tv_sec = timeoutlong / 1000;
+			timeout.tv_usec = (timeoutlong % 1000) * 1000;
+		}
+
+		maxfd = SPDY_get_fdset (daemon,
+								&read_fd_set,
+								&write_fd_set,
+								&except_fd_set);
+
+		ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
+
+		switch(ret) {
+			case -1:
+				printf("select error: %i\n", errno);
+				killchild(child, "select error");
+				break;
+			case 0:
+
+				break;
+			default:
+				SPDY_run(daemon);
+
+			break;
+		}
+	}
+	while(waitpid(child,&childstatus,WNOHANG) != child);
+
+	//give chance to the client to close socket and handle this in run
+	usleep(100000);
+	SPDY_run(daemon);
+
+	SPDY_stop_daemon(daemon);
+
+	SPDY_deinit();
+
+	return WEXITSTATUS(childstatus);
+}
+
+int main()
+{
+	int port = get_port(12123);
+	parent = getpid();
+
+   child = fork();
+   if (child == -1)
+   {
+      fprintf(stderr, "can't fork, error %d\n", errno);
+      exit(EXIT_FAILURE);
+   }
+
+   if (child == 0)
+   {
+      childproc(port);
+      _exit(0);
+   }
+   else
+   {
+	   int ret = parentproc(port);
+	   if(1 == session_closed_called && 0 == ret)
+      exit(0);
+      else
+      exit(ret ? ret : 21);
+   }
+   return 1;
+}
diff --git a/src/testspdy/test_proxies.c b/src/testspdy/test_proxies.c
new file mode 100644
index 0000000..44a0c1b
--- /dev/null
+++ b/src/testspdy/test_proxies.c
@@ -0,0 +1,255 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file test_proxies.c
+ * @brief  test curl > mhd2spdylay > microspdy2http > mhd
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include "microspdy.h"
+#include "common.h"
+#include <sys/wait.h>
+#include <stdio.h>   /* printf, stderr, fprintf */
+#include <sys/types.h> /* pid_t */
+#include <unistd.h>  /* _exit, fork */
+#include <stdlib.h>  /* exit */
+#include <errno.h>   /* errno */
+#include <sys/wait.h> /* pid_t */
+#include "common.h"
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif
+
+#define EXPECTED_BODY "<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>"
+
+
+pid_t parent;
+	pid_t child_mhd;
+	pid_t child_spdy2http;
+	pid_t child_mhd2spdy;
+	pid_t child_curl;
+
+	uint16_t mhd_port;
+	uint16_t spdy2http_port;
+	uint16_t mhd2spdy_port;
+
+void
+killproc(int pid, const char *message)
+{
+	printf("%s\nkilling %i\n",message,pid);
+	kill(pid, SIGKILL);
+}
+
+
+void killchildren()
+{
+    if(0 != child_mhd)
+      killproc(child_mhd,"kill mhd\n");
+    if(0 != child_spdy2http)
+      killproc(child_spdy2http,"kill spdy2http\n");
+    if(0 != child_mhd2spdy)
+      killproc(child_mhd2spdy,"kill mhd2spdy\n");
+    if(0 != child_curl)
+      killproc(child_curl,"kill curl\n");
+}
+
+pid_t au_fork()
+{
+  pid_t child = fork();
+	if (child == -1)
+	{
+    killchildren();
+
+		killproc(parent,"fork failed\n");
+	}
+
+	return child;
+}
+
+
+int main()
+{
+  //pid_t child;
+	int childstatus;
+	pid_t wpid;
+
+	parent = getpid();
+	mhd_port = get_port(4000);
+	spdy2http_port = get_port(4100);
+	mhd2spdy_port = get_port(4200);
+
+	child_mhd = au_fork();
+	if (child_mhd == 0)
+	{
+    //run MHD
+		pid_t devnull;
+    char *port_s;
+
+		close(1);
+		devnull = open("/dev/null", O_WRONLY);
+                if (-1 == devnull)
+                  abort();
+		if (1 != devnull)
+		{
+			dup2(devnull, 1);
+			close(devnull);
+		}
+    asprintf(&port_s, "%i", mhd_port);
+		execlp ("../examples/minimal_example", "minimal_example", port_s, NULL);
+		fprintf(stderr, "executing mhd failed\nFor this test 'make' must be run before 'make check'!\n");
+    //killchildren();
+    _exit(1);
+	}
+
+
+	child_spdy2http = au_fork();
+	if (child_spdy2http == 0)
+	{
+    //run spdy2http
+		pid_t devnull;
+    char *port_s;
+    //char *url;
+
+		close(1);
+		devnull = open("/dev/null", O_WRONLY);
+                if (-1 == devnull)
+                  abort();
+		if (1 != devnull)
+		{
+			dup2(devnull, 1);
+			close(devnull);
+		}
+    //asprintf(&url, "127.0.0.1:%i", mhd_port);
+    asprintf(&port_s, "%i", spdy2http_port);
+    sleep(1);
+		execlp ("../spdy2http/microspdy2http", "microspdy2http", "-v4rtT", "10", "-p", port_s, NULL);
+		fprintf(stderr, "executing microspdy2http failed\n");
+    //killchildren();
+    _exit(1);
+	}
+
+	child_mhd2spdy = au_fork();
+	if (child_mhd2spdy == 0)
+	{
+    //run MHD2sdpy
+		pid_t devnull;
+    char *port_s;
+    char *url;
+
+		close(1);
+		devnull = open("/dev/null", O_WRONLY);
+                if (-1 == devnull)
+                  abort();
+		if (1 != devnull)
+		{
+			dup2(devnull, 1);
+			close(devnull);
+		}
+    asprintf(&url, "http://127.0.0.1:%i", spdy2http_port);
+    asprintf(&port_s, "%i", mhd2spdy_port);
+    sleep(2);
+		execlp ("../examples/mhd2spdy", "mhd2spdy", "-vosb", url, "-p", port_s, NULL);
+		fprintf(stderr, "executing mhd2spdy failed\n");
+    //killchildren();
+    _exit(1);
+	}
+
+	child_curl = au_fork();
+	if (child_curl == 0)
+	{
+    //run curl
+    FILE *p;
+		pid_t devnull;
+		char *cmd;
+    unsigned int i;
+    int retc;
+    char buf[strlen(EXPECTED_BODY) + 1];
+
+		close(1);
+		devnull = open("/dev/null", O_WRONLY);
+                if (-1 == devnull)
+                  abort ();
+		if (1 != devnull)
+		{
+			dup2(devnull, 1);
+			close(devnull);
+		}
+
+		asprintf (&cmd, "curl --proxy http://127.0.0.1:%i http://127.0.0.1:%i/", mhd2spdy_port, mhd_port);
+    sleep(3);
+    p = popen(cmd, "r");
+    if (p != NULL)
+    {
+      for (i = 0; i < strlen(EXPECTED_BODY) && !feof(p); i++)
+      {
+          retc = fgetc (p);
+          if (EOF == retc)
+            abort (); /* what did feof(p) do there!? */
+          buf[i] = (char) retc;
+      }
+
+      pclose(p);
+      buf[i] = 0;
+      _exit(strcmp(EXPECTED_BODY, buf));
+    }
+		fprintf(stderr, "executing curl failed\n");
+    //killchildren();
+    _exit(1);
+	}
+
+  do
+  {
+    wpid = waitpid(child_mhd,&childstatus,WNOHANG);
+    if(wpid == child_mhd)
+    {
+      fprintf(stderr, "mhd died unexpectedly\n");
+      killchildren();
+      return 1;
+    }
+
+    wpid = waitpid(child_spdy2http,&childstatus,WNOHANG);
+    if(wpid == child_spdy2http)
+    {
+      fprintf(stderr, "spdy2http died unexpectedly\n");
+      killchildren();
+      return 1;
+    }
+
+    wpid = waitpid(child_mhd2spdy,&childstatus,WNOHANG);
+    if(wpid == child_mhd2spdy)
+    {
+      fprintf(stderr, "mhd2spdy died unexpectedly\n");
+      killchildren();
+      return 1;
+    }
+
+    if(waitpid(child_curl,&childstatus,WNOHANG) == child_curl)
+    {
+      killchildren();
+      return WEXITSTATUS(childstatus);
+    }
+    sleep(1);
+  }
+	while(true);
+}
diff --git a/src/testspdy/test_request_response.c b/src/testspdy/test_request_response.c
new file mode 100644
index 0000000..093da03
--- /dev/null
+++ b/src/testspdy/test_request_response.c
@@ -0,0 +1,1029 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file request_response.c
+ * @brief  tests receiving request and sending response. spdycli.c (spdylay)
+ * 			code is reused here
+ * @author Andrey Uzunov
+ * @author Tatsuhiro Tsujikawa
+ */
+
+#include "platform.h"
+#include "microspdy.h"
+#include <sys/wait.h>
+#include "common.h"
+
+#define RESPONSE_BODY "<html><body><b>Hi, this is libmicrospdy!</b></body></html>"
+
+#define CLS "anything"
+
+pid_t parent;
+pid_t child;
+char *rcvbuf;
+int rcvbuf_c = 0;
+
+int session_closed_called = 0;
+
+void
+killchild(int pid, char *message)
+{
+	printf("%s\n",message);
+	kill(pid, SIGKILL);
+	exit(1);
+}
+
+void
+killparent(int pid, char *message)
+{
+	printf("%s\n",message);
+	kill(pid, SIGKILL);
+	_exit(1);
+}
+
+
+/*****
+ * start of code needed to utilize spdylay
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <spdylay/spdylay.h>
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+enum {
+  IO_NONE,
+  WANT_READ,
+  WANT_WRITE
+};
+
+struct Connection {
+  SSL *ssl;
+  spdylay_session *session;
+  /* WANT_READ if SSL connection needs more input; or WANT_WRITE if it
+     needs more output; or IO_NONE. This is necessary because SSL/TLS
+     re-negotiation is possible at any time. Spdylay API offers
+     similar functions like spdylay_session_want_read() and
+     spdylay_session_want_write() but they do not take into account
+     SSL connection. */
+  int want_io;
+};
+
+struct Request {
+  char *host;
+  uint16_t port;
+  /* In this program, path contains query component as well. */
+  char *path;
+  /* This is the concatenation of host and port with ":" in
+     between. */
+  char *hostport;
+  /* Stream ID for this request. */
+  int32_t stream_id;
+  /* The gzip stream inflater for the compressed response. */
+  spdylay_gzip *inflater;
+};
+
+struct URI {
+  const char *host;
+  size_t hostlen;
+  uint16_t port;
+  /* In this program, path contains query component as well. */
+  const char *path;
+  size_t pathlen;
+  const char *hostport;
+  size_t hostportlen;
+};
+
+/*
+ * Returns copy of string |s| with the length |len|. The returned
+ * string is NULL-terminated.
+ */
+static char* strcopy(const char *s, size_t len)
+{
+  char *dst;
+  dst = malloc(len+1);
+  if (NULL == dst)
+    abort ();
+  memcpy(dst, s, len);
+  dst[len] = '\0';
+  return dst;
+}
+
+/*
+ * Prints error message |msg| and exit.
+ */
+static void die(const char *msg)
+{
+  fprintf(stderr, "FATAL: %s\n", msg);
+  exit(EXIT_FAILURE);
+}
+
+/*
+ * Prints error containing the function name |func| and message |msg|
+ * and exit.
+ */
+static void dief(const char *func, const char *msg)
+{
+  fprintf(stderr, "FATAL: %s: %s\n", func, msg);
+  exit(EXIT_FAILURE);
+}
+
+/*
+ * Prints error containing the function name |func| and error code
+ * |error_code| and exit.
+ */
+static void diec(const char *func, int error_code)
+{
+  fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
+          spdylay_strerror(error_code));
+  exit(EXIT_FAILURE);
+}
+
+/*
+ * Check response is content-encoding: gzip. We need this because SPDY
+ * client is required to support gzip.
+ */
+static void check_gzip(struct Request *req, char **nv)
+{
+  int gzip = 0;
+  size_t i;
+  for(i = 0; nv[i]; i += 2) {
+    if(strcmp("content-encoding", nv[i]) == 0) {
+      gzip = strcmp("gzip", nv[i+1]) == 0;
+      break;
+    }
+  }
+  if(gzip) {
+    int rv;
+    if(req->inflater) {
+      return;
+    }
+    rv = spdylay_gzip_inflate_new(&req->inflater);
+    if(rv != 0) {
+      die("Can't allocate inflate stream.");
+    }
+  }
+}
+
+/*
+ * The implementation of spdylay_send_callback type. Here we write
+ * |data| with size |length| to the network and return the number of
+ * bytes actually written. See the documentation of
+ * spdylay_send_callback for the details.
+ */
+static ssize_t send_callback(spdylay_session *session,
+                             const uint8_t *data, size_t length, int flags,
+                             void *user_data)
+{
+  (void)session;
+  (void)flags;
+
+  struct Connection *connection;
+  ssize_t rv;
+  connection = (struct Connection*)user_data;
+  connection->want_io = IO_NONE;
+  ERR_clear_error();
+  rv = SSL_write(connection->ssl, data, length);
+  if(rv < 0) {
+    int err = SSL_get_error(connection->ssl, rv);
+    if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
+      connection->want_io = (err == SSL_ERROR_WANT_READ ?
+                             WANT_READ : WANT_WRITE);
+      rv = SPDYLAY_ERR_WOULDBLOCK;
+    } else {
+      rv = SPDYLAY_ERR_CALLBACK_FAILURE;
+    }
+  }
+  return rv;
+}
+
+/*
+ * The implementation of spdylay_recv_callback type. Here we read data
+ * from the network and write them in |buf|. The capacity of |buf| is
+ * |length| bytes. Returns the number of bytes stored in |buf|. See
+ * the documentation of spdylay_recv_callback for the details.
+ */
+static ssize_t recv_callback(spdylay_session *session,
+                             uint8_t *buf, size_t length, int flags,
+                             void *user_data)
+{
+  (void)session;
+  (void)flags;
+
+  struct Connection *connection;
+  ssize_t rv;
+  connection = (struct Connection*)user_data;
+  connection->want_io = IO_NONE;
+  ERR_clear_error();
+  rv = SSL_read(connection->ssl, buf, length);
+  if(rv < 0) {
+    int err = SSL_get_error(connection->ssl, rv);
+    if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
+      connection->want_io = (err == SSL_ERROR_WANT_READ ?
+                             WANT_READ : WANT_WRITE);
+      rv = SPDYLAY_ERR_WOULDBLOCK;
+    } else {
+      rv = SPDYLAY_ERR_CALLBACK_FAILURE;
+    }
+  } else if(rv == 0) {
+    rv = SPDYLAY_ERR_EOF;
+  }
+  return rv;
+}
+
+/*
+ * The implementation of spdylay_before_ctrl_send_callback type.  We
+ * use this function to get stream ID of the request. This is because
+ * stream ID is not known when we submit the request
+ * (spdylay_submit_request).
+ */
+static void before_ctrl_send_callback(spdylay_session *session,
+                                      spdylay_frame_type type,
+                                      spdylay_frame *frame,
+                                      void *user_data)
+{
+  (void)user_data;
+
+  if(type == SPDYLAY_SYN_STREAM) {
+    struct Request *req;
+    int stream_id = frame->syn_stream.stream_id;
+    req = spdylay_session_get_stream_user_data(session, stream_id);
+    if(req && req->stream_id == -1) {
+      req->stream_id = stream_id;
+      printf("[INFO] Stream ID = %d\n", stream_id);
+    }
+  }
+}
+
+static void on_ctrl_send_callback(spdylay_session *session,
+                                  spdylay_frame_type type,
+                                  spdylay_frame *frame, void *user_data)
+{
+  (void)user_data;
+
+  char **nv;
+  const char *name = NULL;
+  int32_t stream_id;
+  size_t i;
+  switch(type) {
+  case SPDYLAY_SYN_STREAM:
+    nv = frame->syn_stream.nv;
+    name = "SYN_STREAM";
+    stream_id = frame->syn_stream.stream_id;
+    break;
+  default:
+    break;
+  }
+  if(name && spdylay_session_get_stream_user_data(session, stream_id)) {
+    printf("[INFO] C ----------------------------> S (%s)\n", name);
+    for(i = 0; nv[i]; i += 2) {
+      printf("       %s: %s\n", nv[i], nv[i+1]);
+    }
+  }
+}
+
+static void on_ctrl_recv_callback(spdylay_session *session,
+                                  spdylay_frame_type type,
+                                  spdylay_frame *frame, void *user_data)
+{
+  (void)user_data;
+
+  struct Request *req;
+  char **nv;
+  const char *name = NULL;
+  int32_t stream_id;
+  size_t i;
+  switch(type) {
+  case SPDYLAY_SYN_REPLY:
+    nv = frame->syn_reply.nv;
+    name = "SYN_REPLY";
+    stream_id = frame->syn_reply.stream_id;
+    break;
+  case SPDYLAY_HEADERS:
+    nv = frame->headers.nv;
+    name = "HEADERS";
+    stream_id = frame->headers.stream_id;
+    break;
+  default:
+    break;
+  }
+  if(!name) {
+    return;
+  }
+  req = spdylay_session_get_stream_user_data(session, stream_id);
+  if(req) {
+    check_gzip(req, nv);
+    printf("[INFO] C <---------------------------- S (%s)\n", name);
+    for(i = 0; nv[i]; i += 2) {
+      printf("       %s: %s\n", nv[i], nv[i+1]);
+    }
+  }
+}
+
+/*
+ * The implementation of spdylay_on_stream_close_callback type. We use
+ * this function to know the response is fully received. Since we just
+ * fetch 1 resource in this program, after reception of the response,
+ * we submit GOAWAY and close the session.
+ */
+static void on_stream_close_callback(spdylay_session *session,
+                                     int32_t stream_id,
+                                     spdylay_status_code status_code,
+                                     void *user_data)
+{
+  (void)user_data;
+  (void)status_code;
+
+  struct Request *req;
+  req = spdylay_session_get_stream_user_data(session, stream_id);
+  if(req) {
+    int rv;
+    rv = spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);
+    if(rv != 0) {
+      diec("spdylay_submit_goaway", rv);
+    }
+  }
+}
+
+#define MAX_OUTLEN 4096
+
+/*
+ * The implementation of spdylay_on_data_chunk_recv_callback type. We
+ * use this function to print the received response body.
+ */
+static void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags,
+                                        int32_t stream_id,
+                                        const uint8_t *data, size_t len,
+                                        void *user_data)
+{
+  (void)user_data;
+  (void)flags;
+
+  struct Request *req;
+  req = spdylay_session_get_stream_user_data(session, stream_id);
+  if(req) {
+    printf("[INFO] C <---------------------------- S (DATA)\n");
+    printf("       %lu bytes\n", (unsigned long int)len);
+    if(req->inflater) {
+      while(len > 0) {
+        uint8_t out[MAX_OUTLEN];
+        size_t outlen = MAX_OUTLEN;
+        size_t tlen = len;
+        int rv;
+        rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen);
+        if(rv == -1) {
+          spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR);
+          break;
+        }
+        fwrite(out, 1, outlen, stdout);
+        data += tlen;
+        len -= tlen;
+      }
+    } else {
+      /* TODO add support gzip */
+      fwrite(data, 1, len, stdout);
+
+      //check if the data is correct
+      //if(strcmp(RESPONSE_BODY, data) != 0)
+		//killparent(parent, "\nreceived data is not the same");
+      if(len + rcvbuf_c > strlen(RESPONSE_BODY))
+		killparent(parent, "\nreceived data is not the same");
+
+		strcpy(rcvbuf + rcvbuf_c,(char*)data);
+		rcvbuf_c+=len;
+    }
+    printf("\n");
+  }
+}
+
+/*
+ * Setup callback functions. Spdylay API offers many callback
+ * functions, but most of them are optional. The send_callback is
+ * always required. Since we use spdylay_session_recv(), the
+ * recv_callback is also required.
+ */
+static void setup_spdylay_callbacks(spdylay_session_callbacks *callbacks)
+{
+  memset(callbacks, 0, sizeof(spdylay_session_callbacks));
+  callbacks->send_callback = send_callback;
+  callbacks->recv_callback = recv_callback;
+  callbacks->before_ctrl_send_callback = before_ctrl_send_callback;
+  callbacks->on_ctrl_send_callback = on_ctrl_send_callback;
+  callbacks->on_ctrl_recv_callback = on_ctrl_recv_callback;
+  callbacks->on_stream_close_callback = on_stream_close_callback;
+  callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback;
+}
+
+/*
+ * Callback function for SSL/TLS NPN. Since this program only supports
+ * SPDY protocol, if server does not offer SPDY protocol the Spdylay
+ * library supports, we terminate program.
+ */
+static int select_next_proto_cb(SSL* ssl,
+                                unsigned char **out, unsigned char *outlen,
+                                const unsigned char *in, unsigned int inlen,
+                                void *arg)
+{
+  (void)ssl;
+
+  int rv;
+  uint16_t *spdy_proto_version;
+  /* spdylay_select_next_protocol() selects SPDY protocol version the
+     Spdylay library supports. */
+  rv = spdylay_select_next_protocol(out, outlen, in, inlen);
+  if(rv <= 0) {
+    die("Server did not advertise spdy/2 or spdy/3 protocol.");
+  }
+  spdy_proto_version = (uint16_t*)arg;
+  *spdy_proto_version = rv;
+  return SSL_TLSEXT_ERR_OK;
+}
+
+/*
+ * Setup SSL context. We pass |spdy_proto_version| to get negotiated
+ * SPDY protocol version in NPN callback.
+ */
+static void init_ssl_ctx(SSL_CTX *ssl_ctx, uint16_t *spdy_proto_version)
+{
+  /* Disable SSLv2 and enable all workarounds for buggy servers */
+  SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
+  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
+  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
+  /* Set NPN callback */
+  SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb,
+                                   spdy_proto_version);
+}
+
+static void ssl_handshake(SSL *ssl, int fd)
+{
+  int rv;
+  if(SSL_set_fd(ssl, fd) == 0) {
+    dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
+  }
+  ERR_clear_error();
+  rv = SSL_connect(ssl);
+  if(rv <= 0) {
+    dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL));
+  }
+}
+
+/*
+ * Connects to the host |host| and port |port|.  This function returns
+ * the file descriptor of the client socket.
+ */
+static int connect_to(const char *host, uint16_t port)
+{
+  struct addrinfo hints;
+  int fd = -1;
+  int rv;
+  char service[NI_MAXSERV];
+  struct addrinfo *res, *rp;
+  snprintf(service, sizeof(service), "%u", port);
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  rv = getaddrinfo(host, service, &hints, &res);
+  if(rv != 0) {
+    dief("getaddrinfo", gai_strerror(rv));
+  }
+  for(rp = res; rp; rp = rp->ai_next) {
+    fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+    if(fd == -1) {
+      continue;
+    }
+    while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
+          errno == EINTR);
+    if(rv == 0) {
+      break;
+    }
+    close(fd);
+    fd = -1;
+  }
+  freeaddrinfo(res);
+  return fd;
+}
+
+static void make_non_block(int fd)
+{
+  int flags, rv;
+  while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);
+  if(flags == -1) {
+    dief("fcntl", strerror(errno));
+  }
+  while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);
+  if(rv == -1) {
+    dief("fcntl", strerror(errno));
+  }
+}
+
+/*
+ * Setting TCP_NODELAY is not mandatory for the SPDY protocol.
+ */
+static void set_tcp_nodelay(int fd)
+{
+  int val = 1;
+  int rv;
+  rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
+  if(rv == -1) {
+    dief("setsockopt", strerror(errno));
+  }
+}
+
+/*
+ * Update |pollfd| based on the state of |connection|.
+ */
+static void ctl_poll(struct pollfd *pollfd, struct Connection *connection)
+{
+  pollfd->events = 0;
+  if(spdylay_session_want_read(connection->session) ||
+     connection->want_io == WANT_READ) {
+    pollfd->events |= POLLIN;
+  }
+  if(spdylay_session_want_write(connection->session) ||
+     connection->want_io == WANT_WRITE) {
+    pollfd->events |= POLLOUT;
+  }
+}
+
+/*
+ * Submits the request |req| to the connection |connection|.  This
+ * function does not send packets; just append the request to the
+ * internal queue in |connection->session|.
+ */
+static void submit_request(struct Connection *connection, struct Request *req)
+{
+  int pri = 0;
+  int rv;
+  const char *nv[15];
+  /* We always use SPDY/3 style header even if the negotiated protocol
+     version is SPDY/2. The library translates the header name as
+     necessary. Make sure that the last item is NULL! */
+  nv[0] = ":method";     nv[1] = "GET";
+  nv[2] = ":path";       nv[3] = req->path;
+  nv[4] = ":version";    nv[5] = "HTTP/1.1";
+  nv[6] = ":scheme";     nv[7] = "https";
+  nv[8] = ":host";       nv[9] = req->hostport;
+  nv[10] = "accept";     nv[11] = "*/*";
+  nv[12] = "user-agent"; nv[13] = "spdylay/"SPDYLAY_VERSION;
+  nv[14] = NULL;
+  rv = spdylay_submit_request(connection->session, pri, nv, NULL, req);
+  if(rv != 0) {
+    diec("spdylay_submit_request", rv);
+  }
+}
+
+/*
+ * Performs the network I/O.
+ */
+static void exec_io(struct Connection *connection)
+{
+  int rv;
+  rv = spdylay_session_recv(connection->session);
+  if(rv != 0) {
+    diec("spdylay_session_recv", rv);
+  }
+  rv = spdylay_session_send(connection->session);
+  if(rv != 0) {
+    diec("spdylay_session_send", rv);
+  }
+}
+
+static void request_init(struct Request *req, const struct URI *uri)
+{
+  req->host = strcopy(uri->host, uri->hostlen);
+  req->port = uri->port;
+  req->path = strcopy(uri->path, uri->pathlen);
+  req->hostport = strcopy(uri->hostport, uri->hostportlen);
+  req->stream_id = -1;
+  req->inflater = NULL;
+}
+
+static void request_free(struct Request *req)
+{
+  free(req->host);
+  free(req->path);
+  free(req->hostport);
+  spdylay_gzip_inflate_del(req->inflater);
+}
+
+/*
+ * Fetches the resource denoted by |uri|.
+ */
+static void fetch_uri(const struct URI *uri)
+{
+  spdylay_session_callbacks callbacks;
+  int fd;
+  SSL_CTX *ssl_ctx;
+  SSL *ssl;
+  struct Request req;
+  struct Connection connection;
+  int rv;
+  nfds_t npollfds = 1;
+  struct pollfd pollfds[1];
+  uint16_t spdy_proto_version;
+
+  request_init(&req, uri);
+
+  setup_spdylay_callbacks(&callbacks);
+
+  /* Establish connection and setup SSL */
+  fd = connect_to(req.host, req.port);
+  if (-1 == fd)
+    abort ();
+  ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+  if(ssl_ctx == NULL) {
+    dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL));
+  }
+  init_ssl_ctx(ssl_ctx, &spdy_proto_version);
+  ssl = SSL_new(ssl_ctx);
+  if(ssl == NULL) {
+    dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
+  }
+  /* To simplify the program, we perform SSL/TLS handshake in blocking
+     I/O. */
+  ssl_handshake(ssl, fd);
+
+  connection.ssl = ssl;
+  connection.want_io = IO_NONE;
+
+  /* Here make file descriptor non-block */
+  make_non_block(fd);
+  set_tcp_nodelay(fd);
+
+  printf("[INFO] SPDY protocol version = %d\n", spdy_proto_version);
+  rv = spdylay_session_client_new(&connection.session, spdy_proto_version,
+                                  &callbacks, &connection);
+  if(rv != 0) {
+    diec("spdylay_session_client_new", rv);
+  }
+
+  /* Submit the HTTP request to the outbound queue. */
+  submit_request(&connection, &req);
+
+  pollfds[0].fd = fd;
+  ctl_poll(pollfds, &connection);
+
+  /* Event loop */
+  while(spdylay_session_want_read(connection.session) ||
+        spdylay_session_want_write(connection.session)) {
+    int nfds = poll(pollfds, npollfds, -1);
+    if(nfds == -1) {
+      dief("poll", strerror(errno));
+    }
+    if(pollfds[0].revents & (POLLIN | POLLOUT)) {
+      exec_io(&connection);
+    }
+    if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
+      die("Connection error");
+    }
+    ctl_poll(pollfds, &connection);
+  }
+
+  /* Resource cleanup */
+  spdylay_session_del(connection.session);
+  SSL_shutdown(ssl);
+  SSL_free(ssl);
+  SSL_CTX_free(ssl_ctx);
+  shutdown(fd, SHUT_WR);
+  close(fd);
+  request_free(&req);
+}
+
+static int parse_uri(struct URI *res, const char *uri)
+{
+  /* We only interested in https */
+  size_t len, i, offset;
+  memset(res, 0, sizeof(struct URI));
+  len = strlen(uri);
+  if(len < 9 || memcmp("https://", uri, 8) != 0) {
+    return -1;
+  }
+  offset = 8;
+  res->host = res->hostport = &uri[offset];
+  res->hostlen = 0;
+  if(uri[offset] == '[') {
+    /* IPv6 literal address */
+    ++offset;
+    ++res->host;
+    for(i = offset; i < len; ++i) {
+      if(uri[i] == ']') {
+        res->hostlen = i-offset;
+        offset = i+1;
+        break;
+      }
+    }
+  } else {
+    const char delims[] = ":/?#";
+    for(i = offset; i < len; ++i) {
+      if(strchr(delims, uri[i]) != NULL) {
+        break;
+      }
+    }
+    res->hostlen = i-offset;
+    offset = i;
+  }
+  if(res->hostlen == 0) {
+    return -1;
+  }
+  /* Assuming https */
+  res->port = 443;
+  if(offset < len) {
+    if(uri[offset] == ':') {
+      /* port */
+      const char delims[] = "/?#";
+      int port = 0;
+      ++offset;
+      for(i = offset; i < len; ++i) {
+        if(strchr(delims, uri[i]) != NULL) {
+          break;
+        }
+        if('0' <= uri[i] && uri[i] <= '9') {
+          port *= 10;
+          port += uri[i]-'0';
+          if(port > 65535) {
+            return -1;
+          }
+        } else {
+          return -1;
+        }
+      }
+      if(port == 0) {
+        return -1;
+      }
+      offset = i;
+      res->port = port;
+    }
+  }
+  res->hostportlen = uri+offset-res->host;
+  for(i = offset; i < len; ++i) {
+    if(uri[i] == '#') {
+      break;
+    }
+  }
+  if(i-offset == 0) {
+    res->path = "/";
+    res->pathlen = 1;
+  } else {
+    res->path = &uri[offset];
+    res->pathlen = i-offset;
+  }
+  return 0;
+}
+
+
+/*****
+ * end of code needed to utilize spdylay
+ */
+
+
+/*****
+ * start of code needed to utilize microspdy
+ */
+
+
+void
+standard_request_handler(void *cls,
+						struct SPDY_Request * request,
+						uint8_t priority,
+                        const char *method,
+                        const char *path,
+                        const char *version,
+                        const char *host,
+                        const char *scheme,
+						struct SPDY_NameValue * headers,
+            bool more)
+{
+	(void)cls;
+	(void)request;
+	(void)priority;
+	(void)host;
+	(void)scheme;
+	(void)headers;
+	(void)method;
+	(void)version;
+
+	struct SPDY_Response *response=NULL;
+
+	if(strcmp(CLS,cls)!=0)
+	{
+		killchild(child,"wrong cls");
+	}
+
+	if(false != more){
+		fprintf(stdout,"more has wrong value\n");
+		exit(5);
+	}
+
+	response = SPDY_build_response(200,NULL,SPDY_HTTP_VERSION_1_1,NULL,RESPONSE_BODY,strlen(RESPONSE_BODY));
+
+	if(NULL==response){
+		fprintf(stdout,"no response obj\n");
+		exit(3);
+	}
+
+	if(SPDY_queue_response(request,response,true,false,NULL,(void*)strdup(path))!=SPDY_YES)
+	{
+		fprintf(stdout,"queue\n");
+		exit(4);
+	}
+}
+
+void
+session_closed_handler (void *cls,
+						struct SPDY_Session * session,
+						int by_client)
+{
+	printf("session_closed_handler called\n");
+
+	if(strcmp(CLS,cls)!=0)
+	{
+		killchild(child,"wrong cls");
+	}
+
+	if(SPDY_YES != by_client)
+	{
+		//killchild(child,"wrong by_client");
+		printf("session closed by server\n");
+	}
+	else
+	{
+		printf("session closed by client\n");
+	}
+
+	if(NULL == session)
+	{
+		killchild(child,"session is NULL");
+	}
+
+	session_closed_called = 1;
+}
+
+
+/*****
+ * end of code needed to utilize microspdy
+ */
+
+//child process
+void
+childproc(int port)
+{
+  struct URI uri;
+  struct sigaction act;
+  int rv;
+  char *uristr;
+
+  memset(&act, 0, sizeof(struct sigaction));
+  act.sa_handler = SIG_IGN;
+  sigaction(SIGPIPE, &act, 0);
+
+	asprintf(&uristr, "https://127.0.0.1:%i/",port);
+	if(NULL == (rcvbuf = malloc(strlen(RESPONSE_BODY)+1)))
+		killparent(parent,"no memory");
+
+  SSL_load_error_strings();
+  SSL_library_init();
+
+  rv = parse_uri(&uri, uristr);
+  if(rv != 0) {
+    killparent(parent,"parse_uri failed");
+  }
+  fetch_uri(&uri);
+
+  if(strcmp(rcvbuf, RESPONSE_BODY))
+    killparent(parent,"received data is different");
+}
+
+//parent proc
+int
+parentproc( int port)
+{
+	int childstatus;
+	unsigned long long timeoutlong=0;
+	struct timeval timeout;
+	int ret;
+	fd_set read_fd_set;
+	fd_set write_fd_set;
+	fd_set except_fd_set;
+	int maxfd = -1;
+	struct SPDY_Daemon *daemon;
+
+	SPDY_init();
+
+	daemon = SPDY_start_daemon(port,
+								DATA_DIR "cert-and-key.pem",
+								DATA_DIR "cert-and-key.pem",
+								NULL,&session_closed_handler,&standard_request_handler,NULL,CLS,SPDY_DAEMON_OPTION_END);
+
+	if(NULL==daemon){
+		printf("no daemon\n");
+		return 1;
+	}
+
+	do
+	{
+		FD_ZERO(&read_fd_set);
+		FD_ZERO(&write_fd_set);
+		FD_ZERO(&except_fd_set);
+
+		ret = SPDY_get_timeout(daemon, &timeoutlong);
+		if(SPDY_NO == ret || timeoutlong > 1000)
+		{
+			timeout.tv_sec = 1;
+      timeout.tv_usec = 0;
+		}
+		else
+		{
+			timeout.tv_sec = timeoutlong / 1000;
+			timeout.tv_usec = (timeoutlong % 1000) * 1000;
+		}
+
+		maxfd = SPDY_get_fdset (daemon,
+								&read_fd_set,
+								&write_fd_set,
+								&except_fd_set);
+
+		ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
+
+		switch(ret) {
+			case -1:
+				printf("select error: %i\n", errno);
+				killchild(child, "select error");
+				break;
+			case 0:
+
+				break;
+			default:
+				SPDY_run(daemon);
+
+			break;
+		}
+	}
+	while(waitpid(child,&childstatus,WNOHANG) != child);
+
+	//give chance to the client to close socket and handle this in run
+	usleep(100000);
+	SPDY_run(daemon);
+
+	SPDY_stop_daemon(daemon);
+
+	SPDY_deinit();
+
+	return WEXITSTATUS(childstatus);
+}
+
+int main()
+{
+	int port = get_port(12123);
+	parent = getpid();
+
+   child = fork();
+   if (child == -1)
+   {
+      fprintf(stderr, "can't fork, error %d\n", errno);
+      exit(EXIT_FAILURE);
+   }
+
+   if (child == 0)
+   {
+      childproc(port);
+      _exit(0);
+   }
+   else
+   {
+	   int ret = parentproc(port);
+	   if(1 == session_closed_called && 0 == ret)
+      exit(0);
+      else
+      exit(ret ? ret : 21);
+   }
+   return 1;
+}
diff --git a/src/testspdy/test_request_response_with_callback.c b/src/testspdy/test_request_response_with_callback.c
new file mode 100644
index 0000000..95fc263
--- /dev/null
+++ b/src/testspdy/test_request_response_with_callback.c
@@ -0,0 +1,320 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file request_response_with_callback.c
+ * @brief  tests responses with callbacks
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include "microspdy.h"
+#include "stdio.h"
+#include <sys/wait.h>
+#include <ctype.h>
+#include "common.h"
+#include <sys/time.h>
+#include <sys/stat.h>
+
+int port;
+
+pid_t parent;
+pid_t child;
+
+int run = 1;
+int chunk_size=1;
+
+void
+killchild()
+{
+	kill(child, SIGKILL);
+	exit(1);
+}
+
+void
+killparent()
+{
+	kill(parent, SIGKILL);
+	_exit(1);
+}
+
+ssize_t
+response_callback (void *cls,
+						void *buffer,
+						size_t max,
+						bool *more)
+{
+	FILE *fd =(FILE*)cls;
+	
+	size_t n;
+	if(chunk_size % 2)
+		n = chunk_size;
+	else
+		n = max - chunk_size;
+	
+	if(n < 1) n = 1;
+	else if (n > max) n=max;
+	chunk_size++;
+	
+	int ret = fread(buffer,1,n,fd);
+	*more = feof(fd) == 0;
+	
+	//printf("more is %i\n",*more);
+	
+	if(!(*more))
+		fclose(fd);
+	
+	return ret;
+}
+
+
+void
+response_done_callback(void *cls,
+								struct SPDY_Response * response,
+								struct SPDY_Request * request,
+								enum SPDY_RESPONSE_RESULT status,
+						bool streamopened)
+{
+  (void)status;
+  (void)streamopened;
+  
+	printf("answer for %s was sent\n", (char*)cls);
+	
+	SPDY_destroy_request(request);
+	SPDY_destroy_response(response);
+	free(cls);
+	
+	run = 0;
+}
+
+void
+standard_request_handler(void *cls,
+						struct SPDY_Request * request,
+						uint8_t priority,
+                        const char *method,
+                        const char *path,
+                        const char *version,
+                        const char *host,
+                        const char *scheme,
+						struct SPDY_NameValue * headers,
+            bool more)
+{
+	(void)cls;
+	(void)request;
+	(void)priority;
+	(void)host;
+	(void)scheme;
+	(void)headers;
+	(void)method;
+	(void)version;
+	(void)more;
+  
+	struct SPDY_Response *response=NULL;
+	struct SPDY_NameValue *resp_headers;
+	
+	printf("received request for '%s %s %s'\n", method, path, version);
+	
+		FILE *fd = fopen(DATA_DIR "spdy-draft.txt","r");
+		
+		if(NULL == (resp_headers = SPDY_name_value_create()))
+		{
+			fprintf(stdout,"SPDY_name_value_create failed\n");
+			killchild();
+		}
+		if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_TYPE,"text/plain"))
+		{
+			fprintf(stdout,"SPDY_name_value_add failed\n");
+			killchild();
+		}
+		
+		response = SPDY_build_response_with_callback(200,NULL,
+			SPDY_HTTP_VERSION_1_1,resp_headers,&response_callback,fd,SPDY_MAX_SUPPORTED_FRAME_SIZE);
+		SPDY_name_value_destroy(resp_headers);
+	
+	if(NULL==response){
+		fprintf(stdout,"no response obj\n");
+			killchild();
+	}
+	
+	void *clspath = strdup(path);
+	
+	if(SPDY_queue_response(request,response,true,false,&response_done_callback,clspath)!=SPDY_YES)
+	{
+		fprintf(stdout,"queue\n");
+			killchild();
+	}
+}
+
+int
+parentproc()
+{	
+	int childstatus;
+	unsigned long long timeoutlong=0;
+	struct timeval timeout;
+	int ret;
+	fd_set read_fd_set;
+	fd_set write_fd_set;
+	fd_set except_fd_set;
+	int maxfd = -1;
+	struct SPDY_Daemon *daemon;
+	
+	SPDY_init();
+	
+	daemon = SPDY_start_daemon(port,
+								DATA_DIR "cert-and-key.pem",
+								DATA_DIR "cert-and-key.pem",
+								NULL,
+								NULL,
+								&standard_request_handler,
+								NULL,
+								NULL,
+								SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
+								1800,
+								SPDY_DAEMON_OPTION_END);
+	
+	if(NULL==daemon){
+		printf("no daemon\n");
+		return 1;
+	}
+
+	do
+	{
+		FD_ZERO(&read_fd_set);
+		FD_ZERO(&write_fd_set);
+		FD_ZERO(&except_fd_set);
+
+		ret = SPDY_get_timeout(daemon, &timeoutlong);
+		if(SPDY_NO == ret || timeoutlong > 1000)
+		{
+			timeout.tv_sec = 1;
+      timeout.tv_usec = 0;
+		}
+		else
+		{
+			timeout.tv_sec = timeoutlong / 1000;
+			timeout.tv_usec = (timeoutlong % 1000) * 1000;
+		}
+		
+		maxfd = SPDY_get_fdset (daemon,
+								&read_fd_set,
+								&write_fd_set, 
+								&except_fd_set);
+								
+		ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
+		
+		switch(ret) {
+			case -1:
+				printf("select error: %i\n", errno);
+				break;
+			case 0:
+
+				break;
+			default:
+				SPDY_run(daemon);
+
+			break;
+		}
+	}
+	while(waitpid(child,&childstatus,WNOHANG) != child);
+
+	SPDY_stop_daemon(daemon);
+	
+	SPDY_deinit();
+	
+	return WEXITSTATUS(childstatus);
+}
+
+#define MD5_LEN 32
+
+int
+md5(char *cmd, char *md5_sum)
+{
+    FILE *p = popen(cmd, "r");
+    if (p == NULL) return 0;
+
+    int i, ch;
+    for (i = 0; i < MD5_LEN && isxdigit(ch = fgetc(p)); i++) {
+        *md5_sum++ = ch;
+    }
+
+    *md5_sum = '\0';
+    pclose(p);
+    return i == MD5_LEN;
+}
+
+int
+childproc()
+{
+	char *cmd1;
+	char *cmd2;
+	char md5_sum1[33];
+	char md5_sum2[33];
+	int ret;
+	struct timeval tv1;
+	struct timeval tv2;
+	struct stat st;
+	//int secs;
+	uint64_t usecs;
+	
+	asprintf(&cmd1, "spdycat https://127.0.0.1:%i/ | md5sum",port);
+	asprintf(&cmd2, "md5sum " DATA_DIR "spdy-draft.txt");
+	
+	gettimeofday(&tv1, NULL);
+	md5(cmd1,md5_sum1);
+	gettimeofday(&tv2, NULL);
+	md5(cmd2,md5_sum2);
+	
+	printf("downloaded file md5: %s\n", md5_sum1);
+	printf("original   file md5: %s\n", md5_sum2);
+	ret = strcmp(md5_sum1, md5_sum2);
+	
+	if(0 == ret && 0 == stat(DATA_DIR "spdy-draft.txt", &st))
+	{
+		usecs = (uint64_t)1000000 * (uint64_t)(tv2.tv_sec - tv1.tv_sec) + tv2.tv_usec - tv1.tv_usec;
+		printf("%lld bytes read in %llu usecs\n", (long long)st.st_size, (long long unsigned )usecs);
+	}
+	
+	return ret;
+}
+
+
+int
+main()
+{
+	port = get_port(11123);
+	parent = getpid();
+
+	child = fork();
+	if (-1 == child)
+	{   
+		fprintf(stderr, "can't fork, error %d\n", errno);
+		exit(EXIT_FAILURE);
+	}
+
+	if (child == 0)
+	{
+		int ret = childproc();
+		_exit(ret);
+	}
+	else
+	{ 
+		int ret = parentproc();
+		exit(ret);
+	}
+	return 1;
+}
diff --git a/src/testspdy/test_session_timeout.c b/src/testspdy/test_session_timeout.c
new file mode 100644
index 0000000..ec1eced
--- /dev/null
+++ b/src/testspdy/test_session_timeout.c
@@ -0,0 +1,338 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2013 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file session_timeout.c
+ * @brief  tests closing sessions after set timeout. Openssl is used for
+ * 			client
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include "microspdy.h"
+#include "stdio.h"
+#include <sys/wait.h>
+#include <ctype.h>
+#include "common.h"
+#include <sys/time.h>
+#include <sys/stat.h>
+#include "../microspdy/internal.h"
+
+#define TIMEOUT 2
+#define SELECT_MS_TIMEOUT 20
+
+int port;
+
+pid_t parent;
+pid_t child;
+
+int run = 1;
+int chunk_size=1;
+int new_session;
+int closed_session;
+int do_sleep;
+
+
+
+static unsigned long long
+monotonic_time (void)
+{
+#ifdef HAVE_CLOCK_GETTIME
+#ifdef CLOCK_MONOTONIC
+  struct timespec ts;
+  if (0 == clock_gettime (CLOCK_MONOTONIC, &ts))
+    return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+#endif
+#endif
+  return time (NULL) * 1000;
+}
+
+
+static void
+killchild(char *msg)
+{
+	printf("%s\n",msg);
+	kill(child, SIGKILL);
+	exit(1);
+}
+
+
+static void
+killparent(char *msg)
+{
+	printf("%s\n",msg);
+	kill(parent, SIGKILL);
+	_exit(1);
+}
+
+
+static void
+new_session_cb (void *cls,
+                struct SPDY_Session * session)
+{
+  (void)cls;
+  (void)session;
+
+	if(!new_session)do_sleep = 1;
+	new_session = 1;
+	printf("new session\n");
+}
+
+
+static void
+closed_session_cb (void *cls,
+                   struct SPDY_Session * session,
+                   int by_client)
+{
+  (void)cls;
+  (void)session;
+
+	printf("closed_session_cb called\n");
+
+	if(SPDY_YES == by_client)
+	{
+		killchild("closed by the client");
+	}
+	if(closed_session)
+	{
+		killchild("closed_session_cb called twice");
+	}
+
+	closed_session = 1;
+}
+
+
+static int
+parentproc()
+{
+	int childstatus;
+	unsigned long long timeoutlong=0;
+	struct timeval timeout;
+	int ret;
+	fd_set read_fd_set;
+	fd_set write_fd_set;
+	fd_set except_fd_set;
+	int maxfd = -1;
+	struct SPDY_Daemon *daemon;
+  unsigned long long  beginning = 0;
+	unsigned long long now;
+
+	SPDY_init();
+
+	daemon = SPDY_start_daemon(port,
+								DATA_DIR "cert-and-key.pem",
+								DATA_DIR "cert-and-key.pem",
+								&new_session_cb,
+								&closed_session_cb,
+								NULL,
+								NULL,
+								NULL,
+								SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
+								TIMEOUT,
+								SPDY_DAEMON_OPTION_END);
+
+	if(NULL==daemon){
+		printf("no daemon\n");
+		return 1;
+	}
+
+	do
+	{
+		do_sleep=0;
+		FD_ZERO(&read_fd_set);
+		FD_ZERO(&write_fd_set);
+		FD_ZERO(&except_fd_set);
+
+		ret = SPDY_get_timeout(daemon, &timeoutlong);
+
+		if(new_session && !closed_session)
+		{
+			if(SPDY_NO == ret)
+			{
+				killchild("SPDY_get_timeout returned wrong SPDY_NO");
+			}
+			/*if(timeoutlong)
+			{
+				killchild("SPDY_get_timeout returned wrong timeout");
+			}*/
+			now = monotonic_time ();
+      if(now - beginning > TIMEOUT*1000 + SELECT_MS_TIMEOUT)
+      {
+        printf("Started at: %llums\n",beginning);
+        printf("Now is: %llums\n",now);
+        printf("Timeout is: %i\n",TIMEOUT);
+        printf("Select Timeout is: %ims\n",SELECT_MS_TIMEOUT);
+        printf("SPDY_get_timeout gave: %llums\n",timeoutlong);
+				killchild("Timeout passed but session was not closed");
+      }
+      if(timeoutlong > beginning + TIMEOUT *1000)
+      {
+        printf("Started at: %llums\n",beginning);
+        printf("Now is: %llums\n",now);
+        printf("Timeout is: %i\n",TIMEOUT);
+        printf("Select Timeout is: %ims\n",SELECT_MS_TIMEOUT);
+        printf("SPDY_get_timeout gave: %llums\n",timeoutlong);
+				killchild("SPDY_get_timeout returned wrong timeout");
+      }
+		}
+		else
+		{
+			if(SPDY_YES == ret)
+			{
+				killchild("SPDY_get_timeout returned wrong SPDY_YES");
+			}
+		}
+
+		if(SPDY_NO == ret || timeoutlong >= 1000)
+		{
+			timeout.tv_sec = 1;
+      timeout.tv_usec = 0;
+		}
+		else
+		{
+			timeout.tv_sec = timeoutlong / 1000;
+      timeout.tv_usec = (timeoutlong % 1000) * 1000;
+		}
+
+    //ignore values
+    timeout.tv_sec = 0;
+    timeout.tv_usec = SELECT_MS_TIMEOUT * 1000;
+
+		maxfd = SPDY_get_fdset (daemon,
+								&read_fd_set,
+								&write_fd_set,
+								&except_fd_set);
+
+		ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
+
+		switch(ret) {
+			case -1:
+				printf("select error: %i\n", errno);
+				break;
+			case 0:
+				/*if(new_session)
+				{
+					killchild("select returned wrong number");
+				}*/
+				break;
+			default:
+				SPDY_run(daemon);
+        if(0 == beginning)
+        {
+	  beginning = monotonic_time ();
+        }
+				/*if(do_sleep)
+				{
+					sleep(TIMEOUT);
+					do_sleep = 0;
+				}*/
+			break;
+		}
+	}
+	while(waitpid(child,&childstatus,WNOHANG) != child);
+
+	if(!new_session || !closed_session)
+	{
+		killchild("child is dead, callback wasn't called");
+	}
+
+	ret = SPDY_get_timeout(daemon, &timeoutlong);
+
+	if(SPDY_YES == ret)
+	{
+		killchild("SPDY_get_timeout returned wrong SPDY_YES after child died");
+	}
+
+	SPDY_stop_daemon(daemon);
+
+	SPDY_deinit();
+
+	return 0;
+}
+
+
+static int
+childproc()
+{
+	pid_t devnull;
+	int out;
+
+	out=dup(1);
+        if (-1 == out)
+          abort();
+	//close(0);
+	close(1);
+	close(2);
+	/*devnull = open("/dev/null", O_RDONLY);
+	if (0 != devnull)
+	{
+		dup2(devnull, 0);
+		close(devnull);
+	}*/
+	devnull = open("/dev/null", O_WRONLY);
+        if (-1 == devnull)
+          abort ();
+	if (1 != devnull)
+	{
+		dup2(devnull, 1);
+		close(devnull);
+	}
+	devnull = open("/dev/null", O_WRONLY);
+        if (-1 == devnull)
+          abort ();
+	if (2 != devnull)
+	{
+		dup2(devnull, 2);
+		close(devnull);
+	}
+	char *uri;
+	asprintf (&uri, "127.0.0.1:%i", port);
+	execlp ("openssl", "openssl", "s_client", "-connect", uri, NULL);
+	close(1);
+	dup2(out,1);
+	close(out);
+	killparent ("executing openssl failed");
+	return 1;
+}
+
+
+int
+main()
+{
+	port = get_port(11123);
+	parent = getpid();
+
+	child = fork();
+	if (-1 == child)
+	{
+		fprintf(stderr, "can't fork, error %d\n", errno);
+		exit(EXIT_FAILURE);
+	}
+
+	if (child == 0)
+	{
+		int ret = childproc();
+		_exit(ret);
+	}
+	else
+	{
+		int ret = parentproc();
+		exit(ret);
+	}
+	return 1;
+}
diff --git a/src/testspdy/test_struct_namevalue.c b/src/testspdy/test_struct_namevalue.c
new file mode 100644
index 0000000..54e7934
--- /dev/null
+++ b/src/testspdy/test_struct_namevalue.c
@@ -0,0 +1,346 @@
+/*
+    This file is part of libmicrospdy
+    Copyright Copyright (C) 2012 Andrey Uzunov
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file struct_namevalue.c
+ * @brief  tests all the API functions for handling struct SPDY_NameValue
+ * @author Andrey Uzunov
+ */
+
+#include "platform.h"
+#include "microspdy.h"
+#include "common.h"
+#include "../microspdy/structures.h"
+#include "../microspdy/alstructures.h"
+
+char *pairs[] = {"one","1","two","2","three","3","four","4","five","5"};
+char *pairs_with_dups[] = {"one","1","two","2","one","11","two","22","three","3","two","222","two","2222","four","","five","5"};//82
+char *pairs_with_empty[] = {"name","","name","value"};
+char *pairs_different[] = {"30","thirty","40","fouthy"};
+int size;
+int size2;
+int brake_at = 3;
+bool flag;
+
+
+int
+iterate_cb (void *cls, const char *name, const char * const * value, int num_values)
+{
+	int *c = (int*)cls;
+
+	if(*c < 0 || *c > size)
+		exit(11);
+
+	if(strcmp(name,pairs[*c]) != 0)
+	{
+		FAIL_TEST("name is wrong\n");
+	}
+
+	if(1 != num_values)
+	{
+		FAIL_TEST("num_values is wrong\n");
+	}
+
+	if(strcmp(value[0],pairs[(*c)+1]) != 0)
+	{
+		FAIL_TEST("value is wrong\n");
+	}
+
+	(*c)+=2;
+
+	return SPDY_YES;
+}
+
+int
+iterate_brake_cb (void *cls, const char *name, const char * const *value, int num_values)
+{
+  (void)name;
+  (void)value;
+  (void)num_values;
+
+	int *c = (int*)cls;
+
+	if(*c < 0 || *c >= brake_at)
+	{
+		FAIL_TEST("iteration was not interrupted\n");
+	}
+
+	(*c)++;
+
+	if(*c == brake_at) return SPDY_NO;
+
+	return SPDY_YES;
+}
+
+int
+main()
+{
+	SPDY_init();
+
+	const char *const*value;
+	const char *const*value2;
+	int i;
+	int j;
+	int cls = 0;
+	int ret;
+	int ret2;
+	void *ob1;
+	void *ob2;
+	void *ob3;
+	void *stream;
+	char data[] = "anything";
+	struct SPDY_NameValue *container;
+	struct SPDY_NameValue *container2;
+	struct SPDY_NameValue *container3;
+	struct SPDY_NameValue *container_arr[2];
+
+	size = sizeof(pairs)/sizeof(pairs[0]);
+
+	if(NULL == (container = SPDY_name_value_create ()))
+	{
+		FAIL_TEST("SPDY_name_value_create failed\n");
+	}
+
+	if(NULL != SPDY_name_value_lookup (container, "anything", &ret))
+	{
+		FAIL_TEST("SPDY_name_value_lookup failed\n");
+	}
+
+	if(SPDY_name_value_iterate (container, NULL, NULL) != 0)
+	{
+		FAIL_TEST("SPDY_name_value_iterate failed\n");
+	}
+
+	for(i=0;i<size; i+=2)
+	{
+		if(SPDY_YES != SPDY_name_value_add(container,pairs[i],pairs[i+1]))
+		{
+			FAIL_TEST("SPDY_name_value_add failed\n");
+		}
+
+		if(SPDY_name_value_iterate (container, NULL, NULL) != ((i / 2) + 1))
+		{
+			FAIL_TEST("SPDY_name_value_iterate failed\n");
+		}
+	}
+
+	if(NULL != SPDY_name_value_lookup (container, "anything", &ret))
+	{
+		FAIL_TEST("SPDY_name_value_lookup failed\n");
+	}
+
+	for(i=size - 2; i >= 0; i-=2)
+	{
+		value = SPDY_name_value_lookup(container,pairs[i], &ret);
+		if(NULL == value || 1 !=ret || strcmp(value[0], pairs[i+1]) != 0)
+		{
+			printf("%p; %i; %i\n", value, ret,
+                               (NULL == value) ? -1 : strcmp(value[0], pairs[i+1]));
+			FAIL_TEST("SPDY_name_value_lookup failed\n");
+		}
+	}
+
+	SPDY_name_value_iterate (container, &iterate_cb, &cls);
+
+	cls = 0;
+	if(SPDY_name_value_iterate (container, &iterate_brake_cb, &cls) != brake_at)
+	{
+		FAIL_TEST("SPDY_name_value_iterate with brake failed\n");
+	}
+
+	SPDY_name_value_destroy(container);
+
+	//check everything with NULL values
+	for(i=0; i<7; ++i)
+	{
+		printf("%i ",i);
+		ob1 = (i & 4) ? data : NULL;
+		ob2 = (i & 2) ? data : NULL;
+		ob3 = (i & 1) ? data : NULL;
+		if(SPDY_INPUT_ERROR != SPDY_name_value_add(ob1,ob2,ob3))
+		{
+			FAIL_TEST("SPDY_name_value_add with NULLs failed\n");
+		}
+	}
+	printf("\n");
+	fflush(stdout);
+
+	if(SPDY_INPUT_ERROR != SPDY_name_value_iterate(NULL,NULL,NULL))
+	{
+		FAIL_TEST("SPDY_name_value_iterate with NULLs failed\n");
+	}
+
+	for(i=0; i<7; ++i)
+	{
+		printf("%i ",i);
+		ob1 = (i & 4) ? data : NULL;
+		ob2 = (i & 2) ? data : NULL;
+		ob3 = (i & 1) ? data : NULL;
+		if(NULL != SPDY_name_value_lookup(ob1,ob2,ob3))
+		{
+			FAIL_TEST("SPDY_name_value_lookup with NULLs failed\n");
+		}
+	}
+	printf("\n");
+	SPDY_name_value_destroy(NULL);
+
+	if(NULL == (container = SPDY_name_value_create ()))
+	{
+		FAIL_TEST("SPDY_name_value_create failed\n");
+	}
+
+	size = sizeof(pairs_with_dups)/sizeof(pairs_with_dups[0]);
+
+	for(i=0;i<size; i+=2)
+	{
+		if(SPDY_YES != SPDY_name_value_add(container,pairs_with_dups[i],pairs_with_dups[i+1]))
+		{
+			FAIL_TEST("SPDY_name_value_add failed\n");
+		}
+	}
+	if(SPDY_name_value_iterate (container, NULL, NULL) != atoi(pairs_with_dups[size - 1]))
+	{
+		FAIL_TEST("SPDY_name_value_iterate failed\n");
+	}
+	for(i=size - 2; i >= 0; i-=2)
+	{
+		value = SPDY_name_value_lookup(container,pairs_with_dups[i], &ret);
+		if(NULL == value)
+		{
+			FAIL_TEST("SPDY_name_value_lookup failed\n");
+		}
+		flag = false;
+		for(j=0; j<ret; ++j)
+			if(0 == strcmp(pairs_with_dups[i + 1], value[j]))
+			{
+				if(flag)
+					FAIL_TEST("SPDY_name_value_lookup failed\n");
+				flag=true;
+			}
+
+		if(!flag)
+			FAIL_TEST("SPDY_name_value_lookup failed\n");
+	}
+	if(SPDY_NO != SPDY_name_value_add(container,pairs_with_dups[0],pairs_with_dups[1]))
+		FAIL_TEST("SPDY_name_value_add failed\n");
+
+	SPDY_name_value_destroy(container);
+
+	if(NULL == (container = SPDY_name_value_create ()))
+	{
+		FAIL_TEST("SPDY_name_value_create failed\n");
+	}
+
+	size = sizeof(pairs_with_empty)/sizeof(pairs_with_empty[0]);
+
+	for(i=0;i<size; i+=2)
+	{
+		if(SPDY_YES != SPDY_name_value_add(container,pairs_with_empty[i],pairs_with_empty[i+1]))
+		{
+			FAIL_TEST("SPDY_name_value_add failed\n");
+		}
+		value = SPDY_name_value_lookup(container,pairs_with_empty[i], &ret);
+		if(NULL == value || 1 != ret)
+		{
+			printf("%p; %i\n", value, ret);
+			FAIL_TEST("SPDY_name_value_lookup failed\n");
+		}
+	}
+
+	ret = SPDY_name_value_iterate(container, NULL, NULL);
+	if(SPDY_INPUT_ERROR != SPDY_name_value_add(container, "capitalLeter","anything")
+		|| SPDY_name_value_iterate(container, NULL, NULL) != ret)
+	{
+		FAIL_TEST("SPDY_name_value_add failed\n");
+	}
+
+	SPDY_name_value_destroy(container);
+
+	if(NULL == (container = SPDY_name_value_create ()))
+	{
+		FAIL_TEST("SPDY_name_value_create failed\n");
+	}
+
+	size = sizeof(pairs_with_dups)/sizeof(pairs_with_dups[0]);
+
+	for(i=0;i<size; i+=2)
+	{
+		if(SPDY_YES != SPDY_name_value_add(container,pairs_with_dups[i],pairs_with_dups[i+1]))
+		{
+			FAIL_TEST("SPDY_name_value_add failed\n");
+		}
+	}
+
+	if(NULL == (container2 = SPDY_name_value_create ()))
+	{
+		FAIL_TEST("SPDY_name_value_create failed\n");
+	}
+
+	size2 = sizeof(pairs_different)/sizeof(pairs_different[0]);
+
+	for(i=0;i<size2; i+=2)
+	{
+		if(SPDY_YES != SPDY_name_value_add(container2,pairs_different[i],pairs_different[i+1]))
+		{
+			FAIL_TEST("SPDY_name_value_add failed\n");
+		}
+	}
+
+	container_arr[0] = container;
+	container_arr[1] = container2;
+	if(0 > (ret = SPDYF_name_value_to_stream(container_arr, 2, &stream)) || NULL == stream)
+		FAIL_TEST("SPDYF_name_value_to_stream failed\n");
+	ret = SPDYF_name_value_from_stream(stream, ret, &container3);
+	if(SPDY_YES != ret)
+		FAIL_TEST("SPDYF_name_value_from_stream failed\n");
+
+	if(SPDY_name_value_iterate(container3, NULL, NULL)
+		!= (SPDY_name_value_iterate(container, NULL, NULL) + SPDY_name_value_iterate(container2, NULL, NULL)))
+		FAIL_TEST("SPDYF_name_value_from_stream failed\n");
+
+	for(i=size - 2; i >= 0; i-=2)
+	{
+		value = SPDY_name_value_lookup(container,pairs_with_dups[i], &ret);
+		if(NULL == value)
+			FAIL_TEST("SPDY_name_value_lookup failed\n");
+		value2 = SPDY_name_value_lookup(container3,pairs_with_dups[i], &ret2);
+		if(NULL == value2)
+			FAIL_TEST("SPDY_name_value_lookup failed\n");
+
+		for(j=0; j<ret; ++j)
+			if(0 != strcmp(value2[j], value[j]))
+				FAIL_TEST("SPDY_name_value_lookup failed\n");
+	}
+	for(i=size2 - 2; i >= 0; i-=2)
+	{
+		value = SPDY_name_value_lookup(container2,pairs_different[i], &ret);
+		if(NULL == value)
+			FAIL_TEST("SPDY_name_value_lookup failed\n");
+		value2 = SPDY_name_value_lookup(container3,pairs_different[i], &ret2);
+		if(NULL == value2)
+			FAIL_TEST("SPDY_name_value_lookup failed\n");
+
+		for(j=0; j<ret; ++j)
+			if(0 != strcmp(value2[j], value[j]))
+				FAIL_TEST("SPDY_name_value_lookup failed\n");
+	}
+
+	SPDY_deinit();
+
+	return 0;
+}
diff --git a/src/testzzuf/Makefile.am b/src/testzzuf/Makefile.am
new file mode 100644
index 0000000..329fe04
--- /dev/null
+++ b/src/testzzuf/Makefile.am
@@ -0,0 +1,109 @@
+# This Makefile.am is in the public domain
+SUBDIRS  = .
+
+if USE_COVERAGE
+  AM_CFLAGS = -fprofile-arcs -ftest-coverage
+endif
+
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/include \
+  $(LIBCURL_CPPFLAGS)
+
+EXTRA_DIST = README socat.c
+
+check_PROGRAMS = \
+  test_get \
+  test_get_chunked \
+  test_post \
+  test_post_form \
+  test_put \
+  test_put_chunked \
+  test_put_large \
+  test_get11 \
+  test_post11 \
+  test_post_form11 \
+  test_put11 \
+  test_put_large11 \
+  test_long_header 
+
+TESTS = $(check_PROGRAMS)
+
+test_get_SOURCES = \
+  test_get.c
+test_get_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_get_chunked_SOURCES = \
+  test_get_chunked.c
+test_get_chunked_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_post_SOURCES = \
+  test_post.c
+test_post_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_post_form_SOURCES = \
+  test_post_form.c
+test_post_form_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_put_SOURCES = \
+  test_put.c
+test_put_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_put_chunked_SOURCES = \
+  test_put_chunked.c
+test_put_chunked_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_put_large_SOURCES = \
+  test_put_large.c
+test_put_large_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+
+
+test_get11_SOURCES = \
+  test_get.c
+test_get11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_post11_SOURCES = \
+  test_post.c
+test_post11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_post_form11_SOURCES = \
+  test_post_form.c
+test_post_form11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_put11_SOURCES = \
+  test_put.c
+test_put11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_put_large11_SOURCES = \
+  test_put_large.c
+test_put_large11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_long_header_SOURCES = \
+  test_long_header.c
+test_long_header_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
diff --git a/src/testzzuf/Makefile.in b/src/testzzuf/Makefile.in
new file mode 100644
index 0000000..da555b8
--- /dev/null
+++ b/src/testzzuf/Makefile.in
@@ -0,0 +1,1420 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 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 = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+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@
+check_PROGRAMS = test_get$(EXEEXT) test_get_chunked$(EXEEXT) \
+	test_post$(EXEEXT) test_post_form$(EXEEXT) test_put$(EXEEXT) \
+	test_put_chunked$(EXEEXT) test_put_large$(EXEEXT) \
+	test_get11$(EXEEXT) test_post11$(EXEEXT) \
+	test_post_form11$(EXEEXT) test_put11$(EXEEXT) \
+	test_put_large11$(EXEEXT) test_long_header$(EXEEXT)
+subdir = src/testzzuf
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/depcomp $(top_srcdir)/test-driver README
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_compile_flags.m4 \
+	$(top_srcdir)/m4/ax_append_flag.m4 \
+	$(top_srcdir)/m4/ax_check_compile_flag.m4 \
+	$(top_srcdir)/m4/ax_check_link_flag.m4 \
+	$(top_srcdir)/m4/ax_check_openssl.m4 \
+	$(top_srcdir)/m4/ax_count_cpus.m4 \
+	$(top_srcdir)/m4/ax_have_epoll.m4 \
+	$(top_srcdir)/m4/ax_pthread.m4 \
+	$(top_srcdir)/m4/ax_require_defined.m4 \
+	$(top_srcdir)/m4/libcurl.m4 $(top_srcdir)/m4/libgcrypt.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)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/MHD_config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am_test_get_OBJECTS = test_get.$(OBJEXT)
+test_get_OBJECTS = $(am_test_get_OBJECTS)
+test_get_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+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_test_get11_OBJECTS = test_get.$(OBJEXT)
+test_get11_OBJECTS = $(am_test_get11_OBJECTS)
+test_get11_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_get_chunked_OBJECTS = test_get_chunked.$(OBJEXT)
+test_get_chunked_OBJECTS = $(am_test_get_chunked_OBJECTS)
+test_get_chunked_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_long_header_OBJECTS = test_long_header.$(OBJEXT)
+test_long_header_OBJECTS = $(am_test_long_header_OBJECTS)
+test_long_header_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_post_OBJECTS = test_post.$(OBJEXT)
+test_post_OBJECTS = $(am_test_post_OBJECTS)
+test_post_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_post11_OBJECTS = test_post.$(OBJEXT)
+test_post11_OBJECTS = $(am_test_post11_OBJECTS)
+test_post11_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_post_form_OBJECTS = test_post_form.$(OBJEXT)
+test_post_form_OBJECTS = $(am_test_post_form_OBJECTS)
+test_post_form_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_post_form11_OBJECTS = test_post_form.$(OBJEXT)
+test_post_form11_OBJECTS = $(am_test_post_form11_OBJECTS)
+test_post_form11_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_put_OBJECTS = test_put.$(OBJEXT)
+test_put_OBJECTS = $(am_test_put_OBJECTS)
+test_put_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_put11_OBJECTS = test_put.$(OBJEXT)
+test_put11_OBJECTS = $(am_test_put11_OBJECTS)
+test_put11_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_put_chunked_OBJECTS = test_put_chunked.$(OBJEXT)
+test_put_chunked_OBJECTS = $(am_test_put_chunked_OBJECTS)
+test_put_chunked_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_put_large_OBJECTS = test_put_large.$(OBJEXT)
+test_put_large_OBJECTS = $(am_test_put_large_OBJECTS)
+test_put_large_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+am_test_put_large11_OBJECTS = test_put_large.$(OBJEXT)
+test_put_large11_OBJECTS = $(am_test_put_large11_OBJECTS)
+test_put_large11_DEPENDENCIES =  \
+	$(top_builddir)/src/microhttpd/libmicrohttpd.la
+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__depfiles_maybe = depfiles
+am__mv = mv -f
+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 = $(test_get_SOURCES) $(test_get11_SOURCES) \
+	$(test_get_chunked_SOURCES) $(test_long_header_SOURCES) \
+	$(test_post_SOURCES) $(test_post11_SOURCES) \
+	$(test_post_form_SOURCES) $(test_post_form11_SOURCES) \
+	$(test_put_SOURCES) $(test_put11_SOURCES) \
+	$(test_put_chunked_SOURCES) $(test_put_large_SOURCES) \
+	$(test_put_large11_SOURCES)
+DIST_SOURCES = $(test_get_SOURCES) $(test_get11_SOURCES) \
+	$(test_get_chunked_SOURCES) $(test_long_header_SOURCES) \
+	$(test_post_SOURCES) $(test_post11_SOURCES) \
+	$(test_post_form_SOURCES) $(test_post_form11_SOURCES) \
+	$(test_put_SOURCES) $(test_put11_SOURCES) \
+	$(test_put_chunked_SOURCES) $(test_put_large_SOURCES) \
+	$(test_put_large11_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-recursive dvi-recursive html-recursive info-recursive \
+	install-data-recursive install-dvi-recursive \
+	install-exec-recursive install-html-recursive \
+	install-info-recursive install-pdf-recursive \
+	install-ps-recursive install-recursive installcheck-recursive \
+	installdirs-recursive pdf-recursive ps-recursive \
+	tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	check recheck distdir
+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__tty_colors_dummy = \
+  mgn= red= grn= lgn= blu= brg= std=; \
+  am__color_tests=no
+am__tty_colors = { \
+  $(am__tty_colors_dummy); \
+  if test "X$(AM_COLOR_TESTS)" = Xno; then \
+    am__color_tests=no; \
+  elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+    am__color_tests=yes; \
+  elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+    am__color_tests=yes; \
+  fi; \
+  if test $$am__color_tests = yes; then \
+    red=''; \
+    grn=''; \
+    lgn=''; \
+    blu=''; \
+    mgn=''; \
+    brg=''; \
+    std=''; \
+  fi; \
+}
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__recheck_rx = ^[ 	]*:recheck:[ 	]*
+am__global_test_result_rx = ^[ 	]*:global-test-result:[ 	]*
+am__copy_in_global_log_rx = ^[ 	]*:copy-in-global-log:[ 	]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+  recheck = 1; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+        { \
+          if ((getline line2 < ($$0 ".log")) < 0) \
+	    recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+        { \
+          recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+        { \
+          break; \
+        } \
+    }; \
+  if (recheck) \
+    print $$0; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+  print "fatal: making $@: " msg | "cat >&2"; \
+  exit 1; \
+} \
+function rst_section(header) \
+{ \
+  print header; \
+  len = length(header); \
+  for (i = 1; i <= len; i = i + 1) \
+    printf "="; \
+  printf "\n\n"; \
+} \
+{ \
+  copy_in_global_log = 1; \
+  global_test_result = "RUN"; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+         fatal("failed to read from " $$0 ".trs"); \
+      if (line ~ /$(am__global_test_result_rx)/) \
+        { \
+          sub("$(am__global_test_result_rx)", "", line); \
+          sub("[ 	]*$$", "", line); \
+          global_test_result = line; \
+        } \
+      else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+        copy_in_global_log = 0; \
+    }; \
+  if (copy_in_global_log) \
+    { \
+      rst_section(global_test_result ": " $$0); \
+      while ((rc = (getline line < ($$0 ".log"))) != 0) \
+      { \
+        if (rc < 0) \
+          fatal("failed to read from " $$0 ".log"); \
+        print line; \
+      }; \
+      printf "\n"; \
+    }; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/   &   /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this.  Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+  --color-tests "$$am__color_tests" \
+  --enable-hard-errors "$$am__enable_hard_errors" \
+  --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test.  Creates the
+# directory for the log if needed.  Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log.  Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT.  Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup);					\
+$(am__vpath_adj_setup) $(am__vpath_adj)			\
+$(am__tty_colors);					\
+srcdir=$(srcdir); export srcdir;			\
+case "$@" in						\
+  */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;;	\
+    *) am__odir=.;; 					\
+esac;							\
+test "x$$am__odir" = x"." || test -d "$$am__odir" 	\
+  || $(MKDIR_P) "$$am__odir" || exit $$?;		\
+if test -f "./$$f"; then dir=./;			\
+elif test -f "$$f"; then dir=;				\
+else dir="$(srcdir)/"; fi;				\
+tst=$$dir$$f; log='$@'; 				\
+if test -n '$(DISABLE_HARD_ERRORS)'; then		\
+  am__enable_hard_errors=no; 				\
+else							\
+  am__enable_hard_errors=yes; 				\
+fi; 							\
+case " $(XFAIL_TESTS) " in				\
+  *[\ \	]$$f[\ \	]* | *[\ \	]$$dir$$f[\ \	]*) \
+    am__expect_failure=yes;;				\
+  *)							\
+    am__expect_failure=no;;				\
+esac; 							\
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed).  The result is saved in the shell variable
+# '$bases'.  This honors runtime overriding of TESTS and TEST_LOGS.  Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+  bases='$(TEST_LOGS)'; \
+  bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+  bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+  case '$@' in \
+    */*) \
+      case '$*' in \
+        */*) b='$*';; \
+          *) b=`echo '$@' | sed 's/\.log$$//'`; \
+       esac;; \
+    *) \
+      b='$*';; \
+  esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+	$(TEST_LOG_FLAGS)
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPU_COUNT = @CPU_COUNT@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_CPPFLAGS = @GNUTLS_CPPFLAGS@
+GNUTLS_LDFLAGS = @GNUTLS_LDFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+HAVE_CURL_BINARY = @HAVE_CURL_BINARY@
+HAVE_MAKEINFO_BINARY = @HAVE_MAKEINFO_BINARY@
+HIDDEN_VISIBILITY_CFLAGS = @HIDDEN_VISIBILITY_CFLAGS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSPDY_VERSION_AGE = @LIBSPDY_VERSION_AGE@
+LIBSPDY_VERSION_CURRENT = @LIBSPDY_VERSION_CURRENT@
+LIBSPDY_VERSION_REVISION = @LIBSPDY_VERSION_REVISION@
+LIBTOOL = @LIBTOOL@
+LIB_VERSION_AGE = @LIB_VERSION_AGE@
+LIB_VERSION_CURRENT = @LIB_VERSION_CURRENT@
+LIB_VERSION_REVISION = @LIB_VERSION_REVISION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MHD_LIBDEPS = @MHD_LIBDEPS@
+MHD_LIBDEPS_PKGCFG = @MHD_LIBDEPS_PKGCFG@
+MHD_LIB_CFLAGS = @MHD_LIB_CFLAGS@
+MHD_LIB_CPPFLAGS = @MHD_LIB_CPPFLAGS@
+MHD_LIB_LDFLAGS = @MHD_LIB_LDFLAGS@
+MHD_REQ_PRIVATE = @MHD_REQ_PRIVATE@
+MKDIR_P = @MKDIR_P@
+MS_LIB_TOOL = @MS_LIB_TOOL@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_INCLUDES = @OPENSSL_INCLUDES@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@
+PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@
+PACKAGE_VERSION_SUBMINOR = @PACKAGE_VERSION_SUBMINOR@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPDY_LIBDEPS = @SPDY_LIBDEPS@
+SPDY_LIB_CFLAGS = @SPDY_LIB_CFLAGS@
+SPDY_LIB_CPPFLAGS = @SPDY_LIB_CPPFLAGS@
+SPDY_LIB_LDFLAGS = @SPDY_LIB_LDFLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+_libcurl_config = @_libcurl_config@
+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_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@
+ax_pthread_config = @ax_pthread_config@
+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@
+have_socat = @have_socat@
+have_zzuf = @have_zzuf@
+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@
+lt_cv_objdir = @lt_cv_objdir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# This Makefile.am is in the public domain
+SUBDIRS = .
+@USE_COVERAGE_TRUE@AM_CFLAGS = -fprofile-arcs -ftest-coverage
+AM_CPPFLAGS = -I$(top_srcdir)/src/include \
+  $(LIBCURL_CPPFLAGS)
+
+EXTRA_DIST = README socat.c
+TESTS = $(check_PROGRAMS)
+test_get_SOURCES = \
+  test_get.c
+
+test_get_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_get_chunked_SOURCES = \
+  test_get_chunked.c
+
+test_get_chunked_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_post_SOURCES = \
+  test_post.c
+
+test_post_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_post_form_SOURCES = \
+  test_post_form.c
+
+test_post_form_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_put_SOURCES = \
+  test_put.c
+
+test_put_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_put_chunked_SOURCES = \
+  test_put_chunked.c
+
+test_put_chunked_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_put_large_SOURCES = \
+  test_put_large.c
+
+test_put_large_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_get11_SOURCES = \
+  test_get.c
+
+test_get11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_post11_SOURCES = \
+  test_post.c
+
+test_post11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_post_form11_SOURCES = \
+  test_post_form.c
+
+test_post_form11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_put11_SOURCES = \
+  test_put.c
+
+test_put11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_put_large11_SOURCES = \
+  test_put_large.c
+
+test_put_large11_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+test_long_header_SOURCES = \
+  test_long_header.c
+
+test_long_header_LDADD = \
+  $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+  @LIBCURL@ 
+
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
+$(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) --gnu src/testzzuf/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/testzzuf/Makefile
+.PRECIOUS: 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__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	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-checkPROGRAMS:
+	@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+
+test_get$(EXEEXT): $(test_get_OBJECTS) $(test_get_DEPENDENCIES) $(EXTRA_test_get_DEPENDENCIES) 
+	@rm -f test_get$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_get_OBJECTS) $(test_get_LDADD) $(LIBS)
+
+test_get11$(EXEEXT): $(test_get11_OBJECTS) $(test_get11_DEPENDENCIES) $(EXTRA_test_get11_DEPENDENCIES) 
+	@rm -f test_get11$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_get11_OBJECTS) $(test_get11_LDADD) $(LIBS)
+
+test_get_chunked$(EXEEXT): $(test_get_chunked_OBJECTS) $(test_get_chunked_DEPENDENCIES) $(EXTRA_test_get_chunked_DEPENDENCIES) 
+	@rm -f test_get_chunked$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_get_chunked_OBJECTS) $(test_get_chunked_LDADD) $(LIBS)
+
+test_long_header$(EXEEXT): $(test_long_header_OBJECTS) $(test_long_header_DEPENDENCIES) $(EXTRA_test_long_header_DEPENDENCIES) 
+	@rm -f test_long_header$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_long_header_OBJECTS) $(test_long_header_LDADD) $(LIBS)
+
+test_post$(EXEEXT): $(test_post_OBJECTS) $(test_post_DEPENDENCIES) $(EXTRA_test_post_DEPENDENCIES) 
+	@rm -f test_post$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_post_OBJECTS) $(test_post_LDADD) $(LIBS)
+
+test_post11$(EXEEXT): $(test_post11_OBJECTS) $(test_post11_DEPENDENCIES) $(EXTRA_test_post11_DEPENDENCIES) 
+	@rm -f test_post11$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_post11_OBJECTS) $(test_post11_LDADD) $(LIBS)
+
+test_post_form$(EXEEXT): $(test_post_form_OBJECTS) $(test_post_form_DEPENDENCIES) $(EXTRA_test_post_form_DEPENDENCIES) 
+	@rm -f test_post_form$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_post_form_OBJECTS) $(test_post_form_LDADD) $(LIBS)
+
+test_post_form11$(EXEEXT): $(test_post_form11_OBJECTS) $(test_post_form11_DEPENDENCIES) $(EXTRA_test_post_form11_DEPENDENCIES) 
+	@rm -f test_post_form11$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_post_form11_OBJECTS) $(test_post_form11_LDADD) $(LIBS)
+
+test_put$(EXEEXT): $(test_put_OBJECTS) $(test_put_DEPENDENCIES) $(EXTRA_test_put_DEPENDENCIES) 
+	@rm -f test_put$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_put_OBJECTS) $(test_put_LDADD) $(LIBS)
+
+test_put11$(EXEEXT): $(test_put11_OBJECTS) $(test_put11_DEPENDENCIES) $(EXTRA_test_put11_DEPENDENCIES) 
+	@rm -f test_put11$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_put11_OBJECTS) $(test_put11_LDADD) $(LIBS)
+
+test_put_chunked$(EXEEXT): $(test_put_chunked_OBJECTS) $(test_put_chunked_DEPENDENCIES) $(EXTRA_test_put_chunked_DEPENDENCIES) 
+	@rm -f test_put_chunked$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_put_chunked_OBJECTS) $(test_put_chunked_LDADD) $(LIBS)
+
+test_put_large$(EXEEXT): $(test_put_large_OBJECTS) $(test_put_large_DEPENDENCIES) $(EXTRA_test_put_large_DEPENDENCIES) 
+	@rm -f test_put_large$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_put_large_OBJECTS) $(test_put_large_LDADD) $(LIBS)
+
+test_put_large11$(EXEEXT): $(test_put_large11_OBJECTS) $(test_put_large11_DEPENDENCIES) $(EXTRA_test_put_large11_DEPENDENCIES) 
+	@rm -f test_put_large11$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_put_large11_OBJECTS) $(test_put_large11_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_get.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_get_chunked.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_long_header.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_post.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_post_form.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_put.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_put_chunked.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_put_large.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+#     (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'.  Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+	rm -f $< $@
+	$(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+	@:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+	@$(am__set_TESTS_bases); \
+	am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+	redo_bases=`for i in $$bases; do \
+	              am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+	            done`; \
+	if test -n "$$redo_bases"; then \
+	  redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+	  redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+	  if $(am__make_dryrun); then :; else \
+	    rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
+	  fi; \
+	fi; \
+	if test -n "$$am__remaking_logs"; then \
+	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+	       "recursion detected" >&2; \
+	else \
+	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+	fi; \
+	if $(am__make_dryrun); then :; else \
+	  st=0;  \
+	  errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+	  for i in $$redo_bases; do \
+	    test -f $$i.trs && test -r $$i.trs \
+	      || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+	    test -f $$i.log && test -r $$i.log \
+	      || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+	  done; \
+	  test $$st -eq 0 || exit 1; \
+	fi
+	@$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+	ws='[ 	]'; \
+	results=`for b in $$bases; do echo $$b.trs; done`; \
+	test -n "$$results" || results=/dev/null; \
+	all=`  grep "^$$ws*:test-result:"           $$results | wc -l`; \
+	pass=` grep "^$$ws*:test-result:$$ws*PASS"  $$results | wc -l`; \
+	fail=` grep "^$$ws*:test-result:$$ws*FAIL"  $$results | wc -l`; \
+	skip=` grep "^$$ws*:test-result:$$ws*SKIP"  $$results | wc -l`; \
+	xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+	xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+	error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+	if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+	  success=true; \
+	else \
+	  success=false; \
+	fi; \
+	br='==================='; br=$$br$$br$$br$$br; \
+	result_count () \
+	{ \
+	    if test x"$$1" = x"--maybe-color"; then \
+	      maybe_colorize=yes; \
+	    elif test x"$$1" = x"--no-color"; then \
+	      maybe_colorize=no; \
+	    else \
+	      echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+	    fi; \
+	    shift; \
+	    desc=$$1 count=$$2; \
+	    if test $$maybe_colorize = yes && test $$count -gt 0; then \
+	      color_start=$$3 color_end=$$std; \
+	    else \
+	      color_start= color_end=; \
+	    fi; \
+	    echo "$${color_start}# $$desc $$count$${color_end}"; \
+	}; \
+	create_testsuite_report () \
+	{ \
+	  result_count $$1 "TOTAL:" $$all   "$$brg"; \
+	  result_count $$1 "PASS: " $$pass  "$$grn"; \
+	  result_count $$1 "SKIP: " $$skip  "$$blu"; \
+	  result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+	  result_count $$1 "FAIL: " $$fail  "$$red"; \
+	  result_count $$1 "XPASS:" $$xpass "$$red"; \
+	  result_count $$1 "ERROR:" $$error "$$mgn"; \
+	}; \
+	{								\
+	  echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" |	\
+	    $(am__rst_title);						\
+	  create_testsuite_report --no-color;				\
+	  echo;								\
+	  echo ".. contents:: :depth: 2";				\
+	  echo;								\
+	  for b in $$bases; do echo $$b; done				\
+	    | $(am__create_global_log);					\
+	} >$(TEST_SUITE_LOG).tmp || exit 1;				\
+	mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG);			\
+	if $$success; then						\
+	  col="$$grn";							\
+	 else								\
+	  col="$$red";							\
+	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
+	fi;								\
+	echo "$${col}$$br$${std}"; 					\
+	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
+	echo "$${col}$$br$${std}"; 					\
+	create_testsuite_report --maybe-color;				\
+	echo "$$col$$br$$std";						\
+	if $$success; then :; else					\
+	  echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}";		\
+	  if test -n "$(PACKAGE_BUGREPORT)"; then			\
+	    echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}";	\
+	  fi;								\
+	  echo "$$col$$br$$std";					\
+	fi;								\
+	$$success || exit 1
+
+check-TESTS:
+	@list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
+	@list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+	log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+	exit $$?;
+recheck: all $(check_PROGRAMS)
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	bases=`for i in $$bases; do echo $$i; done \
+	         | $(am__list_recheck_tests)` || exit 1; \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	log_list=`echo $$log_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+	        am__force_recheck=am--force-recheck \
+	        TEST_LOGS="$$log_list"; \
+	exit $$?
+test_get.log: test_get$(EXEEXT)
+	@p='test_get$(EXEEXT)'; \
+	b='test_get'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_get_chunked.log: test_get_chunked$(EXEEXT)
+	@p='test_get_chunked$(EXEEXT)'; \
+	b='test_get_chunked'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_post.log: test_post$(EXEEXT)
+	@p='test_post$(EXEEXT)'; \
+	b='test_post'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_post_form.log: test_post_form$(EXEEXT)
+	@p='test_post_form$(EXEEXT)'; \
+	b='test_post_form'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_put.log: test_put$(EXEEXT)
+	@p='test_put$(EXEEXT)'; \
+	b='test_put'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_put_chunked.log: test_put_chunked$(EXEEXT)
+	@p='test_put_chunked$(EXEEXT)'; \
+	b='test_put_chunked'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_put_large.log: test_put_large$(EXEEXT)
+	@p='test_put_large$(EXEEXT)'; \
+	b='test_put_large'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_get11.log: test_get11$(EXEEXT)
+	@p='test_get11$(EXEEXT)'; \
+	b='test_get11'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_post11.log: test_post11$(EXEEXT)
+	@p='test_post11$(EXEEXT)'; \
+	b='test_post11'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_post_form11.log: test_post_form11$(EXEEXT)
+	@p='test_post_form11$(EXEEXT)'; \
+	b='test_post_form11'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_put11.log: test_put11$(EXEEXT)
+	@p='test_put11$(EXEEXT)'; \
+	b='test_put11'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_put_large11.log: test_put_large11$(EXEEXT)
+	@p='test_put_large11$(EXEEXT)'; \
+	b='test_put_large11'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+test_long_header.log: test_long_header$(EXEEXT)
+	@p='test_long_header$(EXEEXT)'; \
+	b='test_long_header'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+	@p='$<'; \
+	$(am__set_b); \
+	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@	@p='$<'; \
+@am__EXEEXT_TRUE@	$(am__set_b); \
+@am__EXEEXT_TRUE@	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@	--log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@	"$$tst" $(AM_TESTS_FD_REDIRECT)
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    $(am__make_dryrun) \
+	      || test -d "$(distdir)/$$subdir" \
+	      || $(MKDIR_P) "$(distdir)/$$subdir" \
+	      || exit 1; \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+	-test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+	-test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+	-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-checkPROGRAMS clean-generic clean-libtool \
+	mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) check-am install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+	check-TESTS check-am clean clean-checkPROGRAMS clean-generic \
+	clean-libtool 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 \
+	installdirs-am maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \
+	uninstall uninstall-am
+
+
+# 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/src/testzzuf/README b/src/testzzuf/README
new file mode 100644
index 0000000..5ee1569
--- /dev/null
+++ b/src/testzzuf/README
@@ -0,0 +1,13 @@
+Testcases in this directory require zzuf and socat.
+
+zzuf is used to randomly mess with the TCP connection between the CURL
+clients and the MHD server.  The goal is to expose problems in MHD's
+error handling (by introducing random syntax errors).  socat is
+used to listen on port 11081 and forward the randomzied stream to
+port 11080 where MHD is waiting.
+
+As a result, the testcases in this directory do NOT check that
+whatever CURL returns is what was expected -- random modifications to
+the TCP stream can have random effects ;-).  Testcases "fail" if the
+code crashes or hangs indefinitely.
+
diff --git a/src/testzzuf/socat.c b/src/testzzuf/socat.c
new file mode 100644
index 0000000..8743dc9
--- /dev/null
+++ b/src/testzzuf/socat.c
@@ -0,0 +1,114 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2008 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file socat.c
+ * @brief  Code to fork-exec zzuf and start the socat process
+ * @author Christian Grothoff
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif /* !WIN32_LEAN_AND_MEAN */
+#include <windows.h>
+#endif
+
+
+/**
+ * A larger loop count will run more random tests --
+ * which would be good, except that it may take too
+ * long for most user's patience.  So this small
+ * value is the default.
+ */
+#define LOOP_COUNT 10
+
+#define CURL_TIMEOUT 50L
+
+static pid_t zzuf_pid;
+
+static void
+zzuf_socat_start ()
+{
+  int status;
+  char *const args[] = {
+    "zzuf",
+    "--ratio=0.0:0.75",
+    "-n",
+    "-A",
+    "--",
+    "socat",
+    "-lf",
+    "/dev/null",
+    "TCP4-LISTEN:11081,reuseaddr,fork",
+    "TCP4:127.0.0.1:11080",
+    NULL,
+  };
+  zzuf_pid = fork ();
+  if (zzuf_pid == -1)
+    {
+      fprintf (stderr, "fork failed: %s\n", strerror (errno));
+      exit (1);
+    }
+  if (zzuf_pid != 0)
+    {
+      sleep (1);                /* allow zzuf and socat to start */
+      status = 0;
+      if (0 < waitpid (zzuf_pid, &status, WNOHANG))
+        {
+          if (WIFEXITED (status))
+            fprintf (stderr,
+                     "zzuf died with status code %d!\n",
+                     WEXITSTATUS (status));
+          if (WIFSIGNALED (status))
+            fprintf (stderr,
+                     "zzuf died from signal %d!\n", WTERMSIG (status));
+          exit (1);
+        }
+      return;
+    }
+  setpgrp ();
+  execvp ("zzuf", args);
+  fprintf (stderr, "execution of `zzuf' failed: %s\n", strerror (errno));
+  zzuf_pid = 0;                 /* fork failed */
+  exit (1);
+}
+
+
+static void
+zzuf_socat_stop ()
+{
+  int status;
+  if (zzuf_pid != 0)
+    {
+      if (0 != killpg (zzuf_pid, SIGINT))
+        fprintf (stderr, "Failed to killpg: %s\n", strerror (errno));
+      kill (zzuf_pid, SIGINT);
+      waitpid (zzuf_pid, &status, 0);
+      sleep (1);                /* allow socat to also die in peace */
+    }
+}
+
+/* end of socat.c */
diff --git a/src/testzzuf/test_get.c b/src/testzzuf/test_get.c
new file mode 100644
index 0000000..47cbe12
--- /dev/null
+++ b/src/testzzuf/test_get.c
@@ -0,0 +1,314 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2008 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_get.c
+ * @brief  Testcase for libmicrohttpd GET operations
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+#include "socat.c"
+
+static int oneone;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int ptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&ptr != *unused)
+    {
+      *unused = &ptr;
+      return MHD_YES;
+    }
+  *unused = NULL;
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url,
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  if (ret == MHD_NO)
+    abort ();
+  return ret;
+}
+
+
+static int
+testInternalGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  int i;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
+                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      curl_easy_perform (c);
+      curl_easy_cleanup (c);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+static int
+testMultithreadedGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  int i;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ ,
+                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      curl_easy_perform (c);
+      curl_easy_cleanup (c);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+static int
+testExternalGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  int max;
+  int running;
+  time_t start;
+  struct timeval tv;
+  int i;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */ ,
+                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      mret = curl_multi_add_handle (multi, c);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          zzuf_socat_stop ();
+          MHD_stop_daemon (d);
+          return 1024;
+        }
+      start = time (NULL);
+      while ((time (NULL) - start < 5) && (c != NULL))
+        {
+          max = 0;
+          FD_ZERO (&rs);
+          FD_ZERO (&ws);
+          FD_ZERO (&es);
+          curl_multi_perform (multi, &running);
+          mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+          if (mret != CURLM_OK)
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              zzuf_socat_stop ();
+              MHD_stop_daemon (d);
+              return 2048;
+            }
+          if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              zzuf_socat_stop ();
+              MHD_stop_daemon (d);
+              return 4096;
+            }
+          tv.tv_sec = 0;
+          tv.tv_usec = 1000;
+          select (max + 1, &rs, &ws, &es, &tv);
+          curl_multi_perform (multi, &running);
+          if (running == 0)
+            {
+              curl_multi_info_read (multi, &running);
+              curl_multi_remove_handle (multi, c);
+              curl_easy_cleanup (c);
+              c = NULL;
+            }
+          MHD_run (d);
+        }
+      if (c != NULL)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_easy_cleanup (c);
+        }
+    }
+  fprintf (stderr, "\n");
+  curl_multi_cleanup (multi);
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testInternalGet ();
+  errorCount += testMultithreadedGet ();
+  errorCount += testExternalGet ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testzzuf/test_get_chunked.c b/src/testzzuf/test_get_chunked.c
new file mode 100644
index 0000000..faa2632
--- /dev/null
+++ b/src/testzzuf/test_get_chunked.c
@@ -0,0 +1,330 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2008 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file daemontest_get_chunked.c
+ * @brief  Testcase for libmicrohttpd GET operations with chunked content encoding
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+#include "socat.c"
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+/**
+ * MHD content reader callback that returns
+ * data in chunks.
+ */
+static ssize_t
+crc (void *cls, uint64_t pos, char *buf, size_t max)
+{
+  struct MHD_Response **responseptr = cls;
+
+  if (pos == 128 * 10)
+    {
+      MHD_add_response_header (*responseptr, "Footer", "working");
+      return MHD_CONTENT_READER_END_OF_STREAM;
+    }
+  if (max < 128)
+    abort ();                   /* should not happen in this testcase... */
+  memset (buf, 'A' + (pos / 128), 128);
+  return 128;
+}
+
+/**
+ * Dummy function that does nothing.
+ */
+static void
+crcf (void *ptr)
+{
+  free (ptr);
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size, void **ptr)
+{
+  static int aptr;
+  const char *me = cls;
+  struct MHD_Response *response;
+  struct MHD_Response **responseptr;
+  int ret;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  if (&aptr != *ptr)
+    {
+      /* do never respond on first call */
+      *ptr = &aptr;
+      return MHD_YES;
+    }
+  responseptr = malloc (sizeof (struct MHD_Response *));
+  response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
+                                                1024,
+                                                &crc, responseptr, &crcf);
+  *responseptr = response;
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+static int
+testInternalGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  int i;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
+                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      curl_easy_perform (c);
+      curl_easy_cleanup (c);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+static int
+testMultithreadedGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  int i;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ ,
+                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      curl_easy_perform (c);
+      curl_easy_cleanup (c);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+static int
+testExternalGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  int max;
+  int running;
+  time_t start;
+  struct timeval tv;
+  int i;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */ ,
+                        11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      mret = curl_multi_add_handle (multi, c);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          zzuf_socat_stop ();
+          MHD_stop_daemon (d);
+          return 1024;
+        }
+      start = time (NULL);
+      while ((time (NULL) - start < 5) && (c != NULL))
+        {
+          max = 0;
+          FD_ZERO (&rs);
+          FD_ZERO (&ws);
+          FD_ZERO (&es);
+          curl_multi_perform (multi, &running);
+          mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+          if (mret != CURLM_OK)
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              zzuf_socat_stop ();
+              MHD_stop_daemon (d);
+              return 2048;
+            }
+          if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              zzuf_socat_stop ();
+              MHD_stop_daemon (d);
+              return 4096;
+            }
+          tv.tv_sec = 0;
+          tv.tv_usec = 1000;
+          select (max + 1, &rs, &ws, &es, &tv);
+          curl_multi_perform (multi, &running);
+          if (running == 0)
+            {
+              curl_multi_info_read (multi, &running);
+              curl_multi_remove_handle (multi, c);
+              curl_easy_cleanup (c);
+              c = NULL;
+            }
+          MHD_run (d);
+        }
+      if (c != NULL)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_easy_cleanup (c);
+        }
+    }
+  fprintf (stderr, "\n");
+  curl_multi_cleanup (multi);
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testInternalGet ();
+  errorCount += testMultithreadedGet ();
+  errorCount += testExternalGet ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testzzuf/test_long_header.c b/src/testzzuf/test_long_header.c
new file mode 100644
index 0000000..2004138
--- /dev/null
+++ b/src/testzzuf/test_long_header.c
@@ -0,0 +1,234 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2008 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_long_header.c
+ * @brief  Testcase for libmicrohttpd handling of very long headers
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+#include "socat.c"
+
+/**
+ * We will set the memory available per connection to
+ * half of this value, so the actual value does not have
+ * to be big at all...
+ */
+#define VERY_LONG (1024*10)
+
+static int oneone;
+
+static int
+apc_all (void *cls, const struct sockaddr *addr, socklen_t addrlen)
+{
+  return MHD_YES;
+}
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  const char *me = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp (me, method))
+    return MHD_NO;              /* unexpected method */
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url,
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+static int
+testLongUrlGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  char *url;
+  int i;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
+                        11080,
+                        &apc_all,
+                        NULL,
+                        &ahc_echo,
+                        "GET",
+                        MHD_OPTION_CONNECTION_MEMORY_LIMIT,
+                        (size_t) (VERY_LONG / 2), MHD_OPTION_END);
+
+  if (d == NULL)
+    return 1;
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+
+      c = curl_easy_init ();
+      url = malloc (VERY_LONG);
+      memset (url, 'a', VERY_LONG);
+      url[VERY_LONG - 1] = '\0';
+      memcpy (url, "http://localhost:11081/",
+              strlen ("http://localhost:11081/"));
+      curl_easy_setopt (c, CURLOPT_URL, url);
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      curl_easy_perform (c);
+      curl_easy_cleanup (c);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+
+  MHD_stop_daemon (d);
+  free (url);
+  return 0;
+}
+
+
+static int
+testLongHeaderGet ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  char *url;
+  struct curl_slist *header = NULL;
+  int i;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
+                        11080,
+                        &apc_all,
+                        NULL,
+                        &ahc_echo,
+                        "GET",
+                        MHD_OPTION_CONNECTION_MEMORY_LIMIT,
+                        (size_t) (VERY_LONG / 2), MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+      c = curl_easy_init ();
+      url = malloc (VERY_LONG);
+      memset (url, 'a', VERY_LONG);
+      url[VERY_LONG - 1] = '\0';
+      url[VERY_LONG / 2] = ':';
+      url[VERY_LONG / 2 + 1] = ' ';
+      header = curl_slist_append (header, url);
+
+      curl_easy_setopt (c, CURLOPT_HTTPHEADER, header);
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      curl_easy_perform (c);
+      curl_slist_free_all (header);
+      header = NULL;
+      curl_easy_cleanup (c);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+
+  MHD_stop_daemon (d);
+  free (url);
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testLongUrlGet ();
+  errorCount += testLongHeaderGet ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testzzuf/test_post.c b/src/testzzuf/test_post.c
new file mode 100644
index 0000000..632609f
--- /dev/null
+++ b/src/testzzuf/test_post.c
@@ -0,0 +1,394 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2008 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_post.c
+ * @brief  Testcase for libmicrohttpd POST operations using URL-encoding
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+
+#include "socat.c"
+
+#define POST_DATA "name=daniel&project=curl"
+
+static int oneone;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+
+static void
+completed_cb (void *cls,
+	      struct MHD_Connection *connection,
+	      void **con_cls,
+	      enum MHD_RequestTerminationCode toe)
+{
+  struct MHD_PostProcessor *pp = *con_cls;
+
+  if (NULL != pp)
+    MHD_destroy_post_processor (pp);
+  *con_cls = NULL;
+}
+
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+
+/**
+ * Note that this post_iterator is not perfect
+ * in that it fails to support incremental processing.
+ * (to be fixed in the future)
+ */
+static int
+post_iterator (void *cls,
+               enum MHD_ValueKind kind,
+               const char *key,
+               const char *filename,
+               const char *content_type,
+               const char *transfer_encoding,
+               const char *value, uint64_t off, size_t size)
+{
+  int *eok = cls;
+
+  if ((0 == strcmp (key, "name")) &&
+      (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size)))
+    (*eok) |= 1;
+  if ((0 == strcmp (key, "project")) &&
+      (size == strlen ("curl")) && (0 == strncmp (value, "curl", size)))
+    (*eok) |= 2;
+  return MHD_YES;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int eok;
+  struct MHD_Response *response;
+  struct MHD_PostProcessor *pp;
+  int ret;
+
+  if (0 != strcmp ("POST", method))
+    {
+      return MHD_NO;            /* unexpected method */
+    }
+  pp = *unused;
+  if (pp == NULL)
+    {
+      eok = 0;
+      pp = MHD_create_post_processor (connection, 1024, &post_iterator, &eok);
+      *unused = pp;
+    }
+  MHD_post_process (pp, upload_data, *upload_data_size);
+  if ((eok == 3) && (0 == *upload_data_size))
+    {
+      response = MHD_create_response_from_buffer (strlen (url),
+						  (void *) url,
+						  MHD_RESPMEM_MUST_COPY);
+      ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+      MHD_destroy_response (response);
+      MHD_destroy_post_processor (pp);
+      *unused = NULL;
+      return ret;
+    }
+  *upload_data_size = 0;
+  return MHD_YES;
+}
+
+
+static int
+testInternalPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  int i;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
+                        11080, NULL, NULL, &ahc_echo, NULL, 
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,			
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
+      curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
+      curl_easy_setopt (c, CURLOPT_POST, 1L);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      curl_easy_perform (c);
+      curl_easy_cleanup (c);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+
+  return 0;
+}
+
+static int
+testMultithreadedPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  int i;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ ,
+                        11080, NULL, NULL, &ahc_echo, NULL, 
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
+      curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
+      curl_easy_setopt (c, CURLOPT_POST, 1L);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      curl_easy_perform (c);
+      curl_easy_cleanup (c);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+static int
+testExternalPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  int max;
+  int running;
+  time_t start;
+  struct timeval tv;
+  int i;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */ ,
+                        1082, NULL, NULL, &ahc_echo, NULL, 
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      MHD_stop_daemon (d);
+      return 512;
+    }
+
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+
+
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1082/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
+      curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
+      curl_easy_setopt (c, CURLOPT_POST, 1L);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+      mret = curl_multi_add_handle (multi, c);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          zzuf_socat_stop ();
+          MHD_stop_daemon (d);
+          return 1024;
+        }
+      start = time (NULL);
+      while ((time (NULL) - start < 5) && (c != NULL))
+        {
+          max = 0;
+          FD_ZERO (&rs);
+          FD_ZERO (&ws);
+          FD_ZERO (&es);
+          curl_multi_perform (multi, &running);
+          mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+          if (mret != CURLM_OK)
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              zzuf_socat_stop ();
+              MHD_stop_daemon (d);
+              return 2048;
+            }
+          if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              zzuf_socat_stop ();
+              MHD_stop_daemon (d);
+              return 4096;
+            }
+          tv.tv_sec = 0;
+          tv.tv_usec = 1000;
+          select (max + 1, &rs, &ws, &es, &tv);
+          curl_multi_perform (multi, &running);
+          if (running == 0)
+            {
+              curl_multi_info_read (multi, &running);
+              curl_multi_remove_handle (multi, c);
+              curl_easy_cleanup (c);
+              c = NULL;
+            }
+          MHD_run (d);
+        }
+      if (c != NULL)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_easy_cleanup (c);
+        }
+
+    }
+  fprintf (stderr, "\n");
+  curl_multi_cleanup (multi);
+  zzuf_socat_stop ();
+
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testInternalPost ();
+  errorCount += testMultithreadedPost ();
+  errorCount += testExternalPost ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testzzuf/test_post_form.c b/src/testzzuf/test_post_form.c
new file mode 100644
index 0000000..3e2cdff
--- /dev/null
+++ b/src/testzzuf/test_post_form.c
@@ -0,0 +1,410 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2008 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_post_form.c
+ * @brief  Testcase for libmicrohttpd POST operations using multipart/postform data
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+
+#include "socat.c"
+
+static int oneone;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+
+static void
+completed_cb (void *cls,
+	      struct MHD_Connection *connection,
+	      void **con_cls,
+	      enum MHD_RequestTerminationCode toe)
+{
+  struct MHD_PostProcessor *pp = *con_cls;
+
+  if (NULL != pp)
+    MHD_destroy_post_processor (pp);
+  *con_cls = NULL;
+}
+
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+/**
+ * Note that this post_iterator is not perfect
+ * in that it fails to support incremental processing.
+ * (to be fixed in the future)
+ */
+static int
+post_iterator (void *cls,
+               enum MHD_ValueKind kind,
+               const char *key,
+               const char *filename,
+               const char *content_type,
+               const char *transfer_encoding,
+               const char *value, uint64_t off, size_t size)
+{
+  int *eok = cls;
+
+  if (key == NULL)
+    return MHD_YES;
+#if 0
+  fprintf (stderr, "PI sees %s-%.*s\n", key, size, value);
+#endif
+  if ((0 == strcmp (key, "name")) &&
+      (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size)))
+    (*eok) |= 1;
+  if ((0 == strcmp (key, "project")) &&
+      (size == strlen ("curl")) && (0 == strncmp (value, "curl", size)))
+    (*eok) |= 2;
+  return MHD_YES;
+}
+
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  static int eok;
+  struct MHD_Response *response;
+  struct MHD_PostProcessor *pp;
+  int ret;
+
+  if (0 != strcmp ("POST", method))
+    {
+      return MHD_NO;            /* unexpected method */
+    }
+  pp = *unused;
+  if (pp == NULL)
+    {
+      eok = 0;
+      pp = MHD_create_post_processor (connection, 1024, &post_iterator, &eok);
+      if (pp == NULL)
+        return MHD_NO;
+      *unused = pp;
+    }
+  MHD_post_process (pp, upload_data, *upload_data_size);
+  if ((eok == 3) && (0 == *upload_data_size))
+    {
+      response = MHD_create_response_from_buffer (strlen (url),
+						  (void *) url,
+						  MHD_RESPMEM_MUST_COPY);
+      ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+      MHD_destroy_response (response);
+      MHD_destroy_post_processor (pp);
+      *unused = NULL;
+      return ret;
+    }
+  *upload_data_size = 0;
+  return MHD_YES;
+}
+
+static struct curl_httppost *
+make_form ()
+{
+  struct curl_httppost *post = NULL;
+  struct curl_httppost *last = NULL;
+
+  curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
+                CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END);
+  curl_formadd (&post, &last, CURLFORM_COPYNAME, "project",
+                CURLFORM_COPYCONTENTS, "curl", CURLFORM_END);
+  return post;
+}
+
+
+static int
+testInternalPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  int i;
+  struct curl_httppost *pd;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
+                        11080, NULL, NULL, &ahc_echo, NULL, 
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      pd = make_form ();
+      curl_easy_setopt (c, CURLOPT_HTTPPOST, pd);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      curl_easy_perform (c);
+      curl_easy_cleanup (c);
+      curl_formfree (pd);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+static int
+testMultithreadedPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  int i;
+  struct curl_httppost *pd;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ ,
+                        11080, NULL, NULL, &ahc_echo, NULL, 
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      pd = make_form ();
+      curl_easy_setopt (c, CURLOPT_HTTPPOST, pd);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      curl_easy_perform (c);
+      curl_easy_cleanup (c);
+      curl_formfree (pd);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+static int
+testExternalPost ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  int max;
+  int running;
+  time_t start;
+  struct timeval tv;
+  struct curl_httppost *pd;
+  int i;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */ ,
+                        1082, NULL, NULL, &ahc_echo, NULL, 
+			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,			
+			MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1082/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      pd = make_form ();
+      curl_easy_setopt (c, CURLOPT_HTTPPOST, pd);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+      mret = curl_multi_add_handle (multi, c);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_cleanup (multi);
+          curl_formfree (pd);
+          curl_easy_cleanup (c);
+          zzuf_socat_stop ();
+          MHD_stop_daemon (d);
+          return 1024;
+        }
+      start = time (NULL);
+      while ((time (NULL) - start < 5) && (c != NULL))
+        {
+          max = 0;
+          FD_ZERO (&rs);
+          FD_ZERO (&ws);
+          FD_ZERO (&es);
+          curl_multi_perform (multi, &running);
+          mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+          if (mret != CURLM_OK)
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              zzuf_socat_stop ();
+              MHD_stop_daemon (d);
+              curl_formfree (pd);
+              return 2048;
+            }
+          if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              curl_formfree (pd);
+              zzuf_socat_stop ();
+              MHD_stop_daemon (d);
+              return 4096;
+            }
+          tv.tv_sec = 0;
+          tv.tv_usec = 1000;
+          select (max + 1, &rs, &ws, &es, &tv);
+          curl_multi_perform (multi, &running);
+          if (running == 0)
+            {
+              curl_multi_info_read (multi, &running);
+              curl_multi_remove_handle (multi, c);
+              curl_easy_cleanup (c);
+              c = NULL;
+            }
+          MHD_run (d);
+        }
+      if (c != NULL)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_easy_cleanup (c);
+        }
+      curl_formfree (pd);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testInternalPost ();
+  errorCount += testMultithreadedPost ();
+  errorCount += testExternalPost ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testzzuf/test_put.c b/src/testzzuf/test_put.c
new file mode 100644
index 0000000..c6240c4
--- /dev/null
+++ b/src/testzzuf/test_put.c
@@ -0,0 +1,361 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2008 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_put.c
+ * @brief  Testcase for libmicrohttpd PUT operations
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+
+#include "socat.c"
+
+static int oneone;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
+{
+  unsigned int *pos = ptr;
+  unsigned int wrt;
+
+  wrt = size * nmemb;
+  if (wrt > 8 - (*pos))
+    wrt = 8 - (*pos);
+  memcpy (stream, &("Hello123"[*pos]), wrt);
+  (*pos) += wrt;
+  return wrt;
+}
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  int *done = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp ("PUT", method))
+    return MHD_NO;              /* unexpected method */
+  if ((*done) == 0)
+    {
+      if (*upload_data_size != 8)
+        return MHD_YES;         /* not yet ready */
+      if (0 == memcmp (upload_data, "Hello123", 8))
+        {
+          *upload_data_size = 0;
+        }
+      else
+        {
+          printf ("Invalid upload data `%8s'!\n", upload_data);
+          return MHD_NO;
+        }
+      *done = 1;
+      return MHD_YES;
+    }
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url,
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+static int
+testInternalPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  int i;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
+                        11080,
+                        NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+      curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+      curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+      curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      curl_easy_perform (c);
+      curl_easy_cleanup (c);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+static int
+testMultithreadedPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  int i;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ ,
+                        11080,
+                        NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+      curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+      curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+      curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      curl_easy_perform (c);
+      curl_easy_cleanup (c);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+static int
+testExternalPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  int max;
+  int running;
+  time_t start;
+  struct timeval tv;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  int i;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */ ,
+                        11080,
+                        NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+      curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+      curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+      curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+
+      mret = curl_multi_add_handle (multi, c);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          zzuf_socat_stop ();
+          MHD_stop_daemon (d);
+          return 1024;
+        }
+      start = time (NULL);
+      while ((time (NULL) - start < 5) && (c != NULL))
+        {
+          max = 0;
+          FD_ZERO (&rs);
+          FD_ZERO (&ws);
+          FD_ZERO (&es);
+          curl_multi_perform (multi, &running);
+          mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+          if (mret != CURLM_OK)
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              zzuf_socat_stop ();
+              MHD_stop_daemon (d);
+              return 2048;
+            }
+          if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              zzuf_socat_stop ();
+              MHD_stop_daemon (d);
+              return 4096;
+            }
+          tv.tv_sec = 0;
+          tv.tv_usec = 1000;
+          select (max + 1, &rs, &ws, &es, &tv);
+          curl_multi_perform (multi, &running);
+          if (running == 0)
+            {
+              curl_multi_info_read (multi, &running);
+              curl_multi_remove_handle (multi, c);
+              curl_easy_cleanup (c);
+              c = NULL;
+            }
+          MHD_run (d);
+        }
+      if (c != NULL)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_easy_cleanup (c);
+        }
+    }
+  fprintf (stderr, "\n");
+  curl_multi_cleanup (multi);
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testInternalPut ();
+  errorCount += testMultithreadedPut ();
+  errorCount += testExternalPut ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testzzuf/test_put_chunked.c b/src/testzzuf/test_put_chunked.c
new file mode 100644
index 0000000..c023c5e
--- /dev/null
+++ b/src/testzzuf/test_put_chunked.c
@@ -0,0 +1,371 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2008 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file daemontest_put_chunked.c
+ * @brief Testcase for libmicrohttpd PUT operations with chunked encoding
+ *        for the upload data
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+#include "socat.c"
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
+{
+  unsigned int *pos = ptr;
+  unsigned int wrt;
+
+  wrt = size * nmemb;
+  if (wrt > 8 - (*pos))
+    wrt = 8 - (*pos);
+  if (wrt > 4)
+    wrt = 4;                    /* only send half at first => force multiple chunks! */
+  memcpy (stream, &("Hello123"[*pos]), wrt);
+  (*pos) += wrt;
+  return wrt;
+}
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  int *done = cls;
+  struct MHD_Response *response;
+  int ret;
+  int have;
+
+  if (0 != strcmp ("PUT", method))
+    return MHD_NO;              /* unexpected method */
+  if ((*done) < 8)
+    {
+      have = *upload_data_size;
+      if (have + *done > 8)
+        {
+          return MHD_NO;
+        }
+      if (0 == memcmp (upload_data, &"Hello123"[*done], have))
+        {
+          *done += have;
+          *upload_data_size = 0;
+        }
+      else
+        {
+          return MHD_NO;
+        }
+#if 0
+      fprintf (stderr, "Not ready for response: %u/%u\n", *done, 8);
+#endif
+      return MHD_YES;
+    }
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url,
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+static int
+testInternalPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  int i;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+                        11080,
+                        NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11080/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+      curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+      curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+      /*
+         // by not giving the file size, we force chunking!
+         curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+       */
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      curl_easy_perform (c);
+      curl_easy_cleanup (c);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+static int
+testMultithreadedPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  CURLcode errornum;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        11081,
+                        NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  c = curl_easy_init ();
+  curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+  curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+  curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+  curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+  curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+  /*
+     // by not giving the file size, we force chunking!
+     curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+   */
+  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+  curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
+  // NOTE: use of CONNECTTIMEOUT without also
+  //   setting NOSIGNAL results in really weird
+  //   crashes on my system!
+  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+  if (CURLE_OK != (errornum = curl_easy_perform (c)))
+    {
+      fprintf (stderr,
+               "curl_easy_perform failed: `%s'\n",
+               curl_easy_strerror (errornum));
+      curl_easy_cleanup (c);
+      MHD_stop_daemon (d);
+      return 32;
+    }
+  curl_easy_cleanup (c);
+  MHD_stop_daemon (d);
+  if (cbc.pos != strlen ("/hello_world"))
+    return 64;
+  if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+    return 128;
+
+  return 0;
+}
+
+
+static int
+testExternalPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  char buf[2048];
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  int max;
+  int running;
+  time_t start;
+  struct timeval tv;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  int i;
+
+  multi = NULL;
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_DEBUG,
+                        11082,
+                        NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11082/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+      curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+      curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+      /*
+         // by not giving the file size, we force chunking!
+         curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
+       */
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+      mret = curl_multi_add_handle (multi, c);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          zzuf_socat_stop ();
+          MHD_stop_daemon (d);
+          return 1024;
+        }
+      start = time (NULL);
+      while ((time (NULL) - start < 5) && (c != NULL))
+        {
+          max = 0;
+          FD_ZERO (&rs);
+          FD_ZERO (&ws);
+          FD_ZERO (&es);
+          curl_multi_perform (multi, &running);
+          mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+          if (mret != CURLM_OK)
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              zzuf_socat_stop ();
+              MHD_stop_daemon (d);
+              return 2048;
+            }
+          if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              zzuf_socat_stop ();
+              MHD_stop_daemon (d);
+              return 4096;
+            }
+          tv.tv_sec = 0;
+          tv.tv_usec = 1000;
+          select (max + 1, &rs, &ws, &es, &tv);
+          curl_multi_perform (multi, &running);
+          if (running == 0)
+            {
+              curl_multi_info_read (multi, &running);
+              curl_multi_remove_handle (multi, c);
+              curl_easy_cleanup (c);
+              c = NULL;
+            }
+          MHD_run (d);
+        }
+      if (c != NULL)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_easy_cleanup (c);
+        }
+    }
+  fprintf (stderr, "\n");
+  curl_multi_cleanup (multi);
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  errorCount += testInternalPut ();
+  errorCount += testMultithreadedPut ();
+  errorCount += testExternalPut ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}
diff --git a/src/testzzuf/test_put_large.c b/src/testzzuf/test_put_large.c
new file mode 100644
index 0000000..1fdc92a
--- /dev/null
+++ b/src/testzzuf/test_put_large.c
@@ -0,0 +1,382 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2007, 2008 Christian Grothoff
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_put_large.c
+ * @brief  Testcase for libmicrohttpd PUT operations
+ * @author Christian Grothoff
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+#include "socat.c"
+
+static int oneone;
+
+/**
+ * Do not make this much larger since we will hit the
+ * MHD default buffer limit and the test code is not
+ * written for incremental upload processing...
+ */
+#define PUT_SIZE (256 * 1024)
+
+static char *put_buffer;
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+static size_t
+putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
+{
+  unsigned int *pos = ptr;
+  unsigned int wrt;
+
+  wrt = size * nmemb;
+  if (wrt > PUT_SIZE - (*pos))
+    wrt = PUT_SIZE - (*pos);
+  memcpy (stream, &put_buffer[*pos], wrt);
+  (*pos) += wrt;
+  return wrt;
+}
+
+static size_t
+copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    return 0;                   /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size,
+          void **unused)
+{
+  int *done = cls;
+  struct MHD_Response *response;
+  int ret;
+
+  if (0 != strcmp ("PUT", method))
+    return MHD_NO;              /* unexpected method */
+  if ((*done) == 0)
+    {
+      if (*upload_data_size != PUT_SIZE)
+        {
+#if 0
+          fprintf (stderr,
+                   "Waiting for more data (%u/%u)...\n",
+                   *upload_data_size, PUT_SIZE);
+#endif
+          return MHD_YES;       /* not yet ready */
+        }
+      if (0 == memcmp (upload_data, put_buffer, PUT_SIZE))
+        {
+          *upload_data_size = 0;
+        }
+      else
+        {
+          return MHD_NO;
+        }
+      *done = 1;
+      return MHD_YES;
+    }
+  response = MHD_create_response_from_buffer (strlen (url),
+					      (void *) url, 
+					      MHD_RESPMEM_MUST_COPY);
+  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+  MHD_destroy_response (response);
+  return ret;
+}
+
+
+static int
+testInternalPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  char buf[2048];
+  int i;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
+                        11080,
+                        NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+      curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+      curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+      curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      curl_easy_perform (c);
+      curl_easy_cleanup (c);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+static int
+testMultithreadedPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  struct CBC cbc;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  char buf[2048];
+  int i;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ ,
+                        11080,
+                        NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+  if (d == NULL)
+    return 16;
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+      curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+      curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+      curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+      curl_easy_perform (c);
+      curl_easy_cleanup (c);
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+static int
+testExternalPut ()
+{
+  struct MHD_Daemon *d;
+  CURL *c;
+  struct CBC cbc;
+  CURLM *multi;
+  CURLMcode mret;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  int max;
+  int running;
+  time_t start;
+  struct timeval tv;
+  unsigned int pos = 0;
+  int done_flag = 0;
+  char buf[2048];
+  int i;
+
+  cbc.buf = buf;
+  cbc.size = 2048;
+  cbc.pos = 0;
+  multi = NULL;
+  d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */,
+                        11080,
+                        NULL, NULL, &ahc_echo, &done_flag,
+                        MHD_OPTION_CONNECTION_MEMORY_LIMIT,
+                        (size_t) (PUT_SIZE * 4), MHD_OPTION_END);
+  if (d == NULL)
+    return 256;
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    {
+      MHD_stop_daemon (d);
+      return 512;
+    }
+  zzuf_socat_start ();
+  for (i = 0; i < LOOP_COUNT; i++)
+    {
+      fprintf (stderr, ".");
+
+      c = curl_easy_init ();
+      curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
+      curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
+      curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
+      curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
+      curl_easy_setopt (c, CURLOPT_READDATA, &pos);
+      curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
+      curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
+      curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
+      curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
+      if (oneone)
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+      else
+        curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+      curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
+      // NOTE: use of CONNECTTIMEOUT without also
+      //   setting NOSIGNAL results in really weird
+      //   crashes on my system!
+      curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
+
+
+
+      mret = curl_multi_add_handle (multi, c);
+      if (mret != CURLM_OK)
+        {
+          curl_multi_cleanup (multi);
+          curl_easy_cleanup (c);
+          zzuf_socat_stop ();
+          MHD_stop_daemon (d);
+          return 1024;
+        }
+      start = time (NULL);
+      while ((time (NULL) - start < 5) && (c != NULL))
+        {
+          max = 0;
+          FD_ZERO (&rs);
+          FD_ZERO (&ws);
+          FD_ZERO (&es);
+          curl_multi_perform (multi, &running);
+          mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
+          if (mret != CURLM_OK)
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              zzuf_socat_stop ();
+              MHD_stop_daemon (d);
+              return 2048;
+            }
+          if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+            {
+              curl_multi_remove_handle (multi, c);
+              curl_multi_cleanup (multi);
+              curl_easy_cleanup (c);
+              zzuf_socat_stop ();
+              MHD_stop_daemon (d);
+              return 4096;
+            }
+          tv.tv_sec = 0;
+          tv.tv_usec = 1000;
+          select (max + 1, &rs, &ws, &es, &tv);
+          curl_multi_perform (multi, &running);
+          if (running == 0)
+            {
+              curl_multi_info_read (multi, &running);
+              curl_multi_remove_handle (multi, c);
+              curl_easy_cleanup (c);
+              c = NULL;
+            }
+          MHD_run (d);
+        }
+      if (c != NULL)
+        {
+          curl_multi_remove_handle (multi, c);
+          curl_easy_cleanup (c);
+        }
+    }
+  fprintf (stderr, "\n");
+  zzuf_socat_stop ();
+  curl_multi_cleanup (multi);
+  MHD_stop_daemon (d);
+  return 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+  unsigned int errorCount = 0;
+
+  oneone = (NULL != strrchr (argv[0], (int) '/')) ?
+    (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    return 2;
+  put_buffer = malloc (PUT_SIZE);
+  memset (put_buffer, 1, PUT_SIZE);
+  errorCount += testInternalPut ();
+  errorCount += testMultithreadedPut ();
+  errorCount += testExternalPut ();
+  free (put_buffer);
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  curl_global_cleanup ();
+  return errorCount != 0;       /* 0 == pass */
+}