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);