Merge branch 'release/1.2.10'
diff --git a/.gitignore b/.gitignore
index 4d45a10..00eb13f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,21 +1,5 @@
 # cmake manages these; they shouldn't go in version control
-
-/CMakeCache.txt
-/CMakeFiles/**
-/Makefile
-/cmake_install.cmake
-
-# generated .h files
-
-/compat/sys/tree.h
-/oniguruma/config.h
-
-# compiled files
-
-/libevhtp.a
-/test
-/test_basic
-/test_vhost
-
-/test_client
-/test_proxy
+#
+#
+# they aren't going into version control, but shouldn't be
+# completely ignored. I'm removing the mods here.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5d0a967..ee79fa8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,9 +1,11 @@
 cmake_minimum_required(VERSION 2.8)
-project(reason)
+project(libevhtp)
 
 set(PROJECT_MAJOR_VERSION 1)
 set(PROJECT_MINOR_VERSION 2)
-set(PROJECT_PATCH_VERSION 9)
+set(PROJECT_PATCH_VERSION 10)
+
+#add_definitions(-D_FORTIFY_SOURCE=2)
 
 set (PROJECT_VERSION ${PROJECT_MAJOR_VERSION}.${PROJECT_MINOR_VERSION}.${PROJECT_PATCH_VERSION})
 set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules)
@@ -11,12 +13,14 @@
 INCLUDE (CheckFunctionExists)
 INCLUDE (CheckIncludeFiles)
 INCLUDE (CheckTypeSize)
+INCLUDE (CheckCCompilerFlag)
+INCLUDE (TestBigEndian)
+INCLUDE (UseDebugSymbols)
 
-CHECK_FUNCTION_EXISTS(alloca  C_ALLOCA)
+
 CHECK_FUNCTION_EXISTS(memcmp  HAVE_MEMCMP)
 CHECK_FUNCTION_EXISTS(strndup HAVE_STRNDUP)
 CHECK_FUNCTION_EXISTS(strnlen HAVE_STRNLEN)
-
 CHECK_INCLUDE_FILES(alloca.h HAVE_ALLOCA_H)
 CHECK_INCLUDE_FILES(strings.h HAVE_STRINGS_H)
 CHECK_INCLUDE_FILES(string.h HAVE_STRING_H)
@@ -34,6 +38,19 @@
 CHECK_TYPE_SIZE("long" SIZEOF_LONG)
 CHECK_TYPE_SIZE("short" SIZEOF_SHORT)
 
+TEST_BIG_ENDIAN(HOST_BIG_ENDIAN)
+
+check_c_compiler_flag(-fvisibility=hidden EVHTP_HAS_VISIBILITY_HIDDEN)
+check_c_compiler_flag(-std=c99 EVHTP_HAS_C99)
+
+if (EVHTP_HAS_C99)
+	add_definitions(-DEVHTP_HAS_C99)
+endif()
+
+if (EVHTP_HAS_VISIBILITY_HIDDEN)
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
+endif()
+
 if (NOT HAVE_SYS_TREE)
     CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/compat/sys/tree.h.in ${CMAKE_CURRENT_BINARY_DIR}/compat/sys/tree.h)
 endif(NOT HAVE_SYS_TREE)
@@ -54,6 +71,19 @@
 	set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_SYS_UN")
 endif(NOT HAVE_SYS_UN)
 
+if (HOST_BIG_ENDIAN)
+		add_definitions(-DHOST_BIG_ENDIAN)
+endif()
+
+# Test 32/64 bits
+if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
+		add_definitions(-DEVHTP_SYS_ARCH=64)
+elseif("${CMAKE_SIZEOF_VOID_P}" EQUAL "4")
+		add_definitions(-DEVHTP_SYS_ARCH=32)
+else()
+	message(ERROR "Unable to determine architecture")
+endif()
+
 # -DEVHTP_DISABLE_SSL:STRING=ON
 OPTION(EVHTP_DISABLE_SSL       "Disable ssl support"      OFF)
 
@@ -77,10 +107,10 @@
 
 include(BaseConfig)
 
-message("Build Type: ${CMAKE_BUILD_TYPE}")
-message("Std CFLAGS: ${CMAKE_C_FLAGS}")
-message("Dbg CFLAGS: ${CMAKE_C_FLAGS_DEBUG}")
-message("Rel CFLAGS: ${CMAKE_C_FLAGS_RELEASE}")
+message(STATUS "Build Type               : ${CMAKE_BUILD_TYPE}")
+message(STATUS "std Debug CFLAGS         : ${CMAKE_C_FLAGS_DEBUG}")
+message(STATUS "Std Release CFLAGS       : ${CMAKE_C_FLAGS_RELEASE}")
+message(STATUS "Std ReleaseWDebug CFLAGS : ${CMAKE_C_FLAGS_RELWITHDEBINFO}")
 
 find_package(LibEvent REQUIRED)
 
@@ -99,12 +129,17 @@
 endif()
 
 if (NOT OPENSSL_FOUND)
-	message("Diabling SSL")
-	set (EVHTP_DISABLE_SSL ON)
-	set (OPENSSL_CRYPTO_LIBRARY "")
-	set (OPENSSL_INCLUDE_DIR "")
-	set (OPENSSL_LIBRARIES "")
+	message(WARN"Unable to find OpenSSL, will continue, but without the support")
+
+	set (EVHTP_DISABLE_SSL        ON)
+	set (OPENSSL_CRYPTO_LIBRARY   "")
+	set (OPENSSL_INCLUDE_DIR      "")
+	set (OPENSSL_LIBRARIES        "")
 	set (LIBEVENT_OPENSSL_LIBRARY "")
+elseif(APPLE)
+	# darwin based hosts have deprecated normal openssl functions, which is
+	# annoying to see warnings, for now, just ignore them.
+	set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations")
 endif()
 
 if (NOT EVHTP_DISABLE_REGEX)
@@ -193,17 +228,13 @@
 	set (EVHTP_DISABLE_SSL ON)
 endif(NOT ${LIBEVENT_OPENSSL_FOUND})
 
-set(LIBEVHTP_SOURCES evhtp.c htparse/htparse.c)
+set(LIBEVHTP_SOURCES evhtp.c evhtp_numtoa.c htparse/htparse.c)
 
 if (NOT EVHTP_DISABLE_EVTHR)
 	set (LIBEVHTP_EXTERNAL_LIBS ${LIBEVHTP_EXTERNAL_LIBS} pthread)
 	set (LIBEVHTP_SOURCES ${LIBEVHTP_SOURCES} evthr/evthr.c)
 endif(NOT EVHTP_DISABLE_EVTHR)
 
-if (NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug")
-  set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNDEBUG")
-endif (NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug")
-
 IF (WIN32)
 	ADD_DEFINITIONS(-DWIN32)
 	ADD_DEFINITIONS(-march=i486)
@@ -219,7 +250,6 @@
 		find_library (LIB_RT rt)
 		set (SYS_LIBS ${SYS_LIBS} ${LIB_RT})
 	endif()
-
 ENDIF (WIN32)
 
 add_custom_target(examples)
@@ -234,24 +264,29 @@
 	${CMAKE_CURRENT_SOURCE_DIR}/evhtp-config.h.in
 	${CMAKE_CURRENT_BINARY_DIR}/evhtp-config.h)
 
-add_library(libevhtp ${EVHTP_LIBTYPE} ${LIBEVHTP_SOURCES} ${ONIG_SOURCES})
-
-set_target_properties(libevhtp PROPERTIES OUTPUT_NAME "evhtp")
-target_link_libraries(libevhtp ${LIBEVHTP_EXTERNAL_LIBS})
+add_library(evhtp ${EVHTP_LIBTYPE} ${LIBEVHTP_SOURCES} ${ONIG_SOURCES})
+target_link_libraries(evhtp ${LIBEVHTP_EXTERNAL_LIBS})
 
 add_executable(test EXCLUDE_FROM_ALL examples/test.c)
 add_executable(test_basic EXCLUDE_FROM_ALL examples/test_basic.c)
 add_executable(test_vhost EXCLUDE_FROM_ALL examples/test_vhost.c)
 add_executable(test_client EXCLUDE_FROM_ALL examples/test_client.c)
-add_executable(test_proxy EXCLUDE_FROM_ALL examples/test_proxy.c)
+add_executable(test_query EXCLUDE_FROM_ALL examples/test_query.c)
 
-target_link_libraries(test libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
-target_link_libraries(test_basic libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
-target_link_libraries(test_vhost libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
-target_link_libraries(test_client libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
-target_link_libraries(test_proxy libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
+strip_debug_symbols(test_query)
 
-add_dependencies(examples test test_basic test_vhost test_client test_proxy)
+if (NOT EVHTP_DISABLE_EVTHR)
+	add_executable(test_proxy EXCLUDE_FROM_ALL examples/test_proxy.c)
+	target_link_libraries(test_proxy evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
+endif()
+
+target_link_libraries(test evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
+target_link_libraries(test_basic evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
+target_link_libraries(test_vhost evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
+target_link_libraries(test_client evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
+target_link_libraries(test_query evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
+
+add_dependencies(examples test test_basic test_vhost test_client test_proxy test_query)
 
 if (NOT LIB_INSTALL_DIR)
   set (LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib")
@@ -261,7 +296,7 @@
 	set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include)
 endif()
 
-install (TARGETS libevhtp DESTINATION ${LIB_INSTALL_DIR})
+install (TARGETS evhtp DESTINATION ${LIB_INSTALL_DIR})
 install (FILES evhtp.h DESTINATION ${INCLUDE_INSTALL_DIR})
 install (FILES htparse/htparse.h DESTINATION ${INCLUDE_INSTALL_DIR})
 install (FILES ${CMAKE_CURRENT_BINARY_DIR}/evhtp-config.h DESTINATION ${INCLUDE_INSTALL_DIR})
@@ -282,3 +317,33 @@
    install (FILES compat/sys/queue.h DESTINATION ${INCLUDE_INSTALL_DIR}/sys)
    install (FILES oniguruma/onigposix.h DESTINATION ${INCLUDE_INSTALL_DIR})
 ENDIF (WIN32)
+
+configure_file(
+	${CMAKE_CURRENT_SOURCE_DIR}/evhtp.pc.in
+	${CMAKE_CURRENT_BINARY_DIR}/evhtp.pc @ONLY)
+
+message(STATUS "CMAKE_BUILD_TYPE         : " ${CMAKE_BUILD_TYPE})
+message(STATUS "CMAKE_BINARY_DIR         : " ${CMAKE_BINARY_DIR})
+message(STATUS "CMAKE_CURRENT_BINARY_DIR : " ${CMAKE_CURRENT_BINARY_DIR})
+message(STATUS "CMAKE_SOURCE_DIR         : " ${CMAKE_SOURCE_DIR})
+message(STATUS "CMAKE_CURRENT_SOURCE_DIR : " ${CMAKE_CURRENT_SOURCE_DIR})
+message(STATUS "PROJECT_BINARY_DIR       : " ${PROJECT_BINARY_DIR})
+message(STATUS "PROJECT_SOURCE_DIR       : " ${PROJECT_SOURCE_DIR})
+message(STATUS "CMAKE_MODULE_PATH        : " ${CMAKE_MODULE_PATH})
+message(STATUS "CMAKE_COMMAND            : " ${CMAKE_COMMAND})
+message(STATUS "CMAKE_ROOT               : " ${CMAKE_ROOT})
+message(STATUS "CMAKE_SYSTEM             : " ${CMAKE_SYSTEM})
+message(STATUS "CMAKE_SYSTEM_NAME        : " ${CMAKE_SYSTEM_NAME})
+message(STATUS "CMAKE_SYSTEM_VERSION     : " ${CMAKE_SYSTEM_VERSION})
+message(STATUS "CMAKE_SYSTEM_PROCESSOR   : " ${CMAKE_SYSTEM_PROCESSOR})
+message(STATUS "CMAKE_C_FLAGS            : " ${CMAKE_C_FLAGS})
+message(STATUS "CMAKE_CXX_FLAGS          : " ${CMAKE_CXX_FLAGS})
+message(STATUS "CMAKE_C_COMPILER         : " ${CMAKE_C_COMPILER})
+message(STATUS "CMAKE_CXX_COMPILER       : " ${CMAKE_CXX_COMPILER})
+message(STATUS "CMAKE_AR                 : " ${CMAKE_AR})
+message(STATUS "CMAKE_RANLIB             : " ${CMAKE_RANLIB})
+message(STATUS "CMAKE_C_FLAGS_DEBUG      : " ${CMAKE_C_FLAGS_DEBUG})
+message(STATUS "CMAKE_C_FLAGS_RELEASE    : " ${CMAKE_C_FLAGS_RELEASE})
+message(STATUS "CMAKE_C_FLAGS_RELWDBGIFO : " ${CMAKE_C_FLAGS_RELWITHDEBINFO})
+message(STATUS "CMAKE_INSTALL_PREFIX     : " ${CMAKE_INSTALL_PREFIX})
+message(STATUS "Version                  : " ${PROJECT_VERSION})
diff --git a/CMakeModules/AddOptions.cmake b/CMakeModules/AddOptions.cmake
new file mode 100644
index 0000000..3255d90
--- /dev/null
+++ b/CMakeModules/AddOptions.cmake
@@ -0,0 +1,102 @@
+# - Add options without repeating them on the command line
+#
+# Synopsis:
+#
+#	add_options (lang build opts)
+#
+# where:
+#
+#	lang       Name of the language whose compiler should receive the
+#	           options, e.g. CXX. If a comma-separated list is received
+#	           then the option is added for all those languages. Use the
+#	           special value ALL_LANGUAGES for these languages: CXX, C
+#	           and Fortran
+#
+#	build      Kind of build to which this options should apply,
+#              such as DEBUG and RELEASE. This can also be a comma-
+#	           separated list. Use the special value ALL_BUILDS to apply
+#	           to all builds.
+#
+#	opts       List of options to add. Each should be quoted.
+#
+# Example:
+#
+#	add_options (CXX RELEASE "-O3" "-DNDEBUG" "-Wall")
+
+function (add_options langs builds)
+  # special handling of empty language specification
+  if ("${langs}" STREQUAL "ALL_LANGUAGES")
+	set (langs CXX C Fortran)
+  endif ("${langs}" STREQUAL "ALL_LANGUAGES")
+  foreach (lang IN LISTS langs)
+	# prepend underscore if necessary
+	foreach (build IN LISTS builds)
+	  if (NOT ("${build}" STREQUAL "ALL_BUILDS"))
+		set (_bld "_${build}")
+		string (TOUPPER "${_bld}" _bld)
+	  else (NOT ("${build}" STREQUAL "ALL_BUILDS"))
+		set (_bld "")
+	  endif (NOT ("${build}" STREQUAL "ALL_BUILDS"))
+	  # if we want everything in the "global" flag, then simply
+	  # ignore the build type here and go add everything to that one
+	  if (CMAKE_NOT_USING_CONFIG_FLAGS)
+		set (_bld "")
+	  endif ()
+	  foreach (_opt IN LISTS ARGN)
+		set (_var "CMAKE_${lang}_FLAGS${_bld}")
+		#message (STATUS "Adding \"${_opt}\" to \${${_var}}")
+		# remove it first
+		string (REPLACE "${_opt}" "" _without "${${_var}}")
+		string (STRIP "${_without}" _without)
+		# we need to strip this one as well, so they are comparable
+		string (STRIP "${${_var}}" _stripped)
+		# if it wasn't there, then add it at the end
+		if ("${_without}" STREQUAL "${_stripped}")
+		  # don't add any extra spaces if no options yet are set
+		  if (NOT ${_stripped} STREQUAL "")
+			set (${_var} "${_stripped} ${_opt}")
+		  else (NOT ${_stripped} STREQUAL "")
+			set (${_var} "${_opt}")
+		  endif (NOT ${_stripped} STREQUAL "")
+		  set (${_var} "${${_var}}" PARENT_SCOPE)
+		endif ("${_without}" STREQUAL "${_stripped}")
+	  endforeach (_opt)
+	endforeach (build)
+  endforeach (lang)
+endfunction (add_options lang build)
+
+# set varname to flag unless user has specified something that matches regex
+function (set_default_option lang varname flag regex)
+  # lang is either C, CXX or Fortran
+  if ("${lang}" STREQUAL "Fortran")
+	set (letter "F")
+  else ()
+	set (letter "${lang}")
+  endif ()
+  string (TOUPPER "${CMAKE_BUILD_TYPE}" _build)
+  if ((NOT ("$ENV{${letter}FLAGS}" MATCHES "${regex}"))
+	  AND (NOT ("${CMAKE_${lang}_FLAGS}" MATCHES "${regex}"))
+	  AND (NOT ("${CMAKE_${lang}_FLAGS_${_build}}" MATCHES "${regex}")))
+	set (${varname} ${flag} PARENT_SCOPE)
+  else ()
+	set (${varname} PARENT_SCOPE)
+  endif ()
+endfunction (set_default_option)
+
+# clear default options as a proxy for not using any default options
+# at all. there is one *huge* problem with this: CMake runs the platform
+# initialization before executing any line at all in the project and
+# there seems to be no way to disable that behaviour, so we cannot really
+# distinguish between a platform default and something that the user has
+# passed on the command line. the best thing we can do is to all user-
+# defined setting if they are something other than the platform default.
+macro (no_default_options)
+  foreach (lang IN ITEMS C CXX Fortran)
+	foreach (build IN ITEMS DEBUG RELEASE MINSIZEREL RELWITHDEBINFO)
+	  if ("${CMAKE_${lang}_FLAGS_${build}}" STREQUAL "${CMAKE_${lang}_FLAGS_${build}_INIT}")
+		# for some strange reason we cannot clear this flag, only set it to empty
+		set (CMAKE_${lang}_FLAGS_${build} "")
+	  endif ()
+	endforeach (build)
+  endforeach (lang)
+endmacro (no_default_options)
diff --git a/CMakeModules/UseCompVer.cmake b/CMakeModules/UseCompVer.cmake
new file mode 100644
index 0000000..e8ac099
--- /dev/null
+++ b/CMakeModules/UseCompVer.cmake
@@ -0,0 +1,108 @@
+# - Get compiler version
+
+# probe the GCC version, returns empty string if GCC is not compiler
+function (get_gcc_version language ver_name)
+  if(CMAKE_${language}_COMPILER_ID STREQUAL GNU)  
+	# exec_program is deprecated, but execute_process does't work :-(
+	exec_program (${CMAKE_${language}_COMPILER}
+	  ARGS ${CMAKE_${language}_COMPILER_ARG1} -dumpversion
+	  OUTPUT_VARIABLE _version
+	  )
+	set (${ver_name} ${_version} PARENT_SCOPE)
+  else (CMAKE_${language}_COMPILER_ID STREQUAL GNU)
+	set (${ver_name} "" PARENT_SCOPE)
+  endif (CMAKE_${language}_COMPILER_ID STREQUAL GNU)
+endfunction (get_gcc_version ver_name)
+
+# less reliable, but includes the patch number
+function (get_gcc_patch language ver_name)
+  if(CMAKE_${language}_COMPILER_ID STREQUAL GNU)
+	# exec_program is deprecated, but execute_process does't work :-(
+	exec_program (${CMAKE_${language}_COMPILER}
+	  ARGS ${CMAKE_${language}_COMPILER_ARG1} --version
+	  OUTPUT_VARIABLE _version
+	  )
+	# split multi-line string into list
+	if (WIN32)
+	  string (REPLACE "\r\n" ";" _version "${_version}")
+	else (WIN32)
+	  string (REPLACE "\n" ";" _version "${_version}")
+	endif (WIN32)
+	# only keep first line
+	list (GET _version 0 _version)
+	# extract version number from it (this is the fragile part)
+	string (REGEX REPLACE "^[^\\(]+(\\([^\\)]*\\))?[\ \t]*([0-9]+\\.[0-9]+\\.[0-9]+)(.*\\(.*\\))?" "\\2" _version "${_version}")
+	# return this to the caller
+	set (${ver_name} ${_version} PARENT_SCOPE)
+  else (CMAKE_${language}_COMPILER_ID STREQUAL GNU)
+	set (${ver_name} "" PARENT_SCOPE)
+  endif (CMAKE_${language}_COMPILER_ID STREQUAL GNU)
+endfunction (get_gcc_patch language ver_name)
+
+function (compiler_info)
+  if (CMAKE_COMPILER_IS_GNUCXX)
+	get_gcc_patch (CXX version)
+	message (STATUS "GNU C++ compiler version: ${version}")
+  endif (CMAKE_COMPILER_IS_GNUCXX)
+endfunction (compiler_info)
+
+function (get_ld_version ver_name)
+  # run linker to get the version number. interestingly, this option works
+  # (for our purposes) on all major platforms (Linux, Mac OS X and Windows);
+  # it returns the program version although it may have ended in error
+  exec_program (${CMAKE_LINKER}
+	ARGS "-v"
+	OUTPUT_VARIABLE _version
+	)
+
+  # keep only first line, even on Mac OS X there is no line end
+  list (GET _version 0 _version)
+
+  # format of the version string is platform-specific
+  if (NOT WIN32)
+	if (APPLE)
+	  string (REGEX REPLACE ".*, from Apple (.*\)" "\\1" _version "${_version}")
+	else (APPLE)
+	  # assuming some GNU toolchain now
+	  string (REGEX REPLACE "GNU ([a-zA-Z0-9_]*) (version|\\(.*\\)) ([^\\ ]*).*" "\\1 \\3" _version "${_version}")
+	endif (APPLE)
+  endif (NOT WIN32)
+
+  # return the string to the caller
+  set (${ver_name} "${_version}" PARENT_SCOPE)
+endfunction (get_ld_version ver_name)
+
+function (linker_info)
+  get_ld_version (version)
+  message (STATUS "Linker: ${version}")
+endfunction (linker_info)
+
+# sets CXX_COMPAT_GCC if we have either GCC or Clang
+macro (is_compiler_gcc_compatible)
+  # is the C++ compiler clang++?
+  string (TOUPPER "${CMAKE_CXX_COMPILER_ID}" _comp_id)
+  if (_comp_id MATCHES "CLANG")
+	set (CMAKE_COMPILER_IS_CLANGXX TRUE)
+  else ()
+	set (CMAKE_COMPILER_IS_CLANGXX FALSE)
+  endif ()
+  # is the C++ compiler g++ or clang++?
+  if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANGXX)
+	set (CXX_COMPAT_GCC TRUE)
+  else ()
+	set (CXX_COMPAT_GCC FALSE)
+  endif ()
+  # is the C compiler clang?
+  string (TOUPPER "${CMAKE_C_COMPILER_ID}" _comp_id)
+  if (_comp_id MATCHES "CLANG")
+	set (CMAKE_COMPILER_IS_CLANG TRUE)
+  else ()
+	set (CMAKE_COMPILER_IS_CLANG FALSE)
+  endif ()
+  # is the C compiler gcc or clang?
+  if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG)
+	set (C_COMPAT_GCC TRUE)
+  else ()
+	set (C_COMPAT_GCC FALSE)
+  endif ()
+endmacro (is_compiler_gcc_compatible)
diff --git a/CMakeModules/UseDebugSymbols.cmake b/CMakeModules/UseDebugSymbols.cmake
new file mode 100644
index 0000000..dbe0a17
--- /dev/null
+++ b/CMakeModules/UseDebugSymbols.cmake
@@ -0,0 +1,139 @@
+# - Generate debug symbols in a separate file
+#
+# (1) Include this file in your CMakeLists.txt; it will setup everything
+#     to compile WITH debug symbols in any case.
+#
+# (2) Run the strip_debug_symbols function on every target that you want
+#     to strip.
+
+# Copyright (C) 2012 Uni Research AS
+# This code is licensed under The GNU General Public License v3.0
+
+include (AddOptions)
+include (UseCompVer)
+is_compiler_gcc_compatible ()
+
+# only debugging using the GNU toolchain is supported for now
+if (CXX_COMPAT_GCC)
+  # default debug level, if not specified by the user
+  set_default_option (CXX _dbg_flag "-ggdb3" "(^|\ )-g")
+
+  # add debug symbols to *all* targets, regardless. there WILL come a
+  # time when you need to find a bug which only manifests itself in a
+  # release target on a production system!
+  if (_dbg_flag)
+	message (STATUS "Generating debug symbols: ${_dbg_flag}")
+	add_options (ALL_LANGUAGES ALL_BUILDS "${_dbg_flag}")
+  endif (_dbg_flag)
+
+  # extracting the debug info is done by a separate utility in the GNU
+  # toolchain. check that this is actually installed.
+  message (STATUS "Looking for strip utility")
+  if (APPLE)
+	# MacOS X has a duo of utilities; we need both
+	find_program (OBJCOPY strip)
+	find_program (DSYMUTIL dsymutil)
+	mark_as_advanced (DSYMUTIL)
+	if (NOT DSYMUTIL)
+	  set (OBJCOPY dsymutil-NOTFOUND)
+	endif (NOT DSYMUTIL)
+  else (APPLE)
+	find_program (OBJCOPY
+	  objcopy
+	  ${CYGWIN_INSTALL_PATH}/bin /usr/bin /usr/local/bin
+	  )
+  endif (APPLE)
+  mark_as_advanced (OBJCOPY)
+  if (OBJCOPY)
+	message (STATUS "Looking for strip utility - found")
+  else (OBJCOPY)
+	message (WARNING "Looking for strip utility - not found")
+  endif (OBJCOPY)
+endif ()
+
+# command to separate the debug information from the executable into
+# its own file; this must be called for each target; optionally takes
+# the name of a variable to receive the list of .debug files
+function (strip_debug_symbols targets)
+  if (CXX_COMPAT_GCC AND OBJCOPY)
+	foreach (target IN LISTS targets)
+	  # libraries must retain the symbols in order to link to them, but
+	  # everything can be stripped in an executable
+	  get_target_property (_kind ${target} TYPE)
+
+	  # don't strip static libraries
+	  if ("${_kind}" STREQUAL "STATIC_LIBRARY")
+			return ()
+	  endif ("${_kind}" STREQUAL "STATIC_LIBRARY")
+
+	  # don't strip public symbols in shared objects
+	  if ("${_kind}" STREQUAL "EXECUTABLE")
+			set (_inst_dir bin)
+			set (_strip_args --strip-all --strip-unneeded)
+	  else ("${_kind}" STREQUAL "EXECUTABLE")
+			set(_inst_dir lib)
+			set (_strip_args --strip-debug --strip-unneeded)
+	  endif ("${_kind}" STREQUAL "EXECUTABLE")
+
+	  # add_custom_command doesn't support generator expressions in the
+	  # working_directory argument (sic; that's what you get when you do
+	  # ad hoc programming all the time), so we need to extract the
+	  # location up front (the location on the other hand should not be
+	  # used for libraries as it does not include the soversion -- sic
+	  # again)
+	  get_target_property (_full ${target} LOCATION)
+	  get_filename_component (_dir ${_full} PATH)
+	  if (NOT (("${_dir}" STREQUAL "") OR ("${_dir}" MATCHES ".*/$")))
+		set (_dir "${_dir}/")
+	  endif (NOT (("${_dir}" STREQUAL "") OR ("${_dir}" MATCHES ".*/$")))
+	  get_filename_component (_name ${_full} NAME_WE)
+	  get_filename_component (_ext ${_full} EXT)
+	  # only libraries have soversion property attached
+	  get_target_property (_target_soversion ${target} SOVERSION)
+	  get_target_property (_target_version ${target} VERSION)
+
+	  if (_target_soversion)
+		# MacOS X puts the version number before the extension
+		if (APPLE)
+		  set (_target_file_name "${_name}.${_target_version}${_ext}")
+		else (APPLE)
+		  set (_target_file_name "${_name}${_ext}.${_target_version}")
+		endif (APPLE)
+	  else (_target_soversion)
+			set (_target_file_name "${_name}${_ext}")
+	  endif (_target_soversion)
+
+	  set (_target_file "${_dir}${_target_file_name}")
+	  # do without generator expressions (which doesn't work everywhere)
+	  if (APPLE)
+			set (_debug_ext ".dSYM")
+			add_custom_command (TARGET ${target}
+				POST_BUILD
+				WORKING_DIRECTORY ${_dir}
+				COMMAND ${DSYMUTIL} ARGS --out=${_target_file}${_debug_ext} ${_target_file}
+				COMMAND ${OBJCOPY} ARGS -S ${_target_file}
+				COMMAND ${CMAKE_COMMAND} -E copy_if_different ${_target_file} ${CMAKE_BINARY_DIR}/install/${_inst_dir}
+				VERBATIM)
+	  else ()
+			set (_debug_ext ".debug")
+			add_custom_command (TARGET ${target}
+		  	POST_BUILD
+		  	WORKING_DIRECTORY ${_dir}
+		  	COMMAND ${OBJCOPY} ARGS --only-keep-debug ${_target_file} ${_target_file}${_debug_ext}
+		  	COMMAND ${OBJCOPY} ARGS ${_strip_args} ${_target_file}
+		  	COMMAND ${OBJCOPY} ARGS --add-gnu-debuglink=${_target_file_name}${_debug_ext} ${_target_file}
+				COMMAND ${CMAKE_COMMAND} -E copy_if_different ${_target_file} ${CMAKE_BINARY_DIR}/install/${_inst_dir}
+		  	VERBATIM)
+	  endif ()
+
+	  # add this .debug file to the list
+	  file (RELATIVE_PATH _this_debug_file "${PROJECT_BINARY_DIR}" "${_target_file}${_debug_ext}")
+	  set (_debug_files ${_debug_files} ${_this_debug_file})
+	endforeach (target)
+	# if optional debug list was requested, then copy to output parameter
+	if (ARGV1)
+	  set (${ARGV1} ${_debug_files} PARENT_SCOPE)
+	endif (ARGV1)
+  endif ()
+endfunction (strip_debug_symbols targets)
+
diff --git a/ChangeLog b/ChangeLog
index 57a102c..4ffb260 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,63 @@
+v1.2.10
+ o client ssl connection added. This requires a SSL_CTX and must be set by the user (439431a StunMan)
+ o Only export public symbols. (cf66c7c Mark Ellzey)
+ o Export evhtp_connection_set_ratelimit (df2fbd6 Mark Ellzey)
+ o Uncomplexify evthr - huge performance boost. (8a6873e Mark Ellzey)
+ o Do backlogs matter for evthr? I am thinking not. (76b4a96 Mark Ellzey)
+ o Remove all the stupid backlog stuff. (cb4e280 Mark Ellzey)
+ o Proposed changes for request pause/resume (pipelined) (6cd8946 Mark Ellzey)
+ o Remove dead code from evthr (3d51c76 Mark Ellzey)
+ o Remove dead declarations in evthr.h (72488a8 Mark Ellzey)
+ o Be more consistent and slightly more lenient when handling GET params (86ba10b TJ Koblentz)
+ o Empty query args processed with a val of NULL, extended the test_query code (bc897d2 Mark Ellzey)
+ o add connection connected status for client connection (0e839f0 zhangjing)
+ o Added on_event hook / cleanup (22c0fac Mark Ellzey)
+ o Fixed bug with calling user-defined event callback :) (246e33d Mark Ellzey)
+ o Add a pkg-config .pc file (6e85a9b Mark Ellzey)
+ o TestBigEndian, and add big endian versions of _str*cmp macros (736bc80 Mark Ellzey)
+ o ignore build directory (submodule doesnt become dirty on build) (1aceedb TJ Koblentz)
+ o missing SSL guard + test compilation problems (-Wunused-but-set-variable) (787eeb9 TJ Koblentz)
+ o Fix some empty GET param cases (with tests) (e282b6f TJ Koblentz)
+ o Formatting cleanup (89f11cd Mark Ellzey)
+ o Cleanup and fixes (61c7f4f Mark Ellzey)
+ o (evhtp_free): Free ssl_ctx if used. (9318571 Marcus Sundberg)
+ o Properly handle errors when allocating connections and requests. (ab2f567 Marcus Sundberg)
+ o Prevent double free of request. (ec445f9 Martin Hedenfalk)
+ o Stop parsing when we have got a complete response/request. (279a7d3 Marcus Sundberg)
+ o (evhtp_connection_free): Call hook before freeing request. (c093ff9 Marcus Sundberg)
+ o Properly parse CONNECT request line. (76f2830 Marcus Sundberg)
+ o Support IPv6 literals in CONNECT authority string. (378b790 Marcus Sundberg)
+ o Separate fragment string from query string according to RFC 3986. (a9c0679 Marcus Sundberg)
+ o (evhtp_parse_query): Remove strange handling of '?' and '/'. (3d96f5e Marcus Sundberg)
+ o Fix parse errors on trailing whitespace in Content-Length header. (b67068c Marcus Sundberg)
+ o (evhtp_parse_query): Advance state to s_query_key after start. (a535258 Marcus Sundberg)
+ o Add hooks for host and port, and fill in authority struct. (1be3a0f Marcus Sundberg)
+ o (_evhtp_path_new): If len is 0 we set full to "/", just like path. (7d277f8 Marcus Sundberg)
+ o Do not use different API/ABI when regexps are disabled. (39fcb28 Marcus Sundberg)
+ o Fix warnings in test.c when EVHTP_DISABLE_REGEX is defined. (05b01cf Marcus Sundberg)
+ o Add keepalive flag to connection. (74031a7 Marcus Sundberg)
+ o Add evhtp_hook_on_conn_error hook for connection errors. (6c3ed3d Marcus Sundberg)
+ o Added the function evhtp_connection_new_dns(). (47eecd0 Jan Edhner)
+ o (evhtp_connection_new_dns): Handle errors. (b13994b Marcus Sundberg)
+ o (evhtp_connection_new): Handle IPv6 addresses. (ac97672 Marcus Sundberg)
+ o (_evhtp_create_headers): Use evbuffer_expand() to reserve space. (c4ed326 Marcus Sundberg)
+ o Use malloc() instead of calloc() for buffers we will immediately fill. (3906a65 Marcus Sundberg)
+ o added padding for all structs containing bitfields (a2ebece Mark Ellzey)
+ o Update evthr.c (1d492cc romange)
+ o Cleanup, use EVHTP_DISABLE_SSL for client (c32562a Mark Ellzey)
+ o Various fixes, see full commit message (d8a4935 Mark Ellzey)
+ o Added evhp_set_flags along with some documentation (27b5e8a Mark Ellzey)
+ o Symbol exports moved into headers, more documentation (352aebe Mark Ellzey)
+ o If available, use c99 to our advantage (read commit msg) (8a44e6f Mark Ellzey)
+ o Remove duplicate evbuffer_new for buffer_in (5284ce3 Mark Ellzey)
+ o Fix for client connect double-free condition (5267ed2 Mark Ellzey)
+ o Don't set conn->bev to NULL if error (ce2197e Mark Ellzey)
+ o Use new and faster wildcard matching function. (4047f1e Mark Ellzey)
+ o Integer to string optimizations. (4189bfa Mark Ellzey)
+ o Export the numtoa functions. (037c766 Mark Ellzey)
+ o Disable unused tailq for client requests (for future use in pipelined) (3516276 Mark Ellzey)
+ o More conversions from free to safe_free (05fd68c Mark Ellzey)
+
 v1.2.9
  o Accept upper-case "Chunked" in addition to "chunked" for transfer encoding. (07a9322 Tim Burks)
  o [htparse] Added length checks for various header values (42050e8 Mark Ellzey)
diff --git a/build/placeholder b/build/placeholder
deleted file mode 100644
index e69de29..0000000
--- a/build/placeholder
+++ /dev/null
diff --git a/evhtp-internal.h b/evhtp-internal.h
new file mode 100644
index 0000000..7c893d1
--- /dev/null
+++ b/evhtp-internal.h
@@ -0,0 +1,24 @@
+#ifndef __EVHTP_INTERNAL_H__
+#define __EVHTP_INTERNAL_H__
+
+#ifdef EVHTP_HAS_VISIBILITY_HIDDEN
+#define __visible __attribute__((visibility("default")))
+#define EXPORT_SYMBOL(x)               typeof(x)(x)__visible
+#else
+#define EXPORT_SYMBOL(n)
+#endif
+
+#ifndef TAILQ_FOREACH_SAFE
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar)        \
+    for ((var) = TAILQ_FIRST((head));                     \
+         (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
+         (var) = (tvar))
+#endif
+
+#define evhtp_safe_free(_var, _freefn) do { \
+        _freefn((_var));                    \
+        (_var) = NULL;                      \
+}  while (0)
+
+#endif
+
diff --git a/evhtp.c b/evhtp.c
index d51ce5e..24ffd6d 100644
--- a/evhtp.c
+++ b/evhtp.c
@@ -21,10 +21,16 @@
 #endif
 
 #include <limits.h>
+#include <event2/dns.h>
 
+#include "evhtp-internal.h"
+#include "evhtp_numtoa.h"
 #include "evhtp.h"
 
+
 static int                  _evhtp_request_parser_start(htparser * p);
+static int                  _evhtp_request_parser_host(htparser * p, const char * data, size_t len);
+static int                  _evhtp_request_parser_port(htparser * p, const char * data, size_t len);
 static int                  _evhtp_request_parser_path(htparser * p, const char * data, size_t len);
 static int                  _evhtp_request_parser_args(htparser * p, const char * data, size_t len);
 static int                  _evhtp_request_parser_header_key(htparser * p, const char * data, size_t len);
@@ -45,9 +51,14 @@
 static evhtp_uri_t        * _evhtp_uri_new(void);
 static void                 _evhtp_uri_free(evhtp_uri_t * uri);
 
+static evhtp_authority_t  * _evhtp_authority_new(void);
+static void                 _evhtp_authority_free(evhtp_authority_t * authority);
+
 static evhtp_path_t       * _evhtp_path_new(const char * data, size_t len);
 static void                 _evhtp_path_free(evhtp_path_t * path);
 
+static void                 _evhtp_request_free(evhtp_request_t *);
+
 #define HOOK_AVAIL(var, hook_name)                 (var->hooks && var->hooks->hook_name)
 #define HOOK_FUNC(var, hook_name)                  (var->hooks->hook_name)
 #define HOOK_ARGS(var, hook_name)                  var->hooks->hook_name ## _arg
@@ -76,6 +87,21 @@
         }                                                                                     \
 } while (0);
 
+#define HOOK_CONN_RUN(conn, hook_name, ...)        do {                         \
+        if (conn->request) {                                                    \
+            evhtp_request_t * request = conn->request;                          \
+            if (HOOK_AVAIL(request, hook_name)) {                               \
+                return HOOK_FUNC(conn, hook_name) (conn, __VA_ARGS__,           \
+                                                   HOOK_ARGS(conn, hook_name)); \
+            }                                                                   \
+        }                                                                       \
+                                                                                \
+        if (HOOK_AVAIL(conn, hook_name)) {                                      \
+            return HOOK_FUNC(conn, hook_name) (conn, __VA_ARGS__,               \
+                                               HOOK_ARGS(conn, hook_name));     \
+        }                                                                       \
+} while (0);
+
 #ifndef EVHTP_DISABLE_EVTHR
 #define _evhtp_lock(h)                             do { \
         if (h->lock) {                                  \
@@ -100,7 +126,7 @@
          (var) = (tvar))
 #endif
 
-const char *
+static const char *
 status_code_to_str(evhtp_res code) {
     switch (code) {
         case EVHTP_RES_200:
@@ -209,8 +235,8 @@
     .on_msg_begin       = _evhtp_request_parser_start,
     .method             = NULL,
     .scheme             = NULL,
-    .host               = NULL,
-    .port               = NULL,
+    .host               = _evhtp_request_parser_host,
+    .port               = _evhtp_request_parser_port,
     .path               = _evhtp_request_parser_path,
     .args               = _evhtp_request_parser_args,
     .uri                = NULL,
@@ -267,7 +293,8 @@
     ret    = malloc(n + 1);
     ret[n] = '\0';
 
-    strncpy(ret, s, n);
+    memcpy(ret, s, n);
+
     return ret;
 }
 
@@ -296,21 +323,17 @@
 }
 
 /**
- * @brief helper function to determine if http version is HTTP/1.0
+ *
+ * @brief helper macro to determine if http version is HTTP/1.0
  *
  * @param major the major version number
  * @param minor the minor version number
  *
  * @return 1 if HTTP/1.0, else 0
  */
-static inline int
-_evhtp_is_http_10(const char major, const char minor) {
-    if (major >= 1 && minor <= 0) {
-        return 1;
-    }
 
-    return 0;
-}
+#define _evhtp_is_http_11(_major, _minor) \
+    (_major >= 1 && _minor >= 1)
 
 /**
  * @brief helper function to determine if http version is HTTP/1.1
@@ -320,14 +343,9 @@
  *
  * @return 1 if HTTP/1.1, else 0
  */
-static inline int
-_evhtp_is_http_11(const char major, const char minor) {
-    if (major >= 1 && minor >= 1) {
-        return 1;
-    }
+#define _evhtp_is_http_10(_major, _minor) \
+    (_major >= 1 && _minor <= 0)
 
-    return 0;
-}
 
 /**
  * @brief returns the HTTP protocol version
@@ -477,6 +495,36 @@
     return EVHTP_RES_OK;
 }
 
+/**
+ * @brief runs the user-defined hook when a connection error occurs
+ *
+ * @param request the request structure
+ * @param errtype the error that ocurred
+ */
+static inline void
+_evhtp_error_hook(evhtp_request_t * request, evhtp_error_flags errtype) {
+    if (request && request->hooks && request->hooks->on_error) {
+        (*request->hooks->on_error)(request, errtype,
+                                    request->hooks->on_error_arg);
+    }
+}
+
+/**
+ * @brief runs the user-defined hook when a connection error occurs
+ *
+ * @param connection the connection structure
+ * @param errtype the error that ocurred
+ */
+static inline evhtp_res
+_evhtp_connection_error_hook(evhtp_connection_t * connection, evhtp_error_flags errtype) {
+    if (connection->request) {
+        _evhtp_error_hook(connection->request, errtype);
+    }
+    HOOK_CONN_RUN(connection, on_connection_error, errtype);
+
+    return EVHTP_RES_OK;
+}
+
 static inline evhtp_res
 _evhtp_hostname_hook(evhtp_request_t * r, const char * hostname) {
     HOOK_REQUEST_RUN(r, on_hostname, hostname);
@@ -494,6 +542,62 @@
     return EVHTP_RES_OK;
 }
 
+static int
+_evhtp_glob_match2(const char * pattern, size_t plen,
+                   const char * string, size_t str_len) {
+    while (plen) {
+        switch (pattern[0]) {
+            case '*':
+                while (pattern[1] == '*') {
+                    pattern++;
+                    plen--;
+                }
+
+                if (plen == 1) {
+                    return 1;     /* match */
+                }
+
+                while (str_len) {
+                    if (_evhtp_glob_match2(pattern + 1, plen - 1,
+                                           string, str_len)) {
+                        return 1; /* match */
+                    }
+
+                    string++;
+                    str_len--;
+                }
+
+                return 0;         /* no match */
+            default:
+                if (pattern[0] != string[0]) {
+                    return 0;     /* no match */
+                }
+
+                string++;
+                str_len--;
+                break;
+        } /* switch */
+
+        pattern++;
+        plen--;
+
+        if (str_len == 0) {
+            while (*pattern == '*') {
+                pattern++;
+                plen--;
+            }
+
+            break;
+        }
+    }
+
+    if (plen == 0 && str_len == 0) {
+        return 1;
+    }
+
+    return 0;
+} /* _evhtp_glob_match2 */
+
 /**
  * @brief glob/wildcard type pattern matching.
  *
@@ -504,18 +608,24 @@
  *
  * @return
  */
-static int
-_evhtp_glob_match(const char * pattern, const char * string) {
-    size_t pat_len;
-    size_t str_len;
-
+static inline int
+_evhtp_glob_match(const char * pattern, size_t pat_len, const char * string, size_t str_len) {
     if (!pattern || !string) {
         return 0;
     }
 
-    pat_len = strlen(pattern);
-    str_len = strlen(string);
+    if (pat_len == 0) {
+        pat_len = strlen(pattern);
+    }
 
+    if (str_len == 0) {
+        str_len = strlen(string);
+    }
+
+    /* XXX still in testing */
+    return _evhtp_glob_match2(pattern, pat_len, string, str_len);
+
+#if 0
     while (pat_len) {
         if (pattern[0] == '*') {
             while (pattern[1] == '*') {
@@ -528,7 +638,7 @@
             }
 
             while (str_len) {
-                if (_evhtp_glob_match(pattern + 1, string)) {
+                if (_evhtp_glob_match(pattern + 1, pat_len, string, str_len)) {
                     return 1;
                 }
 
@@ -563,6 +673,7 @@
     }
 
     return 0;
+#endif
 } /* _evhtp_glob_match */
 
 static evhtp_callback_t *
@@ -585,6 +696,7 @@
                 if (strcmp(callback->val.path, path) == 0) {
                     *start_offset = 0;
                     *end_offset   = (unsigned int)strlen(path);
+
                     return callback;
                 }
                 break;
@@ -600,11 +712,18 @@
                 break;
 #endif
             case evhtp_callback_type_glob:
-                if (_evhtp_glob_match(callback->val.glob, path) == 1) {
+            {
+                size_t path_len = strlen(path);
+                size_t glob_len = strlen(callback->val.glob);
+
+                if (_evhtp_glob_match(callback->val.glob, glob_len,
+                                      path, path_len) == 1) {
                     *start_offset = 0;
-                    *end_offset   = (unsigned int)strlen(path);
+                    *end_offset   = (unsigned int)path_len;
+
                     return callback;
                 }
+            }
             default:
                 break;
         } /* switch */
@@ -623,24 +742,48 @@
 static evhtp_request_t *
 _evhtp_request_new(evhtp_connection_t * c) {
     evhtp_request_t * req;
+    uint8_t           error;
 
     if (!(req = calloc(sizeof(evhtp_request_t), 1))) {
         return NULL;
     }
 
-    req->conn        = c;
-    req->htp         = c ? c->htp : NULL;
-    req->status      = EVHTP_RES_OK;
-    req->buffer_in   = evbuffer_new();
-    req->buffer_out  = evbuffer_new();
-    req->headers_in  = malloc(sizeof(evhtp_headers_t));
-    req->headers_out = malloc(sizeof(evhtp_headers_t));
+    error       = 1;
+    req->conn   = c;
+    req->htp    = c ? c->htp : NULL;
+    req->status = EVHTP_RES_OK;
 
-    TAILQ_INIT(req->headers_in);
-    TAILQ_INIT(req->headers_out);
+    do {
+        if (!(req->buffer_in = evbuffer_new())) {
+            break;
+        }
 
-    return req;
-}
+        if (!(req->buffer_out = evbuffer_new())) {
+            break;
+        }
+
+        if (!(req->headers_in = malloc(sizeof(evhtp_headers_t)))) {
+            break;
+        }
+
+        if (!(req->headers_out = malloc(sizeof(evhtp_headers_t)))) {
+            break;
+        }
+
+        TAILQ_INIT(req->headers_in);
+        TAILQ_INIT(req->headers_out);
+
+        error = 0;
+    } while (0);
+
+    if (error == 0) {
+        return req;
+    }
+
+    _evhtp_request_free(req);
+
+    return NULL;
+} /* _evhtp_request_new */
 
 /**
  * @brief frees all data in an evhtp_request_t along with calling finished hooks
@@ -659,6 +802,9 @@
     evhtp_headers_free(request->headers_in);
     evhtp_headers_free(request->headers_out);
 
+    if (request->conn && request->conn->request == request) {
+        request->conn->request = NULL;
+    }
 
     if (request->buffer_in) {
         evbuffer_free(request->buffer_in);
@@ -685,10 +831,52 @@
         return NULL;
     }
 
+    uri->authority = _evhtp_authority_new();
+
+    if (!uri->authority) {
+        _evhtp_uri_free(uri);
+
+        return NULL;
+    }
+
     return uri;
 }
 
 /**
+ * @brief frees an authority structure
+ *
+ * @param authority evhtp_authority_t
+ */
+static void
+_evhtp_authority_free(evhtp_authority_t * authority) {
+    if (authority == NULL) {
+        return;
+    }
+
+    free(authority->username);
+    free(authority->password);
+    free(authority->hostname);
+
+    free(authority);
+}
+
+/**
+ * @brief create an authority structure
+ *
+ * @return evhtp_authority_t
+ */
+static evhtp_authority_t *
+_evhtp_authority_new(void) {
+    evhtp_authority_t * authority;
+
+    if (!(authority = calloc(1, sizeof(*authority)))) {
+        return NULL;
+    }
+
+    return authority;
+}
+
+/**
  * @brief frees an overlay URI structure
  *
  * @param uri evhtp_uri_t
@@ -700,7 +888,9 @@
     }
 
     evhtp_query_free(uri->query);
+
     _evhtp_path_free(uri->path);
+    _evhtp_authority_free(uri->authority);
 
     free(uri->fragment);
     free(uri->query_raw);
@@ -766,15 +956,15 @@
 
                     /* check for overflow */
                     if ((const char *)(data + path_len) > data_end) {
-                        fprintf(stderr, "PATH Corrupted.. (path_len > len)\n");
                         free(req_path);
+
                         return NULL;
                     }
 
                     /* check for overflow */
                     if ((const char *)(&data[i + 1] + file_len) > data_end) {
-                        fprintf(stderr, "FILE Corrupted.. (file_len > len)\n");
                         free(req_path);
+
                         return NULL;
                     }
 
@@ -801,6 +991,8 @@
 
     if (len != 0) {
         req_path->full = strndup(data, len);
+    } else {
+        req_path->full = strdup("/");
     }
 
     req_path->path = path;
@@ -856,6 +1048,8 @@
 _evhtp_request_parser_args(htparser * p, const char * data, size_t len) {
     evhtp_connection_t * c   = htparser_get_userdata(p);
     evhtp_uri_t        * uri = c->request->uri;
+    const char         * fragment;
+    int                  ignore_fragment;
 
     if (c->type == evhtp_type_client) {
         /* as a client, technically we should never get here, but just in case
@@ -864,16 +1058,61 @@
         return 0;
     }
 
-    if (!(uri->query = evhtp_parse_query(data, len))) {
+
+    /* if the parser flags has the IGNORE_FRAGMENTS bit set, skip
+     * the fragment parsing
+     */
+    ignore_fragment = (c->htp->parser_flags &
+                       EVHTP_PARSE_QUERY_FLAG_IGNORE_FRAGMENTS);
+
+
+    if (!ignore_fragment && (fragment = memchr(data, '#', len))) {
+        /* Separate fragment from query according to RFC 3986.
+         *
+         * XXX: not happy about using strchr stuff, maybe this functionality
+         * is more apt as part of evhtp_parse_query()
+         */
+
+        ptrdiff_t frag_offset;
+
+        frag_offset = fragment - data;
+
+        if (frag_offset < len) {
+            size_t fraglen;
+
+            /* Skip '#'. */
+            fragment    += 1;
+            frag_offset += 1;
+            fraglen      = len - frag_offset;
+
+            if (!(uri->fragment = malloc(fraglen + 1))) {
+                c->request->status = EVHTP_RES_ERROR;
+
+                return -1;
+            }
+
+            memcpy(uri->fragment, fragment, fraglen);
+
+            uri->fragment[fraglen] = '\0';
+            len -= fraglen + 1; /* Skip '#' + fragment string. */
+        }
+    }
+
+    uri->query = evhtp_parse_query_wflags(data, len, c->htp->parser_flags);
+
+    if (!uri->query) {
         c->request->status = EVHTP_RES_ERROR;
+
         return -1;
     }
 
-    uri->query_raw = calloc(len + 1, 1);
+    uri->query_raw      = malloc(len + 1);
     memcpy(uri->query_raw, data, len);
 
+    uri->query_raw[len] = '\0';
+
     return 0;
-}
+} /* _evhtp_request_parser_args */
 
 static int
 _evhtp_request_parser_headers_start(htparser * p) {
@@ -889,7 +1128,7 @@
 static int
 _evhtp_request_parser_header_key(htparser * p, const char * data, size_t len) {
     evhtp_connection_t * c = htparser_get_userdata(p);
-    char               * key_s;     /* = strndup(data, len); */
+    char               * key_s;
     evhtp_header_t     * hdr;
 
     key_s      = malloc(len + 1);
@@ -898,10 +1137,12 @@
 
     if ((hdr = evhtp_header_key_add(c->request->headers_in, key_s, 0)) == NULL) {
         c->request->status = EVHTP_RES_FATAL;
+
         return -1;
     }
 
     hdr->k_heaped = 1;
+
     return 0;
 }
 
@@ -918,6 +1159,7 @@
     if ((header = evhtp_header_val_add(c->request->headers_in, val_s, 0)) == NULL) {
         free(val_s);
         c->request->status = EVHTP_RES_FATAL;
+
         return -1;
     }
 
@@ -940,7 +1182,7 @@
             continue;
         }
 
-        if (_evhtp_glob_match(evhtp_vhost->server_name, name) == 1) {
+        if (_evhtp_glob_match(evhtp_vhost->server_name, 0, name, 0) == 1) {
             return evhtp_vhost;
         }
 
@@ -949,7 +1191,7 @@
                 continue;
             }
 
-            if (_evhtp_glob_match(evhtp_alias->alias, name) == 1) {
+            if (_evhtp_glob_match(evhtp_alias->alias, 0, name, 0) == 1) {
                 return evhtp_vhost;
             }
         }
@@ -1104,27 +1346,82 @@
 } /* _evhtp_request_parser_hostname */
 
 static int
+_evhtp_require_uri(evhtp_connection_t * c) {
+    if (c && c->request && !c->request->uri) {
+        if (!(c->request->uri = _evhtp_uri_new())) {
+            c->request->status = EVHTP_RES_FATAL;
+
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int
+_evhtp_request_parser_host(htparser * p, const char * data, size_t len) {
+    evhtp_connection_t * c = htparser_get_userdata(p);
+    evhtp_authority_t  * authority;
+
+    if (_evhtp_require_uri(c) != 0) {
+        return -1;
+    }
+
+    authority           = c->request->uri->authority;
+    authority->hostname = strndup(data, len);
+
+    if (!authority->hostname) {
+        c->request->status = EVHTP_RES_FATAL;
+
+        return -1;
+    }
+
+    return 0;
+}
+
+static int
+_evhtp_request_parser_port(htparser * p, const char * data, size_t len) {
+    evhtp_connection_t * c = htparser_get_userdata(p);
+    evhtp_authority_t  * authority;
+    char               * endptr;
+    unsigned long        port;
+
+    if (_evhtp_require_uri(c) != 0) {
+        return -1;
+    }
+
+    authority = c->request->uri->authority;
+    port      = strtoul(data, &endptr, 10);
+
+    if (endptr - data != len || port > 65535) {
+        c->request->status = EVHTP_RES_FATAL;
+
+        return -1;
+    }
+
+    authority->port = port;
+
+    return 0;
+}
+
+static int
 _evhtp_request_parser_path(htparser * p, const char * data, size_t len) {
     evhtp_connection_t * c = htparser_get_userdata(p);
-    evhtp_uri_t        * uri;
     evhtp_path_t       * path;
 
-    if (!(uri = _evhtp_uri_new())) {
-        c->request->status = EVHTP_RES_FATAL;
+    if (_evhtp_require_uri(c) != 0) {
         return -1;
     }
 
     if (!(path = _evhtp_path_new(data, len))) {
-        _evhtp_uri_free(uri);
         c->request->status = EVHTP_RES_FATAL;
+
         return -1;
     }
 
-    uri->path          = path;
-    uri->scheme        = htparser_get_scheme(p);
-
-    c->request->method = htparser_get_method(p);
-    c->request->uri    = uri;
+    c->request->uri->path   = path;
+    c->request->uri->scheme = htparser_get_scheme(p);
+    c->request->method      = htparser_get_method(p);
 
     _evhtp_lock(c->htp);
     {
@@ -1161,9 +1458,9 @@
         }
 
         evbuffer_add_printf(bufferevent_get_output(c->bev),
-                            "HTTP/%d.%d 100 Continue\r\n\r\n",
-                            htparser_get_major(p),
-                            htparser_get_minor(p));
+                            "HTTP/%c.%c 100 Continue\r\n\r\n",
+                            evhtp_modp_uchartoa(htparser_get_major(p)),
+                            evhtp_modp_uchartoa(htparser_get_minor(p)));
     }
 
     return 0;
@@ -1282,6 +1579,10 @@
 _evhtp_request_parser_fini(htparser * p) {
     evhtp_connection_t * c = htparser_get_userdata(p);
 
+    if (c->paused == 1) {
+        return -1;
+    }
+
     /* check to see if we should use the body of the request as the query
      * arguments.
      */
@@ -1314,53 +1615,57 @@
         (c->request->cb)(c->request, c->request->cbarg);
     }
 
+    if (c->paused == 1) {
+        return -1;
+    }
+
     return 0;
-}
+} /* _evhtp_request_parser_fini */
 
 static int
 _evhtp_create_headers(evhtp_header_t * header, void * arg) {
     evbuf_t * buf = arg;
 
+    evbuffer_expand(buf, header->klen + 2 + header->vlen + 2);
     evbuffer_add(buf, header->key, header->klen);
     evbuffer_add(buf, ": ", 2);
     evbuffer_add(buf, header->val, header->vlen);
     evbuffer_add(buf, "\r\n", 2);
+
     return 0;
 }
 
 static evbuf_t *
 _evhtp_create_reply(evhtp_request_t * request, evhtp_res code) {
-    evbuf_t    * buf          = evbuffer_new();
-    const char * content_type = evhtp_header_find(request->headers_out, "Content-Type");
-    char         res_buf[1024];
-    int          sres;
+    evbuf_t     * buf;
+    const char  * content_type;
+    char          res_buf[2048];
+    int           sres;
+    size_t        out_len;
+    unsigned char major;
+    unsigned char minor;
+    char          out_buf[64];
+
+
+    content_type = evhtp_header_find(request->headers_out, "Content-Type");
+    out_len      = evbuffer_get_length(request->buffer_out);
+    buf          = evbuffer_new();
 
     if (htparser_get_multipart(request->conn->parser) == 1) {
         goto check_proto;
     }
 
-    if (evbuffer_get_length(request->buffer_out) && request->chunked == 0) {
+    if (out_len && request->chunked == 0) {
         /* add extra headers (like content-length/type) if not already present */
 
         if (!evhtp_header_find(request->headers_out, "Content-Length")) {
-            char lstr[128];
-#ifndef WIN32
-            sres = snprintf(lstr, sizeof(lstr), "%zu",
-                            evbuffer_get_length(request->buffer_out));
-#else
-            sres = snprintf(lstr, sizeof(lstr), "%u",
-                            evbuffer_get_length(request->buffer_out));
-#endif
-
-            if (sres >= sizeof(lstr) || sres < 0) {
-                /* overflow condition, this should never happen, but if it does,
-                 * well lets just shut the connection down */
-                request->keepalive = 0;
-                goto check_proto;
-            }
+            /* convert the buffer_out length to a string and set
+             * and add the new Content-Length header.
+             */
+            evhtp_modp_sizetoa(out_len, out_buf);
 
             evhtp_headers_add_header(request->headers_out,
-                                     evhtp_header_new("Content-Length", lstr, 0, 1));
+                                     evhtp_header_new("Content-Length", out_buf, 0, 1));
         }
 
         if (!content_type) {
@@ -1411,18 +1716,20 @@
      * we fallback to using evbuffer_add_printf().
      */
 
-    sres = snprintf(res_buf, sizeof(res_buf), "HTTP/%d.%d %d %s\r\n",
-                    htparser_get_major(request->conn->parser),
-                    htparser_get_minor(request->conn->parser),
-                    code, status_code_to_str(code));
+    major = evhtp_modp_uchartoa(htparser_get_major(request->conn->parser));
+    minor = evhtp_modp_uchartoa(htparser_get_minor(request->conn->parser));
+
+    evhtp_modp_u32toa((uint32_t)code, out_buf);
+
+    sres  = snprintf(res_buf, sizeof(res_buf), "HTTP/%c.%c %s %s\r\n",
+                     major, minor, out_buf, status_code_to_str(code));
 
     if (sres >= sizeof(res_buf) || sres < 0) {
         /* failed to fit the whole thing in the res_buf, so just fallback to
          * using evbuffer_add_printf().
          */
-        evbuffer_add_printf(buf, "HTTP/%d.%d %d %s\r\n",
-                            htparser_get_major(request->conn->parser),
-                            htparser_get_minor(request->conn->parser),
+        evbuffer_add_printf(buf, "HTTP/%c.%c %d %s\r\n",
+                            major, minor,
                             code, status_code_to_str(code));
     } else {
         /* copy the res_buf using evbuffer_add() instead of add_printf() */
@@ -1446,18 +1753,30 @@
 
     c->paused = 0;
 
-    bufferevent_enable(c->bev, EV_READ);
-
     if (c->request) {
         c->request->status = EVHTP_RES_OK;
     }
 
     if (c->free_connection == 1) {
         evhtp_connection_free(c);
+
         return;
     }
 
-    _evhtp_connection_readcb(c->bev, c);
+    /* XXX this is a hack to show a potential fix for issues/86, the main indea
+     * is that you call resume AFTER you have sent the reply (not BEFORE).
+     *
+     * When it has been decided this is a proper fix, the pause bit should be
+     * changed to a state-type flag.
+     */
+
+    if (evbuffer_get_length(bufferevent_get_output(c->bev))) {
+        bufferevent_enable(c->bev, EV_WRITE);
+        c->waiting = 1;
+    } else {
+        bufferevent_enable(c->bev, EV_READ | EV_WRITE);
+        _evhtp_connection_readcb(c->bev, c);
+    }
 }
 
 static void
@@ -1469,6 +1788,10 @@
 
     avail = evbuffer_get_length(bufferevent_get_input(bev));
 
+    if (avail == 0) {
+        return;
+    }
+
     if (c->request) {
         c->request->status = EVHTP_RES_OK;
     }
@@ -1477,13 +1800,9 @@
         return;
     }
 
-    buf = evbuffer_pullup(bufferevent_get_input(bev), avail);
+    buf   = evbuffer_pullup(bufferevent_get_input(bev), avail);
 
-    bufferevent_disable(bev, EV_WRITE);
-    {
-        nread = htparser_run(c->parser, &request_psets, (const char *)buf, avail);
-    }
-    bufferevent_enable(bev, EV_WRITE);
+    nread = htparser_run(c->parser, &request_psets, (const char *)buf, avail);
 
     if (c->owner != 1) {
         /*
@@ -1492,16 +1811,16 @@
          */
         evbuffer_drain(bufferevent_get_input(bev), nread);
         evhtp_connection_free(c);
+
         return;
     }
 
     if (c->request) {
         switch (c->request->status) {
             case EVHTP_RES_DATA_TOO_LONG:
-                if (c->request->hooks && c->request->hooks->on_error) {
-                    (*c->request->hooks->on_error)(c->request, -1, c->request->hooks->on_error_arg);
-                }
+                _evhtp_connection_error_hook(c, -1);
                 evhtp_connection_free(c);
+
                 return;
             default:
                 break;
@@ -1512,7 +1831,7 @@
 
     if (c->request && c->request->status == EVHTP_RES_PAUSE) {
         evhtp_request_pause(c->request);
-    } else if (avail != nread) {
+    } else if (htparser_get_error(c->parser) != htparse_error_none) {
         evhtp_connection_free(c);
     }
 } /* _evhtp_connection_readcb */
@@ -1531,10 +1850,23 @@
         return;
     }
 
+    if (c->waiting == 1) {
+        c->waiting = 0;
+
+        bufferevent_enable(bev, EV_READ);
+
+        if (evbuffer_get_length(bufferevent_get_input(bev))) {
+            _evhtp_connection_readcb(bev, arg);
+        }
+
+        return;
+    }
+
     if (c->request->finished == 0 || evbuffer_get_length(bufferevent_get_output(bev))) {
         return;
     }
 
+
     /*
      * if there is a set maximum number of keepalive requests configured, check
      * to make sure we are not over it. If we have gone over the max we set the
@@ -1546,9 +1878,10 @@
         }
     }
 
-    if (c->request->keepalive) {
+    if (c->request->keepalive == 1) {
         _evhtp_request_free(c->request);
 
+        c->keepalive       = 1;
         c->request         = NULL;
         c->body_bytes_read = 0;
 
@@ -1567,9 +1900,11 @@
 
 
         htparser_set_userdata(c->parser, c);
+
         return;
     } else {
         evhtp_connection_free(c);
+
         return;
     }
 
@@ -1580,8 +1915,13 @@
 _evhtp_connection_eventcb(evbev_t * bev, short events, void * arg) {
     evhtp_connection_t * c = arg;
 
+    if (c->hooks && c->hooks->on_event) {
+        (c->hooks->on_event)(c, events, c->hooks->on_event_arg);
+    }
+
     if ((events & BEV_EVENT_CONNECTED)) {
         if (c->type == evhtp_type_client) {
+            c->connected = 1;
             bufferevent_setcb(bev,
                               _evhtp_connection_readcb,
                               _evhtp_connection_writecb,
@@ -1591,6 +1931,7 @@
         return;
     }
 
+#ifndef EVHTP_DISABLE_SSL
     if (c->ssl && !(events & BEV_EVENT_EOF)) {
         /* XXX need to do better error handling for SSL specific errors */
         c->error = 1;
@@ -1599,6 +1940,7 @@
             c->request->error = 1;
         }
     }
+#endif
 
     if (events == (BEV_EVENT_EOF | BEV_EVENT_READING)) {
         if (errno == EAGAIN) {
@@ -1612,17 +1954,15 @@
              */
             bufferevent_enable(bev, EV_READ);
             errno = 0;
+
             return;
         }
     }
 
-    c->error = 1;
+    c->error     = 1;
+    c->connected = 0;
 
-    if (c->request && c->request->hooks && c->request->hooks->on_error) {
-        (*c->request->hooks->on_error)(c->request, events,
-                                       c->request->hooks->on_error_arg);
-    }
-
+    _evhtp_connection_error_hook(c, events);
 
     if (c->paused == 1) {
         c->free_connection = 1;
@@ -1657,6 +1997,7 @@
 
     if (_evhtp_run_pre_accept(connection->htp, connection) < 0) {
         evutil_closesocket(connection->sock);
+
         return -1;
     }
 
@@ -1731,6 +2072,7 @@
             ptype = htp_type_request;
             break;
         default:
+
             return NULL;
     }
 
@@ -1738,21 +2080,30 @@
         return NULL;
     }
 
-    connection->error  = 0;
-    connection->owner  = 1;
-    connection->paused = 0;
-    connection->sock   = sock;
-    connection->htp    = htp;
-    connection->type   = type;
-    connection->parser = htparser_new();
+    connection->error     = 0;
+    connection->owner     = 1;
+    connection->paused    = 0;
+    connection->connected = 0;
+    connection->sock      = sock;
+    connection->htp       = htp;
+    connection->type      = type;
+    connection->parser    = htparser_new();
+
+    if (!connection->parser) {
+        evhtp_safe_free(connection, free);
+
+        return NULL;
+    }
 
     htparser_init(connection->parser, ptype);
     htparser_set_userdata(connection->parser, connection);
 
+#ifdef EVHTP_FUTURE_USE
     TAILQ_INIT(&connection->pending);
+#endif
 
     return connection;
-}
+} /* _evhtp_connection_new */
 
 #ifdef LIBEVENT_HAS_SHUTDOWN
 #ifndef EVHTP_DISABLE_SSL
@@ -1791,15 +2142,15 @@
     connection->evbase = evthr_get_base(thr);
     connection->thread = thr;
 
-    evthr_inc_backlog(connection->thread);
-
     if (_evhtp_connection_accept(connection->evbase, connection) < 0) {
         evhtp_connection_free(connection);
+
         return;
     }
 
     if (_evhtp_run_post_accept(htp, connection) < 0) {
         evhtp_connection_free(connection);
+
         return;
     }
 }
@@ -1823,8 +2174,10 @@
         if (evthr_pool_defer(htp->thr_pool, _evhtp_run_in_thread, connection) != EVTHR_RES_OK) {
             evutil_closesocket(connection->sock);
             evhtp_connection_free(connection);
+
             return;
         }
+
         return;
     }
 #endif
@@ -1832,11 +2185,13 @@
 
     if (_evhtp_connection_accept(htp->evbase, connection) < 0) {
         evhtp_connection_free(connection);
+
         return;
     }
 
     if (_evhtp_run_post_accept(htp, connection) < 0) {
         evhtp_connection_free(connection);
+
         return;
     }
 }
@@ -1846,8 +2201,10 @@
 static unsigned long
 _evhtp_ssl_get_thread_id(void) {
 #ifndef WIN32
+
     return (unsigned long)pthread_self();
 #else
+
     return (unsigned long)(pthread_self().p);
 #endif
 }
@@ -1979,10 +2336,11 @@
  */
 void
 evhtp_connection_pause(evhtp_connection_t * c) {
-    if ((bufferevent_get_enabled(c->bev) & EV_READ)) {
-        c->paused = 1;
-        bufferevent_disable(c->bev, EV_READ);
-    }
+    c->paused = 1;
+
+    bufferevent_disable(c->bev, EV_READ | EV_WRITE);
+
+    return;
 }
 
 /**
@@ -1992,11 +2350,11 @@
  */
 void
 evhtp_connection_resume(evhtp_connection_t * c) {
-    if (!(bufferevent_get_enabled(c->bev) & EV_READ)) {
-        /* bufferevent_enable(c->bev, EV_READ); */
-        c->paused = 0;
-        event_active(c->resume_ev, EV_WRITE, 1);
-    }
+    c->paused = 0;
+
+    event_active(c->resume_ev, EV_WRITE, 1);
+
+    return;
 }
 
 /**
@@ -2073,6 +2431,7 @@
     evhtp_kvs_t * kvs = malloc(sizeof(evhtp_kvs_t));
 
     TAILQ_INIT(kvs);
+
     return kvs;
 }
 
@@ -2095,10 +2454,17 @@
         kv->klen = strlen(key);
 
         if (kalloc == 1) {
-            char * s = malloc(kv->klen + 1);
+            char * s;
+
+            if (!(s = malloc(kv->klen + 1))) {
+                evhtp_safe_free(kv, free);
+
+                return NULL;
+            }
+
+            memcpy(s, key, kv->klen);
 
             s[kv->klen] = '\0';
-            memcpy(s, key, kv->klen);
             kv->key     = s;
         } else {
             kv->key = (char *)key;
@@ -2129,14 +2495,14 @@
     }
 
     if (kv->k_heaped) {
-        free(kv->key);
+        evhtp_safe_free(kv->key, free);
     }
 
     if (kv->v_heaped) {
-        free(kv->val);
+        evhtp_safe_free(kv->val, free);
     }
 
-    free(kv);
+    evhtp_safe_free(kv, free);
 }
 
 void
@@ -2167,7 +2533,7 @@
         evhtp_kv_free(kv);
     }
 
-    free(kvs);
+    evhtp_safe_free(kvs, free);
 }
 
 int
@@ -2247,7 +2613,6 @@
 
 typedef enum {
     s_query_start = 0,
-    s_query_question_mark,
     s_query_separator,
     s_query_key,
     s_query_val,
@@ -2277,8 +2642,10 @@
         case '7':
         case '8':
         case '9':
+
             return 1;
         default:
+
             return 0;
     } /* switch */
 }
@@ -2365,29 +2732,42 @@
 }         /* evhtp_unescape_string */
 
 evhtp_query_t *
-evhtp_parse_query(const char * query, size_t len) {
+evhtp_parse_query_wflags(const char * query, size_t len, int flags) {
     evhtp_query_t    * query_args;
-    query_parser_state state   = s_query_start;
-    char             * key_buf = NULL;
-    char             * val_buf = NULL;
-    int                key_idx;
-    int                val_idx;
+    query_parser_state state;
+    size_t             key_idx;
+    size_t             val_idx;
     unsigned char      ch;
     size_t             i;
 
+
+    if (len > (SIZE_MAX - (len + 2))) {
+        return NULL;
+    }
+
     query_args = evhtp_query_new();
 
+    state      = s_query_start;
+    key_idx    = 0;
+    val_idx    = 0;
+
+#ifdef EVHTP_HAS_C99
+    char   key_buf[len + 1];
+    char   val_buf[len + 1];
+#else
+    char * key_buf;
+    char * val_buf;
+
     if (!(key_buf = malloc(len + 1))) {
         return NULL;
     }
 
     if (!(val_buf = malloc(len + 1))) {
-        free(key_buf);
+        evhtp_safe_free(key_buf, free);
+
         return NULL;
     }
-
-    key_idx = 0;
-    val_idx = 0;
+#endif
 
     for (i = 0; i < len; i++) {
         ch = query[i];
@@ -2398,38 +2778,14 @@
 
         switch (state) {
             case s_query_start:
-                memset(key_buf, 0, len);
-                memset(val_buf, 0, len);
+                key_idx    = 0;
+                val_idx    = 0;
 
-                key_idx = 0;
-                val_idx = 0;
+                key_buf[0] = '\0';
+                val_buf[0] = '\0';
 
-                switch (ch) {
-                    case '?':
-                        state = s_query_key;
-                        break;
-                    case '/':
-                        state = s_query_question_mark;
-                        break;
-                    default:
-                        state = s_query_key;
-                        goto query_key;
-                }
-
-                break;
-            case s_query_question_mark:
-                switch (ch) {
-                    case '?':
-                        state = s_query_key;
-                        break;
-                    case '/':
-                        state = s_query_question_mark;
-                        break;
-                    default:
-                        goto error;
-                }
-                break;
-query_key:
+                state      = s_query_key;
+            /* Fall through. */
             case s_query_key:
                 switch (ch) {
                     case '=':
@@ -2437,14 +2793,45 @@
                         break;
                     case '%':
                         key_buf[key_idx++] = ch;
-                        key_buf[key_idx] = '\0';
-                        state = s_query_key_hex_1;
+                        key_buf[key_idx]   = '\0';
+
+                        if (!(flags & EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX)) {
+                            state = s_query_key_hex_1;
+                        }
+
+                        break;
+                    case ';':
+                        if (!(flags & EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP)) {
+                            key_buf[key_idx++] = ch;
+                            key_buf[key_idx]   = '\0';
+                            break;
+                        }
+
+                    /* otherwise we fallthrough */
+                    case '&':
+                        /* in this state, we have a NULL value */
+                        if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS)) {
+                            goto error;
+                        }
+
+                        /* insert the key with value of NULL and set the
+                         * state back to parsing s_query_key.
+                         */
+                        evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, NULL, 1, 1));
+
+                        key_idx            = 0;
+                        val_idx            = 0;
+
+                        key_buf[0]         = '\0';
+                        val_buf[0]         = '\0';
+
+                        state              = s_query_key;
                         break;
                     default:
                         key_buf[key_idx++] = ch;
                         key_buf[key_idx]   = '\0';
                         break;
-                }
+                } /* switch */
                 break;
             case s_query_key_hex_1:
                 if (!evhtp_is_hex_query_char(ch)) {
@@ -2457,6 +2844,7 @@
                     key_buf[key_idx - 1] = '%';
                     key_buf[key_idx++]   = ch;
                     key_buf[key_idx]     = '\0';
+
                     state = s_query_key;
                     break;
                 }
@@ -2479,15 +2867,19 @@
             case s_query_val:
                 switch (ch) {
                     case ';':
+                        if (!(flags & EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP)) {
+                            val_buf[val_idx++] = ch;
+                            val_buf[val_idx]   = '\0';
+                            break;
+                        }
                     case '&':
                         evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, val_buf, 1, 1));
 
-                        memset(key_buf, 0, len);
-                        memset(val_buf, 0, len);
-
                         key_idx            = 0;
                         val_idx            = 0;
 
+                        key_buf[0]         = '\0';
+                        val_buf[0]         = '\0';
                         state              = s_query_key;
 
                         break;
@@ -2495,7 +2887,10 @@
                         val_buf[val_idx++] = ch;
                         val_buf[val_idx]   = '\0';
 
-                        state              = s_query_val_hex_1;
+                        if (!(flags & EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX)) {
+                            state = s_query_val_hex_1;
+                        }
+
                         break;
                     default:
                         val_buf[val_idx++] = ch;
@@ -2512,6 +2907,9 @@
                         goto error;
                     }
 
+                    if (val_idx == 0) {
+                        goto error;
+                    }
 
                     val_buf[val_idx - 1] = '%';
                     val_buf[val_idx++]   = ch;
@@ -2542,21 +2940,50 @@
         }       /* switch */
     }
 
-    if (key_idx && val_idx) {
-        evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, val_buf, 1, 1));
+    if (key_idx) {
+        do {
+            if (val_idx) {
+                evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, val_buf, 1, 1));
+                break;
+            }
+
+            if (state >= s_query_val) {
+                if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS)) {
+                    goto error;
+                }
+
+                evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, "", 1, 1));
+                break;
+            }
+
+            if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS)) {
+                goto error;
+            }
+
+            evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, NULL, 1, 0));
+        } while (0);
     }
 
-    free(key_buf);
-    free(val_buf);
+#ifndef EVHTP_HAS_C99
+    evhtp_safe_free(key_buf, free);
+    evhtp_safe_free(val_buf, free);
+#endif
 
     return query_args;
 error:
-    free(key_buf);
-    free(val_buf);
+#ifndef EVHTP_HAS_C99
+    evhtp_safe_free(key_buf, free);
+    evhtp_safe_free(val_buf, free);
+#endif
 
     return NULL;
 }     /* evhtp_parse_query */
 
+evhtp_query_t *
+evhtp_parse_query(const char * query, size_t len) {
+    return evhtp_parse_query_wflags(query, len, EVHTP_PARSE_QUERY_FLAG_STRICT);
+}
+
 void
 evhtp_send_reply_start(evhtp_request_t * request, evhtp_res code) {
     evhtp_connection_t * c;
@@ -2566,6 +2993,7 @@
 
     if (!(reply_buf = _evhtp_create_reply(request, code))) {
         evhtp_connection_free(c);
+
         return;
     }
 
@@ -2585,9 +3013,6 @@
 void
 evhtp_send_reply_end(evhtp_request_t * request) {
     request->finished = 1;
-
-    _evhtp_connection_writecb(evhtp_request_get_bev(request),
-                              evhtp_request_get_connection(request));
 }
 
 void
@@ -2600,6 +3025,7 @@
 
     if (!(reply_buf = _evhtp_create_reply(request, code))) {
         evhtp_connection_free(request->conn);
+
         return;
     }
 
@@ -2693,20 +3119,24 @@
     if (evbuffer_get_length(buf) == 0) {
         return;
     }
-    if (request->chunked) {
+
+    if (request->chunked == 1) {
         evbuffer_add_printf(output, "%x\r\n",
                             (unsigned)evbuffer_get_length(buf));
     }
+
     evhtp_send_reply_body(request, buf);
+
     if (request->chunked) {
         evbuffer_add(output, "\r\n", 2);
     }
+
     bufferevent_flush(request->conn->bev, EV_WRITE, BEV_FLUSH);
 }
 
 void
 evhtp_send_reply_chunk_end(evhtp_request_t * request) {
-    if (request->chunked) {
+    if (request->chunked == 1) {
         evbuffer_add(bufferevent_get_output(evhtp_request_get_bev(request)),
                      "0\r\n\r\n", 5);
     }
@@ -2764,14 +3194,13 @@
 
 int
 evhtp_bind_socket(evhtp_t * htp, const char * baddr, uint16_t port, int backlog) {
-    struct sockaddr_in  sin;
-    struct sockaddr_in6 sin6;
-
 #ifndef NO_SYS_UN
-    struct sockaddr_un sun;
+    struct sockaddr_un  sun;
 #endif
-    struct sockaddr  * sa;
-    size_t             sin_len;
+    struct sockaddr_in6 sin6;
+    struct sockaddr_in  sin;
+    struct sockaddr   * sa;
+    size_t              sin_len;
 
     memset(&sin, 0, sizeof(sin));
 
@@ -2802,7 +3231,7 @@
 
         sa = (struct sockaddr *)&sun;
 #else
-        fprintf(stderr, "System does not support AF_UNIX sockets\n");
+
         return -1;
 #endif
     } else {
@@ -2834,10 +3263,10 @@
     TAILQ_FOREACH_SAFE(callback, callbacks, next, tmp) {
         TAILQ_REMOVE(callbacks, callback, next);
 
-        evhtp_callback_free(callback);
+        evhtp_safe_free(callback, evhtp_callback_free);
     }
 
-    free(callbacks);
+    evhtp_safe_free(callbacks, free);
 }
 
 evhtp_callback_t *
@@ -2862,8 +3291,9 @@
             hcb->val.regex = malloc(sizeof(regex_t));
 
             if (regcomp(hcb->val.regex, (char *)path, REG_EXTENDED) != 0) {
-                free(hcb->val.regex);
-                free(hcb);
+                evhtp_safe_free(hcb->val.regex, free);
+                evhtp_safe_free(hcb, free);
+
                 return NULL;
             }
             break;
@@ -2872,7 +3302,8 @@
             hcb->val.glob = strdup(path);
             break;
         default:
-            free(hcb);
+            evhtp_safe_free(hcb, free);
+
             return NULL;
     } /* switch */
 
@@ -2890,7 +3321,7 @@
             free(callback->val.path);
             break;
         case evhtp_callback_type_glob:
-            free(callback->val.glob);
+            evhtp_safe_free(callback->val.glob, free);
             break;
 #ifndef EVHTP_DISABLE_REGEX
         case evhtp_callback_type_regex:
@@ -2901,10 +3332,10 @@
     }
 
     if (callback->hooks) {
-        free(callback->hooks);
+        evhtp_safe_free(callback->hooks, free);
     }
 
-    free(callback);
+    evhtp_safe_free(callback, free);
 
     return;
 }
@@ -2926,58 +3357,67 @@
 
     switch (type) {
         case evhtp_hook_on_headers_start:
-            (*hooks)->on_headers_start       = (evhtp_hook_headers_start_cb)cb;
-            (*hooks)->on_headers_start_arg   = arg;
+            (*hooks)->on_headers_start        = (evhtp_hook_headers_start_cb)cb;
+            (*hooks)->on_headers_start_arg    = arg;
             break;
         case evhtp_hook_on_header:
             (*hooks)->on_header = (evhtp_hook_header_cb)cb;
-            (*hooks)->on_header_arg          = arg;
+            (*hooks)->on_header_arg           = arg;
             break;
         case evhtp_hook_on_headers:
-            (*hooks)->on_headers             = (evhtp_hook_headers_cb)cb;
-            (*hooks)->on_headers_arg         = arg;
+            (*hooks)->on_headers              = (evhtp_hook_headers_cb)cb;
+            (*hooks)->on_headers_arg          = arg;
             break;
         case evhtp_hook_on_path:
             (*hooks)->on_path = (evhtp_hook_path_cb)cb;
-            (*hooks)->on_path_arg            = arg;
+            (*hooks)->on_path_arg             = arg;
             break;
         case evhtp_hook_on_read:
             (*hooks)->on_read = (evhtp_hook_read_cb)cb;
-            (*hooks)->on_read_arg            = arg;
+            (*hooks)->on_read_arg             = arg;
             break;
         case evhtp_hook_on_request_fini:
-            (*hooks)->on_request_fini        = (evhtp_hook_request_fini_cb)cb;
-            (*hooks)->on_request_fini_arg    = arg;
+            (*hooks)->on_request_fini         = (evhtp_hook_request_fini_cb)cb;
+            (*hooks)->on_request_fini_arg     = arg;
             break;
         case evhtp_hook_on_connection_fini:
-            (*hooks)->on_connection_fini     = (evhtp_hook_connection_fini_cb)cb;
-            (*hooks)->on_connection_fini_arg = arg;
+            (*hooks)->on_connection_fini      = (evhtp_hook_connection_fini_cb)cb;
+            (*hooks)->on_connection_fini_arg  = arg;
+            break;
+        case evhtp_hook_on_conn_error:
+            (*hooks)->on_connection_error     = (evhtp_hook_conn_err_cb)cb;
+            (*hooks)->on_connection_error_arg = arg;
             break;
         case evhtp_hook_on_error:
             (*hooks)->on_error = (evhtp_hook_err_cb)cb;
-            (*hooks)->on_error_arg           = arg;
+            (*hooks)->on_error_arg            = arg;
             break;
         case evhtp_hook_on_new_chunk:
-            (*hooks)->on_new_chunk           = (evhtp_hook_chunk_new_cb)cb;
-            (*hooks)->on_new_chunk_arg       = arg;
+            (*hooks)->on_new_chunk            = (evhtp_hook_chunk_new_cb)cb;
+            (*hooks)->on_new_chunk_arg        = arg;
             break;
         case evhtp_hook_on_chunk_complete:
-            (*hooks)->on_chunk_fini          = (evhtp_hook_chunk_fini_cb)cb;
-            (*hooks)->on_chunk_fini_arg      = arg;
+            (*hooks)->on_chunk_fini           = (evhtp_hook_chunk_fini_cb)cb;
+            (*hooks)->on_chunk_fini_arg       = arg;
             break;
         case evhtp_hook_on_chunks_complete:
-            (*hooks)->on_chunks_fini         = (evhtp_hook_chunks_fini_cb)cb;
-            (*hooks)->on_chunks_fini_arg     = arg;
+            (*hooks)->on_chunks_fini          = (evhtp_hook_chunks_fini_cb)cb;
+            (*hooks)->on_chunks_fini_arg      = arg;
             break;
         case evhtp_hook_on_hostname:
-            (*hooks)->on_hostname            = (evhtp_hook_hostname_cb)cb;
-            (*hooks)->on_hostname_arg        = arg;
+            (*hooks)->on_hostname             = (evhtp_hook_hostname_cb)cb;
+            (*hooks)->on_hostname_arg         = arg;
             break;
         case evhtp_hook_on_write:
             (*hooks)->on_write = (evhtp_hook_write_cb)cb;
-            (*hooks)->on_write_arg           = arg;
+            (*hooks)->on_write_arg            = arg;
+            break;
+        case evhtp_hook_on_event:
+            (*hooks)->on_event = (evhtp_hook_event_cb)cb;
+            (*hooks)->on_event_arg            = arg;
             break;
         default:
+
             return -1;
     }     /* switch */
 
@@ -3021,6 +3461,10 @@
         res -= 1;
     }
 
+    if (evhtp_unset_hook(hooks, evhtp_hook_on_conn_error)) {
+        res -= 1;
+    }
+
     if (evhtp_unset_hook(hooks, evhtp_hook_on_error)) {
         res -= 1;
     }
@@ -3045,6 +3489,10 @@
         return -1;
     }
 
+    if (evhtp_unset_hook(hooks, evhtp_hook_on_event)) {
+        return -1;
+    }
+
     return res;
 } /* evhtp_unset_all_hooks */
 
@@ -3057,6 +3505,7 @@
     if (htp->callbacks == NULL) {
         if (!(htp->callbacks = calloc(sizeof(evhtp_callbacks_t), 1))) {
             _evhtp_unlock(htp);
+
             return NULL;
         }
 
@@ -3065,16 +3514,19 @@
 
     if (!(hcb = evhtp_callback_new(path, evhtp_callback_type_hash, cb, arg))) {
         _evhtp_unlock(htp);
+
         return NULL;
     }
 
     if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) {
-        evhtp_callback_free(hcb);
+        evhtp_safe_free(hcb, evhtp_callback_free);
         _evhtp_unlock(htp);
+
         return NULL;
     }
 
     _evhtp_unlock(htp);
+
     return hcb;
 }
 
@@ -3102,6 +3554,7 @@
     }
 
     evthr_pool_start(htp->thr_pool);
+
     return 0;
 }
 
@@ -3133,6 +3586,7 @@
     if (htp->callbacks == NULL) {
         if (!(htp->callbacks = calloc(sizeof(evhtp_callbacks_t), 1))) {
             _evhtp_unlock(htp);
+
             return NULL;
         }
 
@@ -3141,16 +3595,19 @@
 
     if (!(hcb = evhtp_callback_new(pattern, evhtp_callback_type_regex, cb, arg))) {
         _evhtp_unlock(htp);
+
         return NULL;
     }
 
     if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) {
-        evhtp_callback_free(hcb);
+        evhtp_safe_free(hcb, evhtp_callback_free);
         _evhtp_unlock(htp);
+
         return NULL;
     }
 
     _evhtp_unlock(htp);
+
     return hcb;
 }
 
@@ -3165,6 +3622,7 @@
     if (htp->callbacks == NULL) {
         if (!(htp->callbacks = calloc(sizeof(evhtp_callbacks_t), 1))) {
             _evhtp_unlock(htp);
+
             return NULL;
         }
 
@@ -3173,16 +3631,19 @@
 
     if (!(hcb = evhtp_callback_new(pattern, evhtp_callback_type_glob, cb, arg))) {
         _evhtp_unlock(htp);
+
         return NULL;
     }
 
     if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) {
-        evhtp_callback_free(hcb);
+        evhtp_safe_free(hcb, evhtp_callback_free);
         _evhtp_unlock(htp);
+
         return NULL;
     }
 
     _evhtp_unlock(htp);
+
     return hcb;
 }
 
@@ -3283,8 +3744,8 @@
 #endif /* OPENSSL_NO_ECDH */
 #ifndef OPENSSL_NO_DH
     if (cfg->dhparams != NULL) {
-        FILE *fh;
-        DH   *dh;
+        FILE * fh;
+        DH   * dh;
 
         fh = fopen(cfg->dhparams, "r");
         if (fh != NULL) {
@@ -3467,15 +3928,15 @@
         return;
     }
 
-    _evhtp_request_free(connection->request);
     _evhtp_connection_fini_hook(connection);
+    evhtp_safe_free(connection->request, _evhtp_request_free);
 
-    free(connection->parser);
-    free(connection->hooks);
-    free(connection->saddr);
+    evhtp_safe_free(connection->parser, free);
+    evhtp_safe_free(connection->hooks, free);
+    evhtp_safe_free(connection->saddr, free);
 
     if (connection->resume_ev) {
-        event_free(connection->resume_ev);
+        evhtp_safe_free(connection->resume_ev, event_free);
     }
 
     if (connection->bev) {
@@ -3492,17 +3953,11 @@
 #endif
     }
 
-#ifndef EVHTP_DISABLE_EVTHR
-    if (connection->thread && connection->type == evhtp_type_server) {
-        evthr_dec_backlog(connection->thread);
-    }
-#endif
-
     if (connection->ratelimit_cfg != NULL) {
         ev_token_bucket_cfg_free(connection->ratelimit_cfg);
     }
 
-    free(connection);
+    evhtp_safe_free(connection, free);
 }     /* evhtp_connection_free */
 
 void
@@ -3547,6 +4002,11 @@
     htp->disable_100_cont = 1;
 }
 
+void
+evhtp_set_parser_flags(evhtp_t * htp, int flags) {
+    htp->parser_flags = flags;
+}
+
 int
 evhtp_add_alias(evhtp_t * evhtp, const char * name) {
     evhtp_alias_t * alias;
@@ -3656,6 +4116,12 @@
     }
 #endif
 
+#ifndef EVHTP_DISABLE_SSL
+    if (evhtp->ssl_ctx) {
+        SSL_CTX_free(evhtp->ssl_ctx);
+    }
+#endif
+
     if (evhtp->server_name) {
         free(evhtp->server_name);
     }
@@ -3679,7 +4145,7 @@
 #endif
 
     free(evhtp);
-}
+} /* evhtp_free */
 
 int
 evhtp_connection_set_rate_limit(evhtp_connection_t * conn,
@@ -3710,6 +4176,78 @@
 
 evhtp_connection_t *
 evhtp_connection_new(evbase_t * evbase, const char * addr, uint16_t port) {
+    return evhtp_connection_new_dns(evbase, NULL, addr, port);
+}
+
+evhtp_connection_t *
+evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base,
+                         const char * addr, uint16_t port) {
+    evhtp_connection_t * conn;
+    int                  err;
+
+    if (evbase == NULL) {
+        return NULL;
+    }
+
+    if (!(conn = _evhtp_connection_new(NULL, -1, evhtp_type_client))) {
+        return NULL;
+    }
+
+    conn->evbase = evbase;
+    conn->bev    = bufferevent_socket_new(evbase, -1, BEV_OPT_CLOSE_ON_FREE);
+
+    if (conn->bev == NULL) {
+        evhtp_connection_free(conn);
+
+        return NULL;
+    }
+
+    bufferevent_enable(conn->bev, EV_READ);
+    bufferevent_setcb(conn->bev, NULL, NULL,
+                      _evhtp_connection_eventcb, conn);
+
+    if (dns_base != NULL) {
+        err = bufferevent_socket_connect_hostname(conn->bev, dns_base,
+                                                  AF_UNSPEC, addr, port);
+    } else {
+        struct sockaddr_in  sin4;
+        struct sockaddr_in6 sin6;
+        struct sockaddr   * sin;
+        int                 salen;
+
+        if (inet_pton(AF_INET, addr, &sin4.sin_addr)) {
+            sin4.sin_family = AF_INET;
+            sin4.sin_port   = htons(port);
+            sin = (struct sockaddr *)&sin4;
+            salen           = sizeof(sin4);
+        } else if (inet_pton(AF_INET6, addr, &sin6.sin6_addr)) {
+            sin6.sin6_family = AF_INET6;
+            sin6.sin6_port   = htons(port);
+            sin = (struct sockaddr *)&sin6;
+            salen = sizeof(sin6);
+        } else {
+            /* Not a valid IP. */
+            evhtp_connection_free(conn);
+
+            return NULL;
+        }
+
+        err = bufferevent_socket_connect(conn->bev, sin, salen);
+    }
+
+    /* not needed since any of the bufferevent errors will go straight to
+     * the eventcb
+     */
+    if (err) {
+        return NULL;
+    }
+
+    return conn;
+} /* evhtp_connection_new_dns */
+
+#ifndef EVHTP_DISABLE_SSL
+evhtp_connection_t *
+evhtp_connection_ssl_new(evbase_t * evbase, const char * addr, uint16_t port, evhtp_ssl_ctx_t * ctx) {
     evhtp_connection_t * conn;
     struct sockaddr_in   sin;
 
@@ -3725,8 +4263,9 @@
     sin.sin_addr.s_addr = inet_addr(addr);
     sin.sin_port        = htons(port);
 
+    conn->ssl           = SSL_new(ctx);
     conn->evbase        = evbase;
-    conn->bev           = bufferevent_socket_new(evbase, -1, BEV_OPT_CLOSE_ON_FREE);
+    conn->bev           = bufferevent_openssl_socket_new(evbase, -1, conn->ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE);
 
     bufferevent_enable(conn->bev, EV_READ);
 
@@ -3736,9 +4275,13 @@
     bufferevent_socket_connect(conn->bev,
                                (struct sockaddr *)&sin, sizeof(sin));
 
+
     return conn;
 }
 
+#endif
+
+
 evhtp_request_t *
 evhtp_request_new(evhtp_callback_cb cb, void * arg) {
     evhtp_request_t * r;
diff --git a/evhtp.h b/evhtp.h
index 9c11c76..ddc4996 100644
--- a/evhtp.h
+++ b/evhtp.h
@@ -2,6 +2,15 @@
 #define __EVHTP__H__
 
 #include <evhtp-config.h>
+
+#ifndef EVHTP_EXPORT
+# if (defined __GNUC__ && __GNUC__ >= 4) || defined __INTEL_COMPILER || defined __clang__
+#  define EVHTP_EXPORT __attribute__ ((visibility("default")))
+# else
+#  define EVHTP_EXPORT
+# endif
+#endif
+
 #ifndef EVHTP_DISABLE_EVTHR
 #include <evthr.h>
 #endif
@@ -108,15 +117,17 @@
     evhtp_hook_on_headers_start,
     evhtp_hook_on_error,        /**< type which defines to hook whenever an error occurs */
     evhtp_hook_on_hostname,
-    evhtp_hook_on_write
+    evhtp_hook_on_write,
+    evhtp_hook_on_event,
+    evhtp_hook_on_conn_error,   /**< type which defines to hook whenever a connection error occurs */
 };
 
 enum evhtp_callback_type {
     evhtp_callback_type_hash,
+    evhtp_callback_type_glob,
 #ifndef EVHTP_DISABLE_REGEX
     evhtp_callback_type_regex,
 #endif
-    evhtp_callback_type_glob
 };
 
 enum evhtp_proto {
@@ -136,43 +147,47 @@
 typedef enum evhtp_ssl_scache_type evhtp_ssl_scache_type;
 typedef enum evhtp_type            evhtp_type;
 
-typedef void (*evhtp_thread_init_cb)(evhtp_t * htp, evthr_t * thr, void * arg);
-typedef void (*evhtp_callback_cb)(evhtp_request_t * req, void * arg);
-typedef void (*evhtp_hook_err_cb)(evhtp_request_t * req, evhtp_error_flags errtype, void * arg);
+typedef void (* evhtp_thread_init_cb)(evhtp_t * htp, evthr_t * thr, void * arg);
+typedef void (* evhtp_callback_cb)(evhtp_request_t * req, void * arg);
+typedef void (* evhtp_hook_err_cb)(evhtp_request_t * req, evhtp_error_flags errtype, void * arg);
+typedef void (* evhtp_hook_event_cb)(evhtp_connection_t * conn, short events, void * arg);
 
 /* Generic hook for passing ISO tests */
-typedef evhtp_res (*evhtp_hook)();
+typedef evhtp_res (* evhtp_hook)();
 
-typedef evhtp_res (*evhtp_pre_accept_cb)(evhtp_connection_t * conn, void * arg);
-typedef evhtp_res (*evhtp_post_accept_cb)(evhtp_connection_t * conn, void * arg);
-typedef evhtp_res (*evhtp_hook_header_cb)(evhtp_request_t * req, evhtp_header_t * hdr, void * arg);
-typedef evhtp_res (*evhtp_hook_headers_cb)(evhtp_request_t * req, evhtp_headers_t * hdr, void * arg);
-typedef evhtp_res (*evhtp_hook_path_cb)(evhtp_request_t * req, evhtp_path_t * path, void * arg);
-typedef evhtp_res (*evhtp_hook_read_cb)(evhtp_request_t * req, evbuf_t * buf, void * arg);
-typedef evhtp_res (*evhtp_hook_request_fini_cb)(evhtp_request_t * req, void * arg);
-typedef evhtp_res (*evhtp_hook_connection_fini_cb)(evhtp_connection_t * connection, void * arg);
-typedef evhtp_res (*evhtp_hook_chunk_new_cb)(evhtp_request_t * r, uint64_t len, void * arg);
-typedef evhtp_res (*evhtp_hook_chunk_fini_cb)(evhtp_request_t * r, void * arg);
-typedef evhtp_res (*evhtp_hook_chunks_fini_cb)(evhtp_request_t * r, void * arg);
-typedef evhtp_res (*evhtp_hook_headers_start_cb)(evhtp_request_t * r, void * arg);
-typedef evhtp_res (*evhtp_hook_hostname_cb)(evhtp_request_t * r, const char * hostname, void * arg);
-typedef evhtp_res (*evhtp_hook_write_cb)(evhtp_connection_t * conn, void * arg);
+typedef evhtp_res (* evhtp_hook_conn_err_cb)(evhtp_connection_t * connection, evhtp_error_flags errtype, void * arg);
+typedef evhtp_res (* evhtp_pre_accept_cb)(evhtp_connection_t * conn, void * arg);
+typedef evhtp_res (* evhtp_post_accept_cb)(evhtp_connection_t * conn, void * arg);
+typedef evhtp_res (* evhtp_hook_header_cb)(evhtp_request_t * req, evhtp_header_t * hdr, void * arg);
+typedef evhtp_res (* evhtp_hook_headers_cb)(evhtp_request_t * req, evhtp_headers_t * hdr, void * arg);
+typedef evhtp_res (* evhtp_hook_path_cb)(evhtp_request_t * req, evhtp_path_t * path, void * arg);
+typedef evhtp_res (* evhtp_hook_read_cb)(evhtp_request_t * req, evbuf_t * buf, void * arg);
+typedef evhtp_res (* evhtp_hook_request_fini_cb)(evhtp_request_t * req, void * arg);
+typedef evhtp_res (* evhtp_hook_connection_fini_cb)(evhtp_connection_t * connection, void * arg);
+typedef evhtp_res (* evhtp_hook_chunk_new_cb)(evhtp_request_t * r, uint64_t len, void * arg);
+typedef evhtp_res (* evhtp_hook_chunk_fini_cb)(evhtp_request_t * r, void * arg);
+typedef evhtp_res (* evhtp_hook_chunks_fini_cb)(evhtp_request_t * r, void * arg);
+typedef evhtp_res (* evhtp_hook_headers_start_cb)(evhtp_request_t * r, void * arg);
+typedef evhtp_res (* evhtp_hook_hostname_cb)(evhtp_request_t * r, const char * hostname, void * arg);
+typedef evhtp_res (* evhtp_hook_write_cb)(evhtp_connection_t * conn, void * arg);
 
-typedef int (*evhtp_kvs_iterator)(evhtp_kv_t * kv, void * arg);
-typedef int (*evhtp_headers_iterator)(evhtp_header_t * header, void * arg);
+typedef int (* evhtp_kvs_iterator)(evhtp_kv_t * kv, void * arg);
+typedef int (* evhtp_headers_iterator)(evhtp_header_t * header, void * arg);
 
-typedef int (*evhtp_ssl_verify_cb)(int pre_verify, evhtp_x509_store_ctx_t * ctx);
-typedef int (*evhtp_ssl_chk_issued_cb)(evhtp_x509_store_ctx_t * ctx, evhtp_x509_t * x, evhtp_x509_t * issuer);
+#ifndef EVHTP_DISABLE_SSL
+typedef int (* evhtp_ssl_verify_cb)(int pre_verify, evhtp_x509_store_ctx_t * ctx);
+typedef int (* evhtp_ssl_chk_issued_cb)(evhtp_x509_store_ctx_t * ctx, evhtp_x509_t * x, evhtp_x509_t * issuer);
 
-typedef int (*evhtp_ssl_scache_add)(evhtp_connection_t * connection, unsigned char * sid, int sid_len, evhtp_ssl_sess_t * sess);
-typedef void (*evhtp_ssl_scache_del)(evhtp_t * htp, unsigned char * sid, int sid_len);
-typedef evhtp_ssl_sess_t * (*evhtp_ssl_scache_get)(evhtp_connection_t * connection, unsigned char * sid, int sid_len);
-typedef void * (*evhtp_ssl_scache_init)(evhtp_t *);
+typedef int (* evhtp_ssl_scache_add)(evhtp_connection_t * connection, unsigned char * sid, int sid_len, evhtp_ssl_sess_t * sess);
+typedef void (* evhtp_ssl_scache_del)(evhtp_t * htp, unsigned char * sid, int sid_len);
+typedef evhtp_ssl_sess_t * (* evhtp_ssl_scache_get)(evhtp_connection_t * connection, unsigned char * sid, int sid_len);
+typedef void * (* evhtp_ssl_scache_init)(evhtp_t *);
+#endif
 
-#define EVHTP_VERSION           "1.2.9"
+#define EVHTP_VERSION           "1.2.10"
 #define EVHTP_VERSION_MAJOR     1
 #define EVHTP_VERSION_MINOR     2
-#define EVHTP_VERSION_PATCH     9
+#define EVHTP_VERSION_PATCH     10
 
 #define evhtp_headers_iterator  evhtp_kvs_iterator
 
@@ -267,6 +282,7 @@
     uint64_t   max_body_size;
     uint64_t   max_keepalive_requests;
     int        disable_100_cont; /**< if set, evhtp will not respond to Expect: 100-continue */
+    int        parser_flags;     /**< default query flags to alter 'strictness' (see EVHTP_PARSE_QUERY_FLAG_*) */
 
 #ifndef EVHTP_DISABLE_SSL
     evhtp_ssl_ctx_t * ssl_ctx;   /**< if ssl enabled, this is the servers CTX */
@@ -353,10 +369,10 @@
 struct evhtp_uri_s {
     evhtp_authority_t * authority;
     evhtp_path_t      * path;
-    unsigned char     * fragment;     /**< data after '#' in uri */
-    unsigned char     * query_raw;    /**< the unparsed query arguments */
-    evhtp_query_t     * query;        /**< list of k/v for query arguments */
-    htp_scheme          scheme;       /**< set if a scheme is found */
+    unsigned char     * fragment;       /**< data after '#' in uri */
+    unsigned char     * query_raw;      /**< the unparsed query arguments */
+    evhtp_query_t     * query;          /**< list of k/v for query arguments */
+    htp_scheme          scheme;         /**< set if a scheme is found */
 };
 
 
@@ -364,10 +380,10 @@
  * @brief structure which represents authority information in a URI
  */
 struct evhtp_authority_s {
-    char   * username;                /**< the username in URI (scheme://USER:.. */
-    char   * password;                /**< the password in URI (scheme://...:PASS.. */
-    char   * hostname;                /**< hostname if present in URI */
-    uint16_t port;                    /**< port if present in URI */
+    char   * username;                  /**< the username in URI (scheme://USER:.. */
+    char   * password;                  /**< the password in URI (scheme://...:PASS.. */
+    char   * hostname;                  /**< hostname if present in URI */
+    uint16_t port;                      /**< port if present in URI */
 };
 
 
@@ -375,17 +391,17 @@
  * @brief structure which represents a URI path and or file
  */
 struct evhtp_path_s {
-    char       * full;                /**< the full path+file (/a/b/c.html) */
-    char       * path;                /**< the path (/a/b/) */
-    char       * file;                /**< the filename if present (c.html) */
+    char       * full;                  /**< the full path+file (/a/b/c.html) */
+    char       * path;                  /**< the path (/a/b/) */
+    char       * file;                  /**< the filename if present (c.html) */
     char       * match_start;
     char       * match_end;
-    unsigned int matched_soff;        /**< offset of where the uri starts
-                                       *   mainly used for regex matching
-                                       */
-    unsigned int matched_eoff;        /**< offset of where the uri ends
-                                       *   mainly used for regex matching
-                                       */
+    unsigned int matched_soff;          /**< offset of where the uri starts
+                                         *   mainly used for regex matching
+                                         */
+    unsigned int matched_eoff;          /**< offset of where the uri ends
+                                         *   mainly used for regex matching
+                                         */
 };
 
 
@@ -393,24 +409,25 @@
  * @brief a structure containing all information for a http request.
  */
 struct evhtp_request_s {
-    evhtp_t            * htp;         /**< the parent evhtp_t structure */
-    evhtp_connection_t * conn;        /**< the associated connection */
-    evhtp_hooks_t      * hooks;       /**< request specific hooks */
-    evhtp_uri_t        * uri;         /**< request URI information */
-    evbuf_t            * buffer_in;   /**< buffer containing data from client */
-    evbuf_t            * buffer_out;  /**< buffer containing data to client */
-    evhtp_headers_t    * headers_in;  /**< headers from client */
-    evhtp_headers_t    * headers_out; /**< headers to client */
-    evhtp_proto          proto;       /**< HTTP protocol used */
-    htp_method           method;      /**< HTTP method used */
-    evhtp_res            status;      /**< The HTTP response code or other error conditions */
-    int                  keepalive;   /**< set to 1 if the connection is keep-alive */
-    int                  finished;    /**< set to 1 if the request is fully processed */
-    int                  chunked;     /**< set to 1 if the request is chunked */
+    evhtp_t            * htp;           /**< the parent evhtp_t structure */
+    evhtp_connection_t * conn;          /**< the associated connection */
+    evhtp_hooks_t      * hooks;         /**< request specific hooks */
+    evhtp_uri_t        * uri;           /**< request URI information */
+    evbuf_t            * buffer_in;     /**< buffer containing data from client */
+    evbuf_t            * buffer_out;    /**< buffer containing data to client */
+    evhtp_headers_t    * headers_in;    /**< headers from client */
+    evhtp_headers_t    * headers_out;   /**< headers to client */
+    evhtp_proto          proto;         /**< HTTP protocol used */
+    htp_method           method;        /**< HTTP method used */
+    evhtp_res            status;        /**< The HTTP response code or other error conditions */
+    uint8_t              keepalive : 1, /**< set to 1 if the connection is keep-alive */
+                         finished  : 1, /**< set to 1 if the request is fully processed */
+                         chunked   : 1, /**< set to 1 if the request is chunked */
+                         error     : 1, /**< set if any sort of error has occurred. */
+                         pad       : 4; /**< to be used in evhtp2 for new stuff */
 
-    evhtp_callback_cb cb;             /**< the function to call when fully processed */
-    void            * cbarg;          /**< argument which is passed to the cb function */
-    int               error;
+    evhtp_callback_cb cb;               /**< the function to call when fully processed */
+    void            * cbarg;            /**< argument which is passed to the cb function */
 
     TAILQ_ENTRY(evhtp_request_s) next;
 };
@@ -418,31 +435,38 @@
 #define evhtp_request_content_len(r) htparser_get_content_length(r->conn->parser)
 
 struct evhtp_connection_s {
-    evhtp_t                    * htp;
-    evbase_t                   * evbase;
-    evbev_t                    * bev;
-    evthr_t                    * thread;
-    evhtp_ssl_t                * ssl;
-    evhtp_hooks_t              * hooks;
-    htparser                   * parser;
-    event_t                    * resume_ev;
-    struct sockaddr            * saddr;
-    struct timeval               recv_timeo;    /**< conn read timeouts (overrides global) */
-    struct timeval               send_timeo;    /**< conn write timeouts (overrides global) */
-    evutil_socket_t              sock;
-    uint8_t                      error;
-    uint8_t                      owner;         /**< set to 1 if this structure owns the bufferevent */
-    uint8_t                      vhost_via_sni; /**< set to 1 if the vhost was found via SSL SNI */
-    evhtp_request_t            * request;       /**< the request currently being processed */
-    uint64_t                     max_body_size;
-    uint64_t                     body_bytes_read;
-    uint64_t                     num_requests;
-    evhtp_type                   type;          /**< server or client */
-    char                         paused;
-    char                         free_connection;
+    evhtp_t  * htp;
+    evbase_t * evbase;
+    evbev_t  * bev;
+    evthr_t  * thread;
+#ifndef EVHTP_DISABLE_SSL
+    evhtp_ssl_t * ssl;
+#endif
+    evhtp_hooks_t   * hooks;
+    htparser        * parser;
+    event_t         * resume_ev;
+    struct sockaddr * saddr;
+    struct timeval    recv_timeo;               /**< conn read timeouts (overrides global) */
+    struct timeval    send_timeo;               /**< conn write timeouts (overrides global) */
+    evutil_socket_t   sock;
+    evhtp_request_t * request;                  /**< the request currently being processed */
+    uint64_t          max_body_size;
+    uint64_t          body_bytes_read;
+    uint64_t          num_requests;
+    evhtp_type        type;                     /**< server or client */
+    uint8_t           error           : 1,
+                      owner           : 1,      /**< set to 1 if this structure owns the bufferevent */
+                      vhost_via_sni   : 1,      /**< set to 1 if the vhost was found via SSL SNI */
+                      paused          : 1,      /**< this connection has been marked as paused */
+                      connected       : 1,      /**< client specific - set after successful connection */
+                      waiting         : 1,      /**< used to make sure resuming  happens AFTER sending a reply */
+                      free_connection : 1,
+                      keepalive       : 1;      /**< set to 1 after the first request has been processed and the connection is kept open */
     struct ev_token_bucket_cfg * ratelimit_cfg; /**< connection-specific ratelimiting configuration. */
 
+#ifdef EVHTP_FUTURE_USE
     TAILQ_HEAD(, evhtp_request_s) pending;      /**< client pending data */
+#endif
 };
 
 struct evhtp_hooks_s {
@@ -453,12 +477,14 @@
     evhtp_hook_read_cb            on_read;
     evhtp_hook_request_fini_cb    on_request_fini;
     evhtp_hook_connection_fini_cb on_connection_fini;
+    evhtp_hook_conn_err_cb        on_connection_error;
     evhtp_hook_err_cb             on_error;
     evhtp_hook_chunk_new_cb       on_new_chunk;
     evhtp_hook_chunk_fini_cb      on_chunk_fini;
     evhtp_hook_chunks_fini_cb     on_chunks_fini;
     evhtp_hook_hostname_cb        on_hostname;
     evhtp_hook_write_cb           on_write;
+    evhtp_hook_event_cb           on_event;
 
     void * on_headers_start_arg;
     void * on_header_arg;
@@ -467,14 +493,17 @@
     void * on_read_arg;
     void * on_request_fini_arg;
     void * on_connection_fini_arg;
+    void * on_connection_error_arg;
     void * on_error_arg;
     void * on_new_chunk_arg;
     void * on_chunk_fini_arg;
     void * on_chunks_fini_arg;
     void * on_hostname_arg;
     void * on_write_arg;
+    void * on_event_arg;
 };
 
+#ifndef EVHTP_DISABLE_SSL
 struct evhtp_ssl_cfg_s {
     char                  * pemfile;
     char                  * privfile;
@@ -499,6 +528,7 @@
     evhtp_ssl_scache_del    scache_del;
     void                  * args;
 };
+#endif
 
 /**
  * @brief creates a new evhtp_t instance
@@ -508,8 +538,8 @@
  *
  * @return a new evhtp_t structure or NULL on error
  */
-evhtp_t * evhtp_new(evbase_t * evbase, void * arg);
-void      evhtp_free(evhtp_t * evhtp);
+EVHTP_EXPORT evhtp_t * evhtp_new(evbase_t * evbase, void * arg);
+EVHTP_EXPORT void      evhtp_free(evhtp_t * evhtp);
 
 
 /**
@@ -521,10 +551,36 @@
  * @param r read-timeout in timeval
  * @param w write-timeout in timeval.
  */
-void evhtp_set_timeouts(evhtp_t * htp, const struct timeval * r, const struct timeval * w);
-void evhtp_set_bev_flags(evhtp_t * htp, int flags);
-int  evhtp_ssl_use_threads(void);
-int  evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * ssl_cfg);
+EVHTP_EXPORT void evhtp_set_timeouts(evhtp_t * htp, const struct timeval * r, const struct timeval * w);
+
+
+/**
+ * @brief during the request processing cycle, these flags will be used to
+ *        for query argument parsing. i.e., what to parse and not to parse.
+ *
+ *        SEE: EVHTP_PARSE_QUERY_* stuff.
+ *
+ *        For example, if you do not wish for the streaming parser attempting the act
+ *        of fragment parsing:
+ *           evhtp_set_parser_flags(htp, EVHTP_PARSE_QUERY_FLAG_IGNORE_FRAGMENTS);
+ *
+ * @param htp
+ * @param flags
+ */
+EVHTP_EXPORT void evhtp_set_parser_flags(evhtp_t * htp, int flags);
+
+/**
+ * @brief bufferevent flags which will be used for bev sockets.
+ *
+ * @param htp
+ * @param flags
+ */
+EVHTP_EXPORT void evhtp_set_bev_flags(evhtp_t * htp, int flags);
+
+#ifndef EVHTP_DISABLE_SSL
+EVHTP_EXPORT int evhtp_ssl_use_threads(void);
+EVHTP_EXPORT int evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * ssl_cfg);
+#endif
 
 
 /**
@@ -533,7 +589,7 @@
  *
  * @param htp
  */
-void evhtp_disable_100_continue(evhtp_t * htp);
+EVHTP_EXPORT void evhtp_disable_100_continue(evhtp_t * htp);
 
 /**
  * @brief creates a lock around callbacks and hooks, allowing for threaded
@@ -543,7 +599,7 @@
  *
  * @return 0 on success, -1 on error
  */
-int evhtp_use_callback_locks(evhtp_t * htp);
+EVHTP_EXPORT int evhtp_use_callback_locks(evhtp_t * htp);
 
 /**
  * @brief sets a callback which is called if no other callbacks are matched
@@ -552,10 +608,9 @@
  * @param cb  the function to be executed
  * @param arg user-defined argument passed to the callback
  */
-void evhtp_set_gencb(evhtp_t * htp, evhtp_callback_cb cb, void * arg);
-void evhtp_set_pre_accept_cb(evhtp_t * htp, evhtp_pre_accept_cb, void * arg);
-void evhtp_set_post_accept_cb(evhtp_t * htp, evhtp_post_accept_cb, void * arg);
-
+EVHTP_EXPORT void evhtp_set_gencb(evhtp_t * htp, evhtp_callback_cb cb, void * arg);
+EVHTP_EXPORT void evhtp_set_pre_accept_cb(evhtp_t * htp, evhtp_pre_accept_cb, void * arg);
+EVHTP_EXPORT void evhtp_set_post_accept_cb(evhtp_t * htp, evhtp_post_accept_cb, void * arg);
 
 /**
  * @brief sets a callback to be executed on a specific path
@@ -567,7 +622,9 @@
  *
  * @return evhtp_callback_t * on success, NULL on error.
  */
-evhtp_callback_t * evhtp_set_cb(evhtp_t * htp, const char * path, evhtp_callback_cb cb, void * arg);
+EVHTP_EXPORT evhtp_callback_t * evhtp_set_cb(evhtp_t * htp, const char * path,
+    evhtp_callback_cb cb, void * arg);
+
 
 
 /**
@@ -581,7 +638,8 @@
  * @return evhtp_callback_t * on success, NULL on error
  */
 #ifndef EVHTP_DISABLE_REGEX
-evhtp_callback_t * evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg);
+EVHTP_EXPORT evhtp_callback_t * evhtp_set_regex_cb(evhtp_t * htp, const char * pattern,
+    evhtp_callback_cb cb, void * arg);
 #endif
 
 
@@ -598,7 +656,8 @@
  *
  * @return
  */
-evhtp_callback_t * evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg);
+EVHTP_EXPORT evhtp_callback_t * evhtp_set_glob_cb(evhtp_t * htp, const char * pattern,
+    evhtp_callback_cb cb, void * arg);
 
 /**
  * @brief sets a callback hook for either a connection or a path/regex .
@@ -639,7 +698,8 @@
  *
  * @return 0 on success, -1 on error (if hooks is NULL, it is allocated)
  */
-int evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void * arg);
+EVHTP_EXPORT int evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type,
+    evhtp_hook cb, void * arg);
 
 
 /**
@@ -650,7 +710,7 @@
  *
  * @return
  */
-int evhtp_unset_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type);
+EVHTP_EXPORT int evhtp_unset_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type);
 
 
 /**
@@ -660,8 +720,7 @@
  *
  * @return
  */
-int evhtp_unset_all_hooks(evhtp_hooks_t ** hooks);
-
+EVHTP_EXPORT int evhtp_unset_all_hooks(evhtp_hooks_t ** hooks);
 
 /**
  * @brief bind to a socket, optionally with specific protocol support
@@ -678,7 +737,7 @@
  *
  * @return
  */
-int evhtp_bind_socket(evhtp_t * htp, const char * addr, uint16_t port, int backlog);
+EVHTP_EXPORT int evhtp_bind_socket(evhtp_t * htp, const char * addr, uint16_t port, int backlog);
 
 
 /**
@@ -686,25 +745,53 @@
  *
  * @param htp
  */
-void evhtp_unbind_socket(evhtp_t * htp);
+EVHTP_EXPORT void evhtp_unbind_socket(evhtp_t * htp);
 
 /**
  * @brief bind to an already allocated sockaddr.
  *
  * @param htp
- * @param
+ * @parami s
  * @param sin_len
  * @param backlog
  *
  * @return
  */
-int  evhtp_bind_sockaddr(evhtp_t * htp, struct sockaddr *, size_t sin_len, int backlog);
+EVHTP_EXPORT int evhtp_bind_sockaddr(evhtp_t * htp, struct sockaddr *,
+    size_t sin_len, int backlog);
 
-int  evhtp_use_threads(evhtp_t * htp, evhtp_thread_init_cb init_cb, int nthreads, void * arg);
-void evhtp_send_reply(evhtp_request_t * request, evhtp_res code);
-void evhtp_send_reply_start(evhtp_request_t * request, evhtp_res code);
-void evhtp_send_reply_body(evhtp_request_t * request, evbuf_t * buf);
-void evhtp_send_reply_end(evhtp_request_t * request);
+
+/**
+ * @brief Enable thread-pool support for an evhtp_t context. Connectios are
+ *       distributed across 'nthreads'. An optional "on-start" callback can
+ *       be set which allows you to manipulate the thread-specific inforation
+ *       (such as the thread-specific event_base).
+ *
+ * @param htp
+ * @param init_cb
+ * @param nthreads
+ * @param arg
+ *
+ * @return
+ */
+EVHTP_EXPORT int evhtp_use_threads(evhtp_t * htp, evhtp_thread_init_cb init_cb, int nthreads, void * arg);
+
+
+/**
+ * @brief generates all the right information for a reply to be sent to the client
+ *
+ * @param request
+ * @param code HTTP return status code
+ */
+EVHTP_EXPORT void evhtp_send_reply(evhtp_request_t * request, evhtp_res code);
+
+
+/* The following three functions allow for the user to do what evhtp_send_reply does at its core
+ * but for the weak of heart.
+ */
+EVHTP_EXPORT void evhtp_send_reply_start(evhtp_request_t * request, evhtp_res code);
+EVHTP_EXPORT void evhtp_send_reply_body(evhtp_request_t * request, evbuf_t * buf);
+EVHTP_EXPORT void evhtp_send_reply_end(evhtp_request_t * request);
 
 /**
  * @brief Determine if a response should have a body.
@@ -712,8 +799,7 @@
  * @return 1 if the response MUST have a body; 0 if the response MUST NOT have
  *     a body.
  */
-int evhtp_response_needs_body(const evhtp_res code, const htp_method method);
-
+EVHTP_EXPORT int evhtp_response_needs_body(const evhtp_res code, const htp_method method);
 
 /**
  * @brief start a chunked response. If data already exists on the output buffer,
@@ -722,7 +808,7 @@
  * @param request
  * @param code
  */
-void evhtp_send_reply_chunk_start(evhtp_request_t * request, evhtp_res code);
+EVHTP_EXPORT void evhtp_send_reply_chunk_start(evhtp_request_t * request, evhtp_res code);
 
 
 /**
@@ -731,8 +817,7 @@
  * @param request
  * @param buf
  */
-void evhtp_send_reply_chunk(evhtp_request_t * request, evbuf_t * buf);
-
+EVHTP_EXPORT void evhtp_send_reply_chunk(evhtp_request_t * request, evbuf_t * buf);
 
 /**
  * @brief call when all chunks have been sent and you wish to send the last
@@ -740,7 +825,7 @@
  *
  * @param request
  */
-void evhtp_send_reply_chunk_end(evhtp_request_t * request);
+EVHTP_EXPORT void evhtp_send_reply_chunk_end(evhtp_request_t * request);
 
 /**
  * @brief creates a new evhtp_callback_t structure.
@@ -761,8 +846,16 @@
  *
  * @return 0 on success, -1 on error.
  */
-evhtp_callback_t * evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_cb cb, void * arg);
-void               evhtp_callback_free(evhtp_callback_t * callback);
+EVHTP_EXPORT evhtp_callback_t *
+evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_cb cb, void * arg);
+
+
+/**
+ * @brief frees information associated with a ainflwx callback.
+ *
+ * @param callback
+ */
+EVHTP_EXPORT void evhtp_callback_free(evhtp_callback_t * callback);
 
 
 /**
@@ -773,7 +866,7 @@
  *
  * @return 0 on success, -1 on error
  */
-int evhtp_callbacks_add_callback(evhtp_callbacks_t * cbs, evhtp_callback_t * cb);
+EVHTP_EXPORT int evhtp_callbacks_add_callback(evhtp_callbacks_t * cbs, evhtp_callback_t * cb);
 
 
 /**
@@ -788,7 +881,7 @@
  *
  * @return
  */
-int evhtp_add_vhost(evhtp_t * evhtp, const char * name, evhtp_t * vhost);
+EVHTP_EXPORT int evhtp_add_vhost(evhtp_t * evhtp, const char * name, evhtp_t * vhost);
 
 
 /**
@@ -801,7 +894,7 @@
  *
  * @return
  */
-int evhtp_add_alias(evhtp_t * evhtp, const char * name);
+EVHTP_EXPORT int evhtp_add_alias(evhtp_t * evhtp, const char * name);
 
 /**
  * @brief Allocates a new key/value structure.
@@ -813,15 +906,62 @@
  *
  * @return evhtp_kv_t * on success, NULL on error.
  */
-evhtp_kv_t  * evhtp_kv_new(const char * key, const char * val, char kalloc, char valloc);
-evhtp_kvs_t * evhtp_kvs_new(void);
+EVHTP_EXPORT evhtp_kv_t * evhtp_kv_new(const char * key, const char * val, char kalloc, char valloc);
 
-void          evhtp_kv_free(evhtp_kv_t * kv);
-void          evhtp_kvs_free(evhtp_kvs_t * kvs);
-void          evhtp_kv_rm_and_free(evhtp_kvs_t * kvs, evhtp_kv_t * kv);
 
-const char  * evhtp_kv_find(evhtp_kvs_t * kvs, const char * key);
-evhtp_kv_t  * evhtp_kvs_find_kv(evhtp_kvs_t * kvs, const char * key);
+/**
+ * @brief creates an empty list of key/values
+ *
+ * @return
+ */
+EVHTP_EXPORT evhtp_kvs_t * evhtp_kvs_new(void);
+
+
+/**
+ * @brief frees resources allocated for a single key/value
+ *
+ * @param kv
+ */
+EVHTP_EXPORT void evhtp_kv_free(evhtp_kv_t * kv);
+
+
+/**
+ * @brief frees a the list of key/values, and all underlying entries
+ *
+ * @param kvs
+ */
+EVHTP_EXPORT void evhtp_kvs_free(evhtp_kvs_t * kvs);
+
+
+/**
+ * @brief free's resources associated with 'kv' if ONLY found within the key/value list
+ *
+ * @param kvs
+ * @param kv
+ */
+EVHTP_EXPORT void evhtp_kv_rm_and_free(evhtp_kvs_t * kvs, evhtp_kv_t * kv);
+
+
+/**
+ * @brief find the string value of 'key' from the key/value list 'kvs'
+ *
+ * @param kvs
+ * @param key
+ *
+ * @return NULL if not found
+ */
+EVHTP_EXPORT const char * evhtp_kv_find(evhtp_kvs_t * kvs, const char * key);
+
+
+/**
+ * @brief find the evhtp_kv_t reference 'key' from the k/val list 'kvs'
+ *
+ * @param kvs
+ * @param key
+ *
+ * @return
+ */
+EVHTP_EXPORT evhtp_kv_t * evhtp_kvs_find_kv(evhtp_kvs_t * kvs, const char * key);
 
 
 /**
@@ -830,7 +970,7 @@
  * @param kvs an evhtp_kvs_t structure
  * @param kv  an evhtp_kv_t structure
  */
-void evhtp_kvs_add_kv(evhtp_kvs_t * kvs, evhtp_kv_t * kv);
+EVHTP_EXPORT void evhtp_kvs_add_kv(evhtp_kvs_t * kvs, evhtp_kv_t * kv);
 
 /**
  * @brief appends all key/val structures from src tailq onto dst tailq
@@ -838,22 +978,57 @@
  * @param dst an evhtp_kvs_t structure
  * @param src an evhtp_kvs_t structure
  */
-void evhtp_kvs_add_kvs(evhtp_kvs_t * dst, evhtp_kvs_t * src);
+EVHTP_EXPORT void evhtp_kvs_add_kvs(evhtp_kvs_t * dst, evhtp_kvs_t * src);
 
-int  evhtp_kvs_for_each(evhtp_kvs_t * kvs, evhtp_kvs_iterator cb, void * arg);
+
+/**
+ * @brief callback iterator which executes 'cb' for every entry in 'kvs'
+ *
+ * @param kvs
+ * @param cb
+ * @param arg
+ *
+ * @return
+ */
+EVHTP_EXPORT int evhtp_kvs_for_each(evhtp_kvs_t * kvs, evhtp_kvs_iterator cb, void * arg);
+
+#define EVHTP_PARSE_QUERY_FLAG_STRICT                 0
+#define EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX             (1 << 0)
+#define EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS       (1 << 1)
+#define EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS        (1 << 2)
+#define EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP (1 << 3)
+#define EVHTP_PARSE_QUERY_FLAG_IGNORE_FRAGMENTS       (1 << 4)
+#define EVHTP_PARSE_QUERY_FLAG_LENIENT        \
+    EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX         \
+    | EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS \
+    | EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS  \
+    | EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP
 
 /**
  * @brief Parses the query portion of the uri into a set of key/values
  *
- * Parses query arguments like "?herp=derp&foo=bar;blah=baz"
+ * Parses query arguments like "?herp=&foo=bar;blah=baz&a=%3"
+ *
+ * @param query data containing the uri query arguments
+ * @param len size of the data
+ * @param flags parse query flags to alter 'strictness' (see EVHTP_PARSE_QUERY_FLAG_*)
+ *
+ * @return evhtp_query_t * on success, NULL on error
+ */
+EVHTP_EXPORT evhtp_query_t * evhtp_parse_query_wflags(const char * query, size_t len, int flags);
+
+/**
+ * @brief Parses the query portion of the uri into a set of key/values in a
+ *        strict manner
+ *
+ * Parses query arguments like "?herp=derp&foo=bar&blah=baz"
  *
  * @param query data containing the uri query arguments
  * @param len size of the data
  *
  * @return evhtp_query_t * on success, NULL on error
  */
-evhtp_query_t * evhtp_parse_query(const char * query, size_t len);
-
+EVHTP_EXPORT evhtp_query_t * evhtp_parse_query(const char * query, size_t len);
 
 /**
  * @brief Unescapes strings like '%7B1,%202,%203%7D' would become '{1, 2, 3}'
@@ -864,7 +1039,7 @@
  *
  * @return 0 on success, -1 on error
  */
-int evhtp_unescape_string(unsigned char ** out, unsigned char * str, size_t str_len);
+EVHTP_EXPORT int evhtp_unescape_string(unsigned char ** out, unsigned char * str, size_t str_len);
 
 /**
  * @brief creates a new evhtp_header_t key/val structure
@@ -876,7 +1051,8 @@
  *
  * @return evhtp_header_t * or NULL on error
  */
-evhtp_header_t * evhtp_header_new(const char * key, const char * val, char kalloc, char valloc);
+EVHTP_EXPORT evhtp_header_t * evhtp_header_new(const char * key, const char * val,
+    char kalloc, char valloc);
 
 /**
  * @brief creates a new evhtp_header_t, sets only the key, and adds to the
@@ -888,7 +1064,8 @@
  *
  * @return an evhtp_header_t pointer or NULL on error
  */
-evhtp_header_t * evhtp_header_key_add(evhtp_headers_t * headers, const char * key, char kalloc);
+EVHTP_EXPORT evhtp_header_t * evhtp_header_key_add(evhtp_headers_t * headers,
+    const char * key, char kalloc);
 
 
 /**
@@ -900,7 +1077,8 @@
  *
  * @return an evhtp_header_t pointer or NULL on error
  */
-evhtp_header_t * evhtp_header_val_add(evhtp_headers_t * headers, const char * val, char valloc);
+EVHTP_EXPORT evhtp_header_t * evhtp_header_val_add(evhtp_headers_t * headers,
+    const char * val, char valloc);
 
 
 /**
@@ -909,7 +1087,7 @@
  * @param headers
  * @param header
  */
-void evhtp_headers_add_header(evhtp_headers_t * headers, evhtp_header_t * header);
+EVHTP_EXPORT void evhtp_headers_add_header(evhtp_headers_t * headers, evhtp_header_t * header);
 
 /**
  * @brief finds the value of a key in a evhtp_headers_t structure
@@ -919,7 +1097,7 @@
  *
  * @return the value of the header key if found, NULL if not found.
  */
-const char * evhtp_header_find(evhtp_headers_t * headers, const char * key);
+EVHTP_EXPORT const char * evhtp_header_find(evhtp_headers_t * headers, const char * key);
 
 #define evhtp_header_find         evhtp_kv_find
 #define evhtp_headers_find_header evhtp_kvs_find_kv
@@ -942,12 +1120,13 @@
  *
  * @return htp_method enum
  */
-htp_method evhtp_request_get_method(evhtp_request_t * r);
+EVHTP_EXPORT htp_method evhtp_request_get_method(evhtp_request_t * r);
 
-void       evhtp_connection_pause(evhtp_connection_t * connection);
-void       evhtp_connection_resume(evhtp_connection_t * connection);
-void       evhtp_request_pause(evhtp_request_t * request);
-void       evhtp_request_resume(evhtp_request_t * request);
+/* the following functions all do the same thing, pause and the processing */
+EVHTP_EXPORT void evhtp_connection_pause(evhtp_connection_t * connection);
+EVHTP_EXPORT void evhtp_connection_resume(evhtp_connection_t * connection);
+EVHTP_EXPORT void evhtp_request_pause(evhtp_request_t * request);
+EVHTP_EXPORT void evhtp_request_resume(evhtp_request_t * request);
 
 
 /**
@@ -957,7 +1136,7 @@
  *
  * @return evhtp_connection_t on success, otherwise NULL
  */
-evhtp_connection_t * evhtp_request_get_connection(evhtp_request_t * request);
+EVHTP_EXPORT evhtp_connection_t * evhtp_request_get_connection(evhtp_request_t * request);
 
 /**
  * @brief Sets the connections underlying bufferevent
@@ -965,7 +1144,7 @@
  * @param conn
  * @param bev
  */
-void evhtp_connection_set_bev(evhtp_connection_t * conn, evbev_t * bev);
+EVHTP_EXPORT void evhtp_connection_set_bev(evhtp_connection_t * conn, evbev_t * bev);
 
 /**
  * @brief sets the underlying bufferevent for a evhtp_request
@@ -973,7 +1152,7 @@
  * @param request
  * @param bev
  */
-void evhtp_request_set_bev(evhtp_request_t * request, evbev_t * bev);
+EVHTP_EXPORT void evhtp_request_set_bev(evhtp_request_t * request, evbev_t * bev);
 
 
 /**
@@ -983,8 +1162,7 @@
  *
  * @return bufferevent on success, otherwise NULL
  */
-evbev_t * evhtp_connection_get_bev(evhtp_connection_t * conn);
-
+EVHTP_EXPORT evbev_t * evhtp_connection_get_bev(evhtp_connection_t * conn);
 
 /**
  * @brief sets a connection-specific read/write timeout which overrides the
@@ -994,7 +1172,10 @@
  * @param r timeval for read
  * @param w timeval for write
  */
-void evhtp_connection_set_timeouts(evhtp_connection_t * conn, const struct timeval * r, const struct timeval * w);
+EVHTP_EXPORT void
+evhtp_connection_set_timeouts(evhtp_connection_t * conn,
+    const struct timeval                         * r,
+    const struct timeval                         * w);
 
 /**
  * @brief returns the underlying requests bufferevent
@@ -1003,7 +1184,7 @@
  *
  * @return bufferevent on success, otherwise NULL
  */
-evbev_t * evhtp_request_get_bev(evhtp_request_t * request);
+EVHTP_EXPORT evbev_t * evhtp_request_get_bev(evhtp_request_t * request);
 
 
 /**
@@ -1018,7 +1199,7 @@
  *
  * @return underlying connections bufferevent.
  */
-evbev_t * evhtp_connection_take_ownership(evhtp_connection_t * connection);
+EVHTP_EXPORT evbev_t * evhtp_connection_take_ownership(evhtp_connection_t * connection);
 
 
 /**
@@ -1027,9 +1208,8 @@
  *
  * @param connection
  */
-void evhtp_connection_free(evhtp_connection_t * connection);
-
-void evhtp_request_free(evhtp_request_t * request);
+EVHTP_EXPORT void evhtp_connection_free(evhtp_connection_t * connection);
+EVHTP_EXPORT void evhtp_request_free(evhtp_request_t * request);
 
 /**
  * @brief set a max body size to accept for an incoming request, this will
@@ -1038,8 +1218,7 @@
  * @param htp
  * @param len
  */
-void evhtp_set_max_body_size(evhtp_t * htp, uint64_t len);
-
+EVHTP_EXPORT void evhtp_set_max_body_size(evhtp_t * htp, uint64_t len);
 
 /**
  * @brief set a max body size for a specific connection, this will default to
@@ -1048,7 +1227,7 @@
  * @param conn
  * @param len
  */
-void evhtp_connection_set_max_body_size(evhtp_connection_t * conn, uint64_t len);
+EVHTP_EXPORT void evhtp_connection_set_max_body_size(evhtp_connection_t * conn, uint64_t len);
 
 /**
  * @brief just calls evhtp_connection_set_max_body_size for the request.
@@ -1056,7 +1235,7 @@
  * @param request
  * @param len
  */
-void evhtp_request_set_max_body_size(evhtp_request_t * request, uint64_t len);
+EVHTP_EXPORT void evhtp_request_set_max_body_size(evhtp_request_t * request, uint64_t len);
 
 /**
  * @brief sets a maximum number of requests that a single connection can make.
@@ -1064,7 +1243,7 @@
  * @param htp
  * @param num
  */
-void evhtp_set_max_keepalive_requests(evhtp_t * htp, uint64_t num);
+EVHTP_EXPORT void evhtp_set_max_keepalive_requests(evhtp_t * htp, uint64_t num);
 
 
 /**
@@ -1080,8 +1259,12 @@
  *
  * @return
  */
-int evhtp_connection_set_ratelimit(evhtp_connection_t * c, size_t read_rate,
-    size_t read_burst, size_t write_rate, size_t write_burst, const struct timeval * tick);
+EVHTP_EXPORT int
+evhtp_connection_set_ratelimit(evhtp_connection_t * c,
+    size_t read_rate, size_t read_burst,
+    size_t write_rate,
+    size_t write_burst,
+    const struct timeval * tick);
 
 /*****************************************************************
 * client request functions                                      *
@@ -1090,19 +1273,34 @@
 /**
  * @brief allocate a new connection
  */
-evhtp_connection_t * evhtp_connection_new(evbase_t * evbase, const char * addr, uint16_t port);
+EVHTP_EXPORT evhtp_connection_t *
+evhtp_connection_new_dns(evbase_t * evbase,
+    struct evdns_base * dns_base,
+    const char * addr, uint16_t port);
+
+/**
+ * @brief allocate a new connection
+ */
+EVHTP_EXPORT evhtp_connection_t *
+evhtp_connection_new(evbase_t * evbase, const char * addr, uint16_t port);
+
+#ifndef DISABLE_SSL
+evhtp_connection_t * evhtp_connection_ssl_new(evbase_t * evbase, const char * addr, uint16_t port, evhtp_ssl_ctx_t * ctx);
+#endif
+
 
 /**
  * @brief allocate a new request
  */
-evhtp_request_t * evhtp_request_new(evhtp_callback_cb cb, void * arg);
+EVHTP_EXPORT evhtp_request_t * evhtp_request_new(evhtp_callback_cb cb, void * arg);
 
 /**
  * @brief make a client request
  */
-int          evhtp_make_request(evhtp_connection_t * c, evhtp_request_t * r, htp_method meth, const char * uri);
+EVHTP_EXPORT int evhtp_make_request(evhtp_connection_t * c,
+    evhtp_request_t * r, htp_method meth, const char * uri);
 
-unsigned int evhtp_request_status(evhtp_request_t *);
+EVHTP_EXPORT unsigned int evhtp_request_status(evhtp_request_t *);
 
 #ifdef __cplusplus
 }
diff --git a/evhtp.pc.in b/evhtp.pc.in
new file mode 100644
index 0000000..8ca3523
--- /dev/null
+++ b/evhtp.pc.in
@@ -0,0 +1,11 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@
+
+Name: libevhtp
+Description: A more flexible replacement for libevent's httpd API
+Version: @PROJECT_VERSION@
+Libs: -L${libdir} -levhtp
+Libs.private: @LIBEVHTP_EXTERNAL_LIBS@
+Cflags: -I${includedir}
+
diff --git a/evhtp_numtoa.c b/evhtp_numtoa.c
new file mode 100644
index 0000000..6ccfb89
--- /dev/null
+++ b/evhtp_numtoa.c
@@ -0,0 +1,77 @@
+/*
+ * DERIVED FROM the stringencoders library's modp_numtoa
+ *
+ * Copyright ; 2007, Nick Galbreath -- nickg [at] client9 [dot] com
+ * All rights reserved.
+ * http://code.google.com/p/stringencoders/
+ * Released under the MIT license.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "evhtp-internal.h"
+#include "evhtp_numtoa.h"
+
+static inline void
+strreverse(char * begin, char * end) {
+    char aux;
+
+    while (end > begin) {
+        aux = *end, *end-- = *begin, *begin++ = aux;
+    }
+}
+
+size_t
+evhtp_modp_u64toa(uint64_t value, char * str) {
+    char * wstr = str;
+
+    /* Conversion. Number is reversed. */
+    do {
+        *wstr++ = (char)(48 + (value % 10));
+    } while (value /= 10);
+
+    *wstr = '\0';
+
+    /* Reverse string */
+    strreverse(str, wstr - 1);
+
+    return (size_t)(wstr - str);
+}
+
+EXPORT_SYMBOL(evhtp_modp_u64toa);
+
+size_t
+evhtp_modp_u32toa(uint32_t value, char * str) {
+    char * wstr = str;
+
+    /* Conversion. Number is reversed. */
+    do {
+        *wstr++ = (char)(48 + (value % 10));
+    } while (value /= 10);
+
+    *wstr = '\0';
+
+    /* Reverse string */
+    strreverse(str, wstr - 1);
+
+    return (size_t)(wstr - str);
+}
+
+EXPORT_SYMBOL(evhtp_modp_u32toa);
+
+inline size_t
+evhtp_modp_sizetoa(size_t value, char * str) {
+#if EVHTP_SYS_ARCH == 64
+    return evhtp_modp_u64toa(value, str);
+#elif EVHTP_SYS_ARCH == 32
+    return evhtp_modp_u32toa(value, str);
+#else
+#warn "UNKNOWN ARCH"
+#endif
+}
+
+EXPORT_SYMBOL(evhtp_modp_sizetoa);
diff --git a/evhtp_numtoa.h b/evhtp_numtoa.h
new file mode 100644
index 0000000..de295a4
--- /dev/null
+++ b/evhtp_numtoa.h
@@ -0,0 +1,47 @@
+#ifndef __EVHTP_NUMTOA_H__
+#define __EVHTP_NUMTOA_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief based on the system architecture, convert a size_t
+ *        number to a string.
+ *
+ * @param value the input value
+ * @param str The output buffer, should be 24 chars or more.
+ *
+ * @return
+ */
+size_t evhtp_modp_sizetoa(size_t value, char * str);
+
+/**
+ * @brief converts uint32_t value to string
+ *
+ * @param value input value
+ * @param str output buffer, should be 16 chars or more
+ *
+ * @return
+ */
+size_t evhtp_modp_u32toa(uint32_t value, char * str);
+
+
+/**
+ * @brief convert uint64_t value to a string
+ *
+ * @param value input value
+ * @param str output buffer, should be 24 chars or more
+ *
+ * @return
+ */
+size_t evhtp_modp_u64toa(uint64_t value, char * str);
+
+#define evhtp_modp_uchartoa(_val) (unsigned char)('0' + _val)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/evthr/evthr.c b/evthr/evthr.c
index 22303ec..486c469 100644
--- a/evthr/evthr.c
+++ b/evthr/evthr.c
@@ -19,24 +19,17 @@
 #include <event2/event.h>
 #include <event2/thread.h>
 
+#include "evhtp-internal.h"
 #include "evthr.h"
 
-#if (__GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC__MINOR__ > 4)) && (!defined(__STRICT_ANSI__) || __STRICT_ANSI__ == 0)
-#define __unused__   __attribute__((unused))
-#else
-#define __unused__
-#endif
-
-#define _EVTHR_MAGIC 0x03fb
-
 typedef struct evthr_cmd        evthr_cmd_t;
 typedef struct evthr_pool_slist evthr_pool_slist_t;
 
 struct evthr_cmd {
-    uint8_t  stop : 1;
+    uint8_t  stop;
     void   * args;
     evthr_cb cb;
-} __attribute__ ((packed));
+};
 
 TAILQ_HEAD(evthr_pool_slist, evthr);
 
@@ -46,15 +39,12 @@
 };
 
 struct evthr {
-    int             cur_backlog;
-    int             max_backlog;
     int             rdr;
     int             wdr;
     char            err;
     ev_t          * event;
     evbase_t      * evbase;
     pthread_mutex_t lock;
-    pthread_mutex_t stat_lock;
     pthread_mutex_t rlock;
     pthread_t     * thr;
     evthr_init_cb   init_cb;
@@ -64,107 +54,46 @@
     TAILQ_ENTRY(evthr) next;
 };
 
-#ifndef TAILQ_FOREACH_SAFE
-#define TAILQ_FOREACH_SAFE(var, head, field, tvar)        \
-    for ((var) = TAILQ_FIRST((head));                     \
-         (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
-         (var) = (tvar))
-#endif
-
-inline void
-evthr_inc_backlog(evthr_t * evthr) {
-    __sync_fetch_and_add(&evthr->cur_backlog, 1);
-}
-
-inline void
-evthr_dec_backlog(evthr_t * evthr) {
-    __sync_fetch_and_sub(&evthr->cur_backlog, 1);
-}
-
-inline int
-evthr_get_backlog(evthr_t * evthr) {
-    return __sync_add_and_fetch(&evthr->cur_backlog, 0);
-}
-
-inline void
-evthr_set_max_backlog(evthr_t * evthr, int max) {
-    evthr->max_backlog = max;
-}
-
-inline int
-evthr_set_backlog(evthr_t * evthr, int num) {
-    int rnum;
-
-    if (evthr->wdr < 0) {
-        return -1;
+static inline int
+_evthr_read(evthr_t * thr, evthr_cmd_t * cmd, evutil_socket_t sock) {
+    if (recv(sock, cmd, sizeof(evthr_cmd_t), 0) != sizeof(evthr_cmd_t)) {
+        return 0;
     }
 
-    rnum = num * sizeof(evthr_cmd_t);
-
-    return setsockopt(evthr->wdr, SOL_SOCKET, SO_RCVBUF, &rnum, sizeof(int));
+    return 1;
 }
 
 static void
-_evthr_read_cmd(evutil_socket_t sock, short __unused__ which, void * args) {
+_evthr_read_cmd(evutil_socket_t sock, short which, void * args) {
     evthr_t   * thread;
     evthr_cmd_t cmd;
-    ssize_t     recvd;
+    int         stopped;
 
     if (!(thread = (evthr_t *)args)) {
         return;
     }
 
-    if (pthread_mutex_trylock(&thread->lock) != 0) {
-        return;
-    }
-
     pthread_mutex_lock(&thread->rlock);
 
-    if ((recvd = recv(sock, &cmd, sizeof(evthr_cmd_t), 0)) <= 0) {
-        pthread_mutex_unlock(&thread->rlock);
-        if (errno == EAGAIN) {
-            goto end;
-        } else {
-            goto error;
-        }
-    }
+    stopped = 0;
 
-    if (recvd < (ssize_t)sizeof(evthr_cmd_t)) {
-        pthread_mutex_unlock(&thread->rlock);
-        goto error;
+    while (_evthr_read(thread, &cmd, sock) == 1) {
+        if (cmd.stop == 1) {
+            stopped = 1;
+            break;
+        }
+
+        if (cmd.cb != NULL) {
+            (cmd.cb)(thread, cmd.args, thread->arg);
+        }
     }
 
     pthread_mutex_unlock(&thread->rlock);
 
-    if (recvd != sizeof(evthr_cmd_t)) {
-        goto error;
+    if (stopped == 1) {
+        event_base_loopbreak(thread->evbase);
     }
 
-    if (cmd.stop == 1) {
-        goto stop;
-    }
-
-    if (cmd.cb != NULL) {
-        cmd.cb(thread, cmd.args, thread->arg);
-        goto done;
-    } else {
-        goto done;
-    }
-
-stop:
-    event_base_loopbreak(thread->evbase);
-done:
-    evthr_dec_backlog(thread);
-end:
-    pthread_mutex_unlock(&thread->lock);
-    return;
-error:
-    pthread_mutex_lock(&thread->stat_lock);
-    thread->cur_backlog = -1;
-    thread->err         = 1;
-    pthread_mutex_unlock(&thread->stat_lock);
-    pthread_mutex_unlock(&thread->lock);
-    event_base_loopbreak(thread->evbase);
     return;
 } /* _evthr_read_cmd */
 
@@ -187,9 +116,11 @@
     event_add(thread->event, NULL);
 
     pthread_mutex_lock(&thread->lock);
+
     if (thread->init_cb != NULL) {
         thread->init_cb(thread, thread->arg);
     }
+
     pthread_mutex_unlock(&thread->lock);
 
     event_base_loop(thread->evbase, 0);
@@ -203,32 +134,16 @@
 
 evthr_res
 evthr_defer(evthr_t * thread, evthr_cb cb, void * arg) {
-    int         cur_backlog;
     evthr_cmd_t cmd;
 
-    cur_backlog = evthr_get_backlog(thread);
 
-    if (thread->max_backlog) {
-        if (cur_backlog + 1 > thread->max_backlog) {
-            return EVTHR_RES_BACKLOG;
-        }
-    }
-
-    if (cur_backlog == -1) {
-        return EVTHR_RES_FATAL;
-    }
-
-    /* cmd.magic = _EVTHR_MAGIC; */
     cmd.cb   = cb;
     cmd.args = arg;
     cmd.stop = 0;
 
     pthread_mutex_lock(&thread->rlock);
 
-    evthr_inc_backlog(thread);
-
     if (send(thread->wdr, &cmd, sizeof(cmd), 0) <= 0) {
-        evthr_dec_backlog(thread);
         pthread_mutex_unlock(&thread->rlock);
         return EVTHR_RES_RETRY;
     }
@@ -255,7 +170,7 @@
     }
 
     pthread_mutex_unlock(&thread->rlock);
-
+    pthread_join(*thread->thr, NULL);
     return EVTHR_RES_OK;
 }
 
@@ -301,11 +216,6 @@
         return NULL;
     }
 
-    if (pthread_mutex_init(&thread->stat_lock, NULL)) {
-        evthr_free(thread);
-        return NULL;
-    }
-
     if (pthread_mutex_init(&thread->rlock, NULL)) {
         evthr_free(thread);
         return NULL;
@@ -316,8 +226,6 @@
 
 int
 evthr_start(evthr_t * thread) {
-    int res;
-
     if (thread == NULL || thread->thr == NULL) {
         return -1;
     }
@@ -326,9 +234,7 @@
         return -1;
     }
 
-    res = pthread_detach(*thread->thr);
-
-    return res;
+    return 0;
 }
 
 void
@@ -396,8 +302,7 @@
 
 evthr_res
 evthr_pool_defer(evthr_pool_t * pool, evthr_cb cb, void * arg) {
-    evthr_t * min_thr = NULL;
-    evthr_t * thr     = NULL;
+    evthr_t * thr = NULL;
 
     if (pool == NULL) {
         return EVTHR_RES_FATAL;
@@ -407,29 +312,13 @@
         return EVTHR_RES_NOCB;
     }
 
-    /* find the thread with the smallest backlog */
-    TAILQ_FOREACH(thr, &pool->threads, next) {
-        int thr_backlog = 0;
-        int min_backlog = 0;
+    thr = TAILQ_FIRST(&pool->threads);
 
-        thr_backlog = evthr_get_backlog(thr);
+    TAILQ_REMOVE(&pool->threads, thr, next);
+    TAILQ_INSERT_TAIL(&pool->threads, thr, next);
 
-        if (min_thr) {
-            min_backlog = evthr_get_backlog(min_thr);
-        }
 
-        if (min_thr == NULL) {
-            min_thr = thr;
-        } else if (thr_backlog == 0) {
-            min_thr = thr;
-	    break;
-        } else if (thr_backlog < min_backlog) {
-            min_thr = thr;
-        }
-
-    }
-
-    return evthr_defer(min_thr, cb, arg);
+    return evthr_defer(thr, cb, arg);
 } /* evthr_pool_defer */
 
 evthr_pool_t *
@@ -463,26 +352,6 @@
 }
 
 int
-evthr_pool_set_backlog(evthr_pool_t * pool, int num) {
-    evthr_t * thr;
-
-    TAILQ_FOREACH(thr, &pool->threads, next) {
-        evthr_set_backlog(thr, num);
-    }
-
-    return 0;
-}
-
-void
-evthr_pool_set_max_backlog(evthr_pool_t * pool, int max) {
-    evthr_t * thr;
-
-    TAILQ_FOREACH(thr, &pool->threads, next) {
-        evthr_set_max_backlog(thr, max);
-    }
-}
-
-int
 evthr_pool_start(evthr_pool_t * pool) {
     evthr_t * evthr = NULL;
 
@@ -501,3 +370,16 @@
     return 0;
 }
 
+EXPORT_SYMBOL(evthr_new);
+EXPORT_SYMBOL(evthr_get_base);
+EXPORT_SYMBOL(evthr_set_aux);
+EXPORT_SYMBOL(evthr_get_aux);
+EXPORT_SYMBOL(evthr_start);
+EXPORT_SYMBOL(evthr_stop);
+EXPORT_SYMBOL(evthr_defer);
+EXPORT_SYMBOL(evthr_free);
+EXPORT_SYMBOL(evthr_pool_new);
+EXPORT_SYMBOL(evthr_pool_start);
+EXPORT_SYMBOL(evthr_pool_stop);
+EXPORT_SYMBOL(evthr_pool_defer);
+EXPORT_SYMBOL(evthr_pool_free);
diff --git a/evthr/evthr.h b/evthr/evthr.h
index d151fdc..c5ae91b 100644
--- a/evthr/evthr.h
+++ b/evthr/evthr.h
@@ -43,19 +43,12 @@
 evthr_res      evthr_stop(evthr_t * evthr);
 evthr_res      evthr_defer(evthr_t * evthr, evthr_cb cb, void * arg);
 void           evthr_free(evthr_t * evthr);
-void           evthr_inc_backlog(evthr_t * evthr);
-void           evthr_dec_backlog(evthr_t * evthr);
-int            evthr_get_backlog(evthr_t * evthr);
-void           evthr_set_max_backlog(evthr_t * evthr, int max);
-int            evthr_set_backlog(evthr_t *, int);
 
 evthr_pool_t * evthr_pool_new(int nthreads, evthr_init_cb init_cb, void * shared);
 int            evthr_pool_start(evthr_pool_t * pool);
 evthr_res      evthr_pool_stop(evthr_pool_t * pool);
 evthr_res      evthr_pool_defer(evthr_pool_t * pool, evthr_cb cb, void * arg);
 void           evthr_pool_free(evthr_pool_t * pool);
-void           evthr_pool_set_max_backlog(evthr_pool_t * evthr, int max);
-int            evthr_pool_set_backlog(evthr_pool_t *, int);
 
 #ifdef __cplusplus
 }
diff --git a/examples/test.c b/examples/test.c
index 4c66740..5dd919b 100644
--- a/examples/test.c
+++ b/examples/test.c
@@ -106,7 +106,6 @@
     evhtp_send_reply(request, EVHTP_RES_OK);
 }
 
-#ifndef EVHTP_DISABLE_REGEX
 static void
 _owned_readcb(evbev_t * bev, void * arg) {
     /* echo the input back to the client */
@@ -129,6 +128,7 @@
                       _owned_eventcb, NULL);
 }
 
+#ifndef EVHTP_DISABLE_REGEX
 static void
 test_regex(evhtp_request_t * req, void * arg) {
     evbuffer_add_printf(req->buffer_out,
@@ -139,8 +139,6 @@
     evhtp_send_reply(req, EVHTP_RES_OK);
 }
 
-#endif
-
 static void
 dynamic_cb(evhtp_request_t * r, void * arg) {
     const char * name = arg;
@@ -170,6 +168,8 @@
     evhtp_send_reply(r, EVHTP_RES_OK);
 }
 
+#endif
+
 static void
 test_foo_cb(evhtp_request_t * req, void * arg ) {
     evbuffer_add_reference(req->buffer_out,
@@ -294,7 +294,7 @@
     evbuffer_add_printf(req->buffer_out,
                         "got %zu bytes of data\n",
                         evbuffer_get_length(buf));
-    //printf("%.*s", (int)evbuffer_get_length(buf), (char *)evbuffer_pullup(buf, evbuffer_get_length(buf)));
+    /* printf("%.*s", (int)evbuffer_get_length(buf), (char *)evbuffer_pullup(buf, evbuffer_get_length(buf))); */
 #endif
     evbuffer_drain(buf, -1);
     return EVHTP_RES_OK;
@@ -498,6 +498,8 @@
     event_base_loopexit(data, NULL);
 }
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 int
 main(int argc, char ** argv) {
     struct event     * ev_sigint;
@@ -508,9 +510,14 @@
     evhtp_callback_t * cb_3   = NULL;
     evhtp_callback_t * cb_4   = NULL;
     evhtp_callback_t * cb_5   = NULL;
+
+#ifndef EVHTP_DISABLE_REGEX
     evhtp_callback_t * cb_6   = NULL;
+#endif
     evhtp_callback_t * cb_7   = NULL;
+#ifndef EVHTP_DISABLE_REGEX
     evhtp_callback_t * cb_8   = NULL;
+#endif
     evhtp_callback_t * cb_9   = NULL;
     evhtp_callback_t * cb_10  = NULL;
     evhtp_callback_t * cb_11  = NULL;
@@ -524,6 +531,7 @@
 
     evbase = event_base_new();
     htp    = evhtp_new(evbase, NULL);
+    htp->parser_flags = EVHTP_PARSE_QUERY_FLAG_IGNORE_FRAGMENTS;
 
     evhtp_set_max_keepalive_requests(htp, max_keepalives);
 
@@ -613,7 +621,7 @@
     }
 #endif
 
-    if (evhtp_bind_socket(htp, bind_addr, bind_port, 128) < 0) {
+    if (evhtp_bind_socket(htp, bind_addr, bind_port, 2046) < 0) {
         fprintf(stderr, "Could not bind socket: %s\n", strerror(errno));
         exit(-1);
     }
@@ -632,3 +640,4 @@
     return 0;
 } /* main */
 
+#pragma GCC diagnostic pop
diff --git a/examples/test_basic.c b/examples/test_basic.c
index c676215..64a8e26 100644
--- a/examples/test_basic.c
+++ b/examples/test_basic.c
@@ -21,9 +21,11 @@
     evhtp_set_cb(htp, "/simple/", testcb, "simple");
     evhtp_set_cb(htp, "/1/ping", testcb, "one");
     evhtp_set_cb(htp, "/1/ping.json", testcb, "two");
+#if 0
 #ifndef EVHTP_DISABLE_EVTHR
     evhtp_use_threads(htp, NULL, 4, NULL);
 #endif
+#endif
     evhtp_bind_socket(htp, "0.0.0.0", 8081, 1024);
 
     event_base_loop(evbase, 0);
diff --git a/examples/test_query.c b/examples/test_query.c
new file mode 100644
index 0000000..fe4adc2
--- /dev/null
+++ b/examples/test_query.c
@@ -0,0 +1,238 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "evhtp.h"
+
+struct test {
+    const char * raw_query;
+    int          exp_error;
+    struct expected {
+        char * key;
+        char * val;
+    } exp[10]; /* avoid flexible array member: limit expectations per raw_query */
+};
+
+static int
+test_cmp(evhtp_query_t * query, evhtp_kv_t * kvobj, const char * valstr, struct expected * exp) {
+    if (!query || !kvobj) {
+        return -1;
+    }
+
+    if (exp->val == NULL) {
+        if (kvobj->val || valstr) {
+            return -1;
+        }
+
+        return 0;
+    }
+
+    if (strcmp(kvobj->val, exp->val)) {
+        printf("\n");
+        printf("    expected: '%s'\n", exp->val);
+        printf("    actual:   '%s'\n", kvobj->val);
+        return -1;
+    }
+
+    if (strcmp(valstr, exp->val)) {
+        return -1;
+    }
+
+    return 0;
+}
+
+/* evhtp_kvs_iterator */
+int
+kvs_print(evhtp_kv_t * kvobj, void * arg) {
+    int * key_idx = arg;
+
+    if (*key_idx) {
+        printf(", ");
+    }
+
+    printf("\"%s\": %s%s%s", kvobj->key,
+           kvobj->val ? "\"" : "",
+           kvobj->val,
+           kvobj->val ? "\"" : "");
+
+    *key_idx += 1;
+
+    return 0;
+}
+
+static int
+query_test(const char * raw_query, int exp_error, struct expected exp[], int flags) {
+    evhtp_query_t   * query;
+    struct expected * check;
+    int               key_idx    = 0;
+    int               idx        = 0;
+    int               num_errors = 0;
+
+    /* print whether error is expected or not */
+    printf("%-7s  ", exp_error ? "(error)" : "");
+    query = evhtp_parse_query_wflags(raw_query, strlen(raw_query), flags);
+    if (!query) {
+        printf("<error>");
+        return exp_error == 0;
+    }
+
+    printf("{");
+    evhtp_kvs_for_each(query, kvs_print, &key_idx);
+    /* TODO check for keys in query but not in exp */
+    printf("}");
+
+    while (1) {
+        evhtp_kv_t * kvobj  = NULL;
+        const char * valstr = NULL;
+
+        check = &exp[idx++];
+
+        if (check == NULL || check->key == NULL) {
+            break;
+        }
+
+        kvobj  = evhtp_kvs_find_kv(query, check->key);
+        valstr = evhtp_kv_find(query, check->key);
+
+        if (test_cmp(query, kvobj, valstr, check) == -1) {
+            num_errors += 1;
+            printf("         ");
+        }
+    }
+
+    if (exp_error) {
+        return -1;
+    }
+
+    return num_errors;
+} /* query_test */
+
+struct test base_tests[] = {
+    { "a=b;key&c=val",           0, {
+                { "a",                       "b;key" },
+                { "c",                       "val" },
+                { NULL,                      NULL },
+            } },
+    { "a=b;key=val",             0, {
+                { "a",                       "b;key=val" },
+                { NULL,                      NULL },
+            } },
+    { "a;b=val",                 0, {
+                { "a;b",                     "val" },
+                { NULL,                      NULL },
+            } },
+    { "end_empty_string=",       1 },
+    { "end_null",                1 },
+    { "hexa=some%20&hexb=bla%0", 0, {
+                { "hexa",                    "some%20" },
+                { "hexb",                    "bla%0" },
+                { NULL,                      NULL },
+            } },
+    { "hexa=some%20;hexb=bla",   0, {
+                { "hexa",                    "some%20;hexb=bla" },
+                { NULL,                      NULL },
+            } },
+    { "hexa%z=some",             0, {
+                { "hexa\%%z",                "some" },
+                { NULL,                      NULL },
+            } },
+    { "aaa=some\%az",            1 },
+};
+
+struct test ignore_hex_tests[] = {
+    { "hexa=some%20&hexb=bla%0&hexc=%", 0, {
+                { "hexa",                           "some%20" },
+                { "hexb",                           "bla%0" },
+                { "hexc",                           "%" },
+                { NULL,                             NULL },
+            } },
+    { "hexa%z=some",                    0, {
+                { "hexa%z",                         "some" },
+                { NULL,                             NULL },
+            } },
+    { "aaa=some%zz",                    0, {
+                { "aaa",                            "some%zz" },
+                { NULL,                             NULL },
+            } },
+};
+
+struct test allow_empty_tests[] = {
+    { "end_empty_string=", 0, {
+                { "end_empty_string",  "" },
+                { NULL,                NULL },
+            } },
+};
+
+struct test allow_null_tests[] = {
+    { "end_null", 0, {
+                { "end_null", NULL },
+                { NULL,       NULL },
+            } },
+};
+
+struct test treat_semicolon_as_sep_tests[] = {
+    { "a=b;key=val", 0, {
+                { "a",           "b" },
+                { "key",         "val" },
+                { NULL,          NULL },
+            } },
+    { "a;b=val",     1 },
+};
+
+struct test lenient_tests[] = {
+    { "a=b;key&c=val",                    0, {
+                { "a",                                "b" },
+                { "key",                              NULL },
+                { "c",                                "val" },
+                { NULL,                               NULL },
+            } },
+    { "a=b;key=val",                      0, {
+                { "a",                                "b" },
+                { "key",                              "val" },
+                { NULL,                               NULL },
+            } },
+    { "end_empty_string=",                0, {
+                { "end_empty_string",                 "" },
+                { NULL,                               NULL },
+            } },
+    { "end_null",                         0, {
+                { "end_null",                         NULL },
+                { NULL,                               NULL },
+            } },
+    { "hexa=some\%a;hexb=bl%0&hexc=\%az", 0, {
+                { "hexa",                             "some\%a" },
+                { "hexb",                             "bl%0" },
+                { "hexc",                             "\%az" },
+                { NULL,                               NULL },
+            } },
+};
+
+static void
+test(const char * raw_query, int exp_error, struct expected exp[], int flags) {
+    printf("         %-30s  ", raw_query);
+    printf("\r  %s\n", query_test(raw_query, exp_error, exp, flags) ? "ERROR" : "OK");
+}
+
+#define ARRAY_SIZE(arr)                    (sizeof(arr) / sizeof((arr)[0]))
+
+int
+main(int argc, char ** argv) {
+    int i;
+
+    #define PARSE_QUERY_TEST(tests, flags) do {                                          \
+            printf("- " # tests "\n");                                                   \
+            for (i = 0; i < ARRAY_SIZE(tests); i++) {                                    \
+                test((tests)[i].raw_query, (tests)[i].exp_error, (tests)[i].exp, flags); \
+            }                                                                            \
+    } while (0)
+
+    PARSE_QUERY_TEST(base_tests, EVHTP_PARSE_QUERY_FLAG_STRICT);
+    PARSE_QUERY_TEST(ignore_hex_tests, EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX);
+    PARSE_QUERY_TEST(allow_empty_tests, EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS);
+    PARSE_QUERY_TEST(allow_null_tests, EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS);
+    PARSE_QUERY_TEST(treat_semicolon_as_sep_tests, EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP);
+    PARSE_QUERY_TEST(lenient_tests, EVHTP_PARSE_QUERY_FLAG_LENIENT);
+
+    return 0;
+}
+
diff --git a/htparse/htparse.c b/htparse/htparse.c
index 352dc90..51c9017 100644
--- a/htparse/htparse.c
+++ b/htparse/htparse.c
@@ -5,9 +5,11 @@
 #include <string.h>
 #include <stdint.h>
 #include <errno.h>
+#include <ctype.h>
 #include <unistd.h>
 
 #include "htparse.h"
+#include "evhtp-internal.h"
 
 #ifdef PARSER_DEBUG
 #define __QUOTE(x)                  # x
@@ -48,10 +50,10 @@
 };
 
 enum parser_flags {
-    parser_flag_chunked               = 1 << 0,
-    parser_flag_connection_keep_alive = 1 << 1,
-    parser_flag_connection_close      = 1 << 2,
-    parser_flag_trailing              = 1 << 3,
+    parser_flag_chunked               = (1 << 0),
+    parser_flag_connection_keep_alive = (1 << 1),
+    parser_flag_connection_close      = (1 << 2),
+    parser_flag_trailing              = (1 << 3),
 };
 
 enum parser_state {
@@ -197,6 +199,8 @@
 
 #define _MIN_READ(a, b) ((a) < (b) ? (a) : (b))
 
+#ifndef HOST_BIG_ENDIAN
+/* Little-endian cmp macros */
 #define _str3_cmp(m, c0, c1, c2, c3) \
     *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
 
@@ -226,6 +230,37 @@
     *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)        \
     && ((uint32_t *)m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4) \
     && m[8] == c8
+#else
+/* Big endian cmp macros */
+#define _str3_cmp(m, c0, c1, c2, c3) \
+    m[0] == c0 && m[1] == c1 && m[2] == c2
+
+#define _str3Ocmp(m, c0, c1, c2, c3) \
+    m[0] == c0 && m[2] == c2 && m[3] == c3
+
+#define _str4cmp(m, c0, c1, c2, c3) \
+    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3
+
+#define _str5cmp(m, c0, c1, c2, c3, c4) \
+    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4
+
+#define _str6cmp(m, c0, c1, c2, c3, c4, c5)              \
+    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
+    && m[4] == c4 && m[5] == c5
+
+#define _str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)     \
+    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
+    && m[4] == c4 && m[5] == c5 && m[6] == c6
+
+#define _str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)      \
+    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
+    && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7
+
+#define _str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)  \
+    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
+    && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8
+
+#endif
 
 #define __HTPARSE_GENHOOK(__n)                                                    \
     static inline int hook_ ## __n ## _run(htparser * p, htparse_hooks * hooks) { \
@@ -272,6 +307,11 @@
 str_to_uint64(char * str, size_t n, int * err) {
     uint64_t value;
 
+    /* Trim whitespace after value. */
+    while (n && isblank(str[n-1])) {
+	    n--;
+    }
+
     if (n > 20) {
         /* 18446744073709551615 is 20 bytes */
         *err = 1;
@@ -459,6 +499,22 @@
     return malloc(sizeof(htparser));
 }
 
+static int
+is_host_char(unsigned char ch)
+{
+    char c = (unsigned char)(ch | 0x20);
+
+    if (c >= 'a' && c <= 'z') {
+        return 1;
+    }
+
+    if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
+        return 1;
+    }
+
+    return 0;
+}
+
 size_t
 htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) {
     unsigned char ch;
@@ -659,6 +715,41 @@
             case s_spaces_before_uri:
                 htparse_log_debug("[%p] s_spaces_before_uri", p);
 
+                /* CONNECT is special - RFC 2817 section 5.2:
+                 * The Request-URI portion of the Request-Line is
+                 * always an 'authority' as defined by URI Generic
+                 * Syntax [2], which is to say the host name and port
+                 * number destination of the requested connection
+                 * separated by a colon
+                 */
+                if (p->method == htp_method_CONNECT) {
+                    switch (ch) {
+                    case ' ':
+                        break;
+                    case '[':
+                        /* Literal IPv6 address start. */
+                        p->buf[p->buf_idx++] = ch;
+                        p->buf[p->buf_idx]   = '\0';
+                        p->host_offset       = &p->buf[p->buf_idx];
+
+                        p->state = s_host_ipv6;
+                        break;
+                    default:
+                        if (!is_host_char(ch)) {
+                            p->error = htparse_error_inval_reqline;
+                            return i + 1;
+                        }
+                        p->host_offset       = &p->buf[p->buf_idx];
+                        p->buf[p->buf_idx++] = ch;
+                        p->buf[p->buf_idx]   = '\0';
+
+                        p->state = s_host;
+                        break;
+                    } /* switch */
+
+                    break;
+                }
+
                 switch (ch) {
                     case ' ':
                         break;
@@ -729,10 +820,6 @@
 
                         res                  = hook_scheme_run(p, hooks, p->scheme_offset, (&p->buf[p->buf_idx] - p->scheme_offset));
 
-#if 0
-                        p->buf_idx           = 0;
-                        p->buf[0]            = '\0';
-#endif
                         p->buf[p->buf_idx++] = ch;
                         p->buf[p->buf_idx]   = '\0';
 
@@ -791,15 +878,7 @@
                     p->state = s_host_ipv6;
                     break;
                 }
-                c = (unsigned char)(ch | 0x20);
-
-                if (c >= 'a' && c <= 'z') {
-                    p->buf[p->buf_idx++] = ch;
-                    p->buf[p->buf_idx]   = '\0';
-                    break;
-                }
-
-                if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
+                if (is_host_char(ch)) {
                     p->buf[p->buf_idx++] = ch;
                     p->buf[p->buf_idx]   = '\0';
                     break;
@@ -1863,30 +1942,22 @@
                     const char * pe      = (const char *)(data + len);
                     size_t       to_read = _MIN_READ(pe - pp, p->content_len);
 
-                    htparse_log_debug("[%p] s_body_read %zu", p, to_read);
-
                     if (to_read > 0) {
                         res = hook_body_run(p, hooks, pp, to_read);
 
                         i  += to_read - 1;
                         p->content_len -= to_read;
+                    }
 
-                        htparse_log_debug("[%p] s_body_read content_len is now %zu", p, p->content_len);
-
-                        if (p->content_len == 0) {
-                            res      = hook_on_msg_complete_run(p, hooks);
-
-                            p->state = s_start;
-                        }
-                    } else {
+                    if (p->content_len == 0) {
                         res      = hook_on_msg_complete_run(p, hooks);
                         p->state = s_start;
                     }
-                }
 
-                if (res) {
-                    p->error = htparse_error_user;
-                    return i + 1;
+                    if (res) {
+                        p->error = htparse_error_user;
+                        return i + 1;
+                    }
                 }
 
                 break;
@@ -1896,8 +1967,36 @@
                 p->error = htparse_error_inval_state;
                 return i + 1;
         } /* switch */
+
+	/* If we successfully completed a request/response we return
+	   to caller, and leave it up to him to call us again if
+	   parsing should continue. */
+	if (p->state == s_start) {
+		return i + 1;
+	}
     }
 
     return i;
 }         /* htparser_run */
 
+EXPORT_SYMBOL(htparser_run);
+EXPORT_SYMBOL(htparser_should_keep_alive);
+EXPORT_SYMBOL(htparser_get_scheme);
+EXPORT_SYMBOL(htparser_get_method);
+EXPORT_SYMBOL(htparser_get_methodstr);
+EXPORT_SYMBOL(htparser_get_methodstr_m);
+EXPORT_SYMBOL(htparser_set_major);
+EXPORT_SYMBOL(htparser_set_minor);
+EXPORT_SYMBOL(htparser_get_major);
+EXPORT_SYMBOL(htparser_get_minor);
+EXPORT_SYMBOL(htparser_get_multipart);
+EXPORT_SYMBOL(htparser_get_status);
+EXPORT_SYMBOL(htparser_get_content_length);
+EXPORT_SYMBOL(htparser_get_content_pending);
+EXPORT_SYMBOL(htparser_get_total_bytes_read);
+EXPORT_SYMBOL(htparser_get_error);
+EXPORT_SYMBOL(htparser_get_strerror);
+EXPORT_SYMBOL(htparser_get_userdata);
+EXPORT_SYMBOL(htparser_set_userdata);
+EXPORT_SYMBOL(htparser_init);
+EXPORT_SYMBOL(htparser_new);