pax_global_header00006660000000000000000000000064134266075330014523gustar00rootroot0000000000000052 comment=e200bfa85bf253e9cfe1c1a9e705fccb176b9171 libevhtp-1.2.18/000077500000000000000000000000001342660753300134315ustar00rootroot00000000000000libevhtp-1.2.18/.github/000077500000000000000000000000001342660753300147715ustar00rootroot00000000000000libevhtp-1.2.18/.github/issue_template.md000066400000000000000000000002601342660753300203340ustar00rootroot00000000000000# Details # Steps or code to reproduce the problem. ## Example code (if applicable) ```C #include int main(int argc, char ** argv) { return 0; } ``` # Version libevhtp-1.2.18/.gitignore000066400000000000000000000003531342660753300154220ustar00rootroot00000000000000# cmake manages these; they shouldn't go in version control # # # they aren't going into version control, but shouldn't be # completely ignored. I'm removing the mods here. # docs/ html/ build/* !build/.gitkeep cmake-build-debug .idea libevhtp-1.2.18/.travis.yml000066400000000000000000000011001342660753300155320ustar00rootroot00000000000000language: c compiler: - gcc - clang sudo: required before_install: - sudo apt-get update -qq - sudo apt-get install libevent-dev libonig-dev script: - cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. && make examples notifications: webhooks: urls: - https://webhooks.gitter.im/e/e0b195833e627a66f549 on_success: change # options: [always|never|change] default: always on_failure: always # options: [always|never|change] default: always on_start: never # options: [always|never|change] default: always libevhtp-1.2.18/.ycm_extra_conf.py000066400000000000000000000127441342660753300170710ustar00rootroot00000000000000import os import os.path import fnmatch import logging import ycm_core import re BASE_FLAGS = [ '-Wall', '-Wextra', '-Werror', '-Wno-long-long', '-Wno-variadic-macros', '-fexceptions', '-ferror-limit=10000', '-DNDEBUG', '-xc', '-I/usr/lib/', '-I/usr/include/', '-I./include', ] SOURCE_EXTENSIONS = [ '.c', ] SOURCE_DIRECTORIES = [ '.', ] HEADER_EXTENSIONS = [ '.h', ] HEADER_DIRECTORIES = [ 'include', 'include/evhtp' ] def IsHeaderFile(filename): extension = os.path.splitext(filename)[1] return extension in HEADER_EXTENSIONS def GetCompilationInfoForFile(database, filename): if IsHeaderFile(filename): basename = os.path.splitext(filename)[0] for extension in SOURCE_EXTENSIONS: # Get info from the source files by replacing the extension. replacement_file = basename + extension if os.path.exists(replacement_file): compilation_info = database.GetCompilationInfoForFile(replacement_file) if compilation_info.compiler_flags_: return compilation_info # If that wasn't successful, try replacing possible header directory with possible source directories. for header_dir in HEADER_DIRECTORIES: for source_dir in SOURCE_DIRECTORIES: src_file = replacement_file.replace(header_dir, source_dir) if os.path.exists(src_file): compilation_info = database.GetCompilationInfoForFile(src_file) if compilation_info.compiler_flags_: return compilation_info return None return database.GetCompilationInfoForFile(filename) def FindNearest(path, target, build_folder): candidate = os.path.join(path, target) if(os.path.isfile(candidate) or os.path.isdir(candidate)): logging.info("Found nearest " + target + " at " + candidate) return candidate; parent = os.path.dirname(os.path.abspath(path)); if(parent == path): raise RuntimeError("Could not find " + target); if(build_folder): candidate = os.path.join(parent, build_folder, target) if(os.path.isfile(candidate) or os.path.isdir(candidate)): logging.info("Found nearest " + target + " in build folder at " + candidate) return candidate; return FindNearest(parent, target, build_folder) def MakeRelativePathsInFlagsAbsolute(flags, working_directory): if not working_directory: return list(flags) new_flags = [] make_next_absolute = False path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ] for flag in flags: new_flag = flag if make_next_absolute: make_next_absolute = False if not flag.startswith('/'): new_flag = os.path.join(working_directory, flag) for path_flag in path_flags: if flag == path_flag: make_next_absolute = True break if flag.startswith(path_flag): path = flag[ len(path_flag): ] new_flag = path_flag + os.path.join(working_directory, path) break if new_flag: new_flags.append(new_flag) return new_flags def FlagsForClangComplete(root): try: clang_complete_path = FindNearest(root, '.clang_complete') clang_complete_flags = open(clang_complete_path, 'r').read().splitlines() return clang_complete_flags except: return None def FlagsForInclude(root): try: include_path = FindNearest(root, 'include') flags = [] for dirroot, dirnames, filenames in os.walk(include_path): for dir_path in dirnames: real_path = os.path.join(dirroot, dir_path) flags = flags + ["-I" + real_path] return flags except: return None def FlagsForCompilationDatabase(root, filename): try: # Last argument of next function is the name of the build folder for # out of source projects compilation_db_path = FindNearest(root, 'compile_commands.json', 'build') compilation_db_dir = os.path.dirname(compilation_db_path) logging.info("Set compilation database directory to " + compilation_db_dir) compilation_db = ycm_core.CompilationDatabase(compilation_db_dir) if not compilation_db: logging.info("Compilation database file found but unable to load") return None compilation_info = GetCompilationInfoForFile(compilation_db, filename) if not compilation_info: logging.info("No compilation info for " + filename + " in compilation database") return None return MakeRelativePathsInFlagsAbsolute( compilation_info.compiler_flags_, compilation_info.compiler_working_dir_) except: return None def FlagsForFile(filename): root = os.path.realpath(filename); compilation_db_flags = FlagsForCompilationDatabase(root, filename) if compilation_db_flags: final_flags = compilation_db_flags else: final_flags = BASE_FLAGS clang_flags = FlagsForClangComplete(root) if clang_flags: final_flags = final_flags + clang_flags include_flags = FlagsForInclude(root) if include_flags: final_flags = final_flags + include_flags return { 'flags': final_flags, 'do_cache': True } libevhtp-1.2.18/CMakeLists.txt000066400000000000000000000264141342660753300162000ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.1) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) project(libevhtp VERSION "1.2.18") # For us YCM users. set(CMAKE_EXPORT_COMPILE_COMMANDS ON) include(colors) include(options) include(CheckFunctionExists) include(CheckIncludeFiles) include(CheckTypeSize) include(CheckCCompilerFlag) include(TestBigEndian) check_function_exists(strndup HAVE_STRNDUP) check_function_exists(strnlen HAVE_STRNLEN) check_include_files(stdlib.h HAVE_STDLIB_H) check_include_files(string.h HAVE_STRING_H) check_include_files(stdint.h HAVE_STDINT_H) check_include_files(errno.h HAVE_ERRNO_H) check_include_files(signal.h HAVE_SIGNAL_H) check_include_files(strings.h HAVE_STRINGS_H) check_include_files(inttypes.h HAVE_INTTYPES_H) check_include_files(stdbool.h HAVE_STDBOOL_H) check_include_files(limits.h HAVE_LIMITS_H) check_include_files(stddef.h HAVE_STDDEF_H) check_include_files(ctype.h HAVE_CTYPE_H) check_include_files(unistd.h HAVE_UNISTD_H) check_include_files(stdarg.h HAVE_STDARG_PROTOTYPES) check_include_files(sys/tree.h HAVE_SYS_TREE) check_include_files(sys/queue.h HAVE_SYS_QUEUE) check_include_files(sys/un.h HAVE_SYS_UN) check_include_files(sys/types.h HAVE_SYS_TYPES_H) check_type_size("int" SIZEOF_INT) check_type_size("long" SIZEOF_LONG) check_type_size("short" SIZEOF_SHORT) test_big_endian(HOST_BIG_ENDIAN) check_c_compiler_flag(-std=c99 has_c99) # NOTE in as of ubuntu 14, this is enabled by default, so if you see it fail # the check, don't freak out. check_c_compiler_flag(-fstack-protector-strong has_stack_protector) set(LIBEVHTP_SOURCE_FILES evhtp.c numtoa.c parser.c log.c) find_package(LibEvent REQUIRED) list(APPEND LIBEVHTP_EXTERNAL_LIBS ${LIBEVENT_LIBRARIES}) list(APPEND LIBEVHTP_EXTERNAL_INCLUDES ${LIBEVENT_INCLUDE_DIRS}) list(APPEND package_deps LibEvent) set(evhtp_dir_headers "include/evhtp/evhtp.h" "include/evhtp/parser.h" "include/evhtp/log.h") if(NOT EVHTP_DISABLE_SSL) find_package(OpenSSL) if(OPENSSL_FOUND) list(APPEND LIBEVHTP_SOURCE_FILES sslutils.c) list(APPEND LIBEVHTP_EXTERNAL_LIBS OpenSSL::SSL OpenSSL::Crypto) list(APPEND package_deps OpenSSL) list(APPEND evhtp_dir_headers "include/evhtp/sslutils.h") endif() endif() if(NOT EVHTP_DISABLE_EVTHR) find_package(Threads) if(TARGET Threads::Threads) list(APPEND LIBEVHTP_SOURCE_FILES thread.c) list(APPEND LIBEVHTP_EXTERNAL_LIBS Threads::Threads) list(APPEND package_deps Threads) list(APPEND evhtp_dir_headers "include/evhtp/thread.h") endif() endif() if(NOT EVHTP_DISABLE_REGEX) find_package(Oniguruma) if(ONIGURUMA_FOUND) list(APPEND LIBEVHTP_EXTERNAL_LIBS ${ONIGURUMA_LIBRARIES}) list(APPEND LIBEVHTP_EXTERNAL_INCLUDES ${ONIGURUMA_INCLUDE_DIRS}) list(APPEND package_deps oniguruma) else() message(STATUS "${BoldRed}${CMAKE_BUILD_TYPE}ONIGURUMA NOT FOUND, DISABLING REGEX SUPPORT${ColourReset}") set(EVHTP_DISABLE_REGEX ON) endif() endif() if(EVHTP_ALLOCATOR STREQUAL "jemalloc") find_package(jemalloc) if(JEMALLOC_FOUND) list(APPEND LIBEVHTP_EXTERNAL_LIBS jemalloc) list(APPEND package_deps jemalloc) endif() elseif(EVHTP_ALLOCATOR STREQUAL "tcmalloc") find_package(tcmalloc) if(TCMALLOC_FOUND) list(APPEND LIBEVHTP_EXTERNAL_LIBS tcmalloc) list(APPEND package_deps tcmalloc) endif() endif() add_library(evhtp ${LIBEVHTP_SOURCE_FILES}) target_link_libraries(evhtp PUBLIC ${LIBEVHTP_EXTERNAL_LIBS}) target_include_directories(evhtp PUBLIC ${LIBEVHTP_EXTERNAL_INCLUDES}) target_compile_definitions(evhtp PUBLIC "PROJECT_VERSION=${PROJECT_VERSION}") if(has_stack_protector) target_compile_options(evhtp PUBLIC -fstack-protector-strong) endif() if(EVHTP_THR_SHARED_PIPE) target_compile_definitions(evhtp PUBLIC EVTHR_SHARED_PIPE) endif() if(has_c99) target_compile_definitions(evhtp PUBLIC EVHTP_HAS_C99) endif() if (HAVE_SYS_TYPES_H) target_compile_definitions(evhtp PUBLIC EVHTP_HAS_SYS_TYPES) 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) list(APPEND compat_headers "${CMAKE_CURRENT_BINARY_DIR}/compat/sys/tree.h") endif() if(NOT HAVE_SYS_QUEUE) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/compat/sys/queue.h.in ${CMAKE_CURRENT_BINARY_DIR}/compat/sys/queue.h) list(APPEND compat_headers "${CMAKE_CURRENT_BINARY_DIR}/compat/sys/queue.h") endif() if(NOT HAVE_SYS_TREE OR NOT HAVE_SYS_QUEUE) target_include_directories(evhtp PUBLIC $) endif() if(NOT HAVE_STRNDUP) target_compile_definitions(evhtp PUBLIC NO_STRNDUP) endif() if(NOT HAVE_STRNLEN) target_compile_definitions(evhtp PUBLIC NO_STRNLEN) endif() if(NOT HAVE_SYS_UN) target_compile_definitions(evhtp PUBLIC NO_SYS_UN) endif() if(HOST_BIG_ENDIAN) target_compile_definitions(evhtp PUBLIC HOST_BIG_ENDIAN) endif() # Test 32/64 bits if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") target_compile_definitions(evhtp PUBLIC EVHTP_SYS_ARCH=64) elseif("${CMAKE_SIZEOF_VOID_P}" EQUAL "4") target_compile_definitions(evhtp PUBLIC EVHTP_SYS_ARCH=32) else() message(ERROR "Unable to determine architecture") endif() if(EVHTP_USE_DEFER_ACCEPT) target_compile_definitions(evhtp PUBLIC USE_DEFER_ACCEPT) endif() if(OPENSSL_FOUND AND APPLE) # Darwin based hosts have deprecated normal openssl functions, which is # annoying to see warnings, for now, just ignore them. target_compile_options(evhtp PRIVATE -Wno-deprecated-declarations) endif() if(WIN32) target_compile_definitions(evhtp PUBLIC WIN32) target_compile_options(evhtp PUBLIC -march=i486) find_library(LIB_WS32 ws2_32) list(APPEND SYS_LIBS ${LIB_WS32}) endif() configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/include/evhtp/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/evhtp/config.h) list(APPEND evhtp_dir_headers "${CMAKE_CURRENT_BINARY_DIR}/include/evhtp/config.h") target_include_directories(evhtp PUBLIC $ $ ) if(BUILD_SHARED_LIBS) set_target_properties(evhtp PROPERTIES VERSION "${PROJECT_VERSION}" SOVERSION 0 OUTPUT_NAME "evhtp" C_VISIBILITY_PRESET hidden) endif() add_subdirectory(examples) # Installation (https://github.com/forexample/package-example) # Layout. This works for all platforms: # * /lib/cmake/ # * /lib/ # * /include/ set(config_install_dir "lib/cmake/${PROJECT_NAME}") set(include_install_dir "include") set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") # Configuration set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") set(namespace "${PROJECT_NAME}::") # Include module with fuction 'write_basic_package_version_file' include(CMakePackageConfigHelpers) # Configure 'ConfigVersion.cmake' # Use: # * PROJECT_VERSION write_basic_package_version_file( "${version_config}" COMPATIBILITY SameMajorVersion ) # Configure 'Config.cmake' # Use variables: # * TARGETS_EXPORT_NAME # * PROJECT_NAME configure_package_config_file( "cmake/Config.cmake.in" "${project_config}" INSTALL_DESTINATION "${config_install_dir}" ) # Targets: # * /lib/libevhtp.a # * header location after install: /include/evhtp.h # * headers can be included by C code `#include ` install( TARGETS evhtp EXPORT "${TARGETS_EXPORT_NAME}" LIBRARY DESTINATION "lib" ARCHIVE DESTINATION "lib" RUNTIME DESTINATION "bin" INCLUDES DESTINATION "${include_install_dir}" ) # Headers: # * include/evhtp.h -> /include/evhtp.h install( FILES "include/evhtp.h" DESTINATION "${include_install_dir}" ) # Headers: # * include/evhtp/parser.h -> /include/evhtp/parser.h install( FILES ${evhtp_dir_headers} DESTINATION "${include_install_dir}/evhtp" ) # Headers: # * compat/sys/tree.h -> /include/evhtp/sys/tree.h install( FILES ${compat_headers} DESTINATION "${include_install_dir}/evhtp/sys" ) # Config # * /lib/cmake/libevhtp/libevhtpConfig.cmake # * /lib/cmake/libevhtp/libevhtpConfigVersion.cmake install( FILES "${project_config}" "${version_config}" DESTINATION "${config_install_dir}" ) # Config # * /lib/cmake/libevhtp/libevhtpTargets.cmake install( EXPORT "${TARGETS_EXPORT_NAME}" NAMESPACE "${namespace}" DESTINATION "${config_install_dir}" ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/evhtp.pc.in ${CMAKE_CURRENT_BINARY_DIR}/evhtp.pc @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/evhtp.pc" DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig") message("") message(STATUS "${BoldBlue}EVHTP_VERSION${ColourReset} : ${BoldGreen} ${PROJECT_VERSION}${ColourReset}") message(STATUS "${BoldBlue}EVHTP_DISABLE_SSL${ColourReset} : ${BoldGreen} ${EVHTP_DISABLE_SSL}${ColourReset}") message(STATUS "${BoldBlue}EVHTP_DISABLE_EVTHR${ColourReset} : ${BoldGreen} ${EVHTP_DISABLE_EVTHR}${ColourReset}") message(STATUS "${BoldBlue}EVHTP_DISABLE_REGEX${ColourReset} : ${BoldGreen} ${EVHTP_DISABLE_REGEX}${ColourReset}") message(STATUS "${BoldBlue}EVHTP_BUILD_SHARED${ColourReset} : ${BoldGreen} ${EVHTP_BUILD_SHARED}${ColourReset}") message(STATUS "${BoldBlue}EVHTP_USE_JEMALLOC${ColourReset} : ${BoldGreen} ${EVHTP_USE_JEMALLOC}${ColourReset}") message(STATUS "${BoldBlue}EVHTP_USE_TCMALLOC${ColourReset} : ${BoldGreen} ${EVHTP_USE_TCMALLOC}${ColourReset}") message("") message(STATUS "${Blue}CMAKE_BUILD_TYPE${ColourReset} : " ${BoldRed}${CMAKE_BUILD_TYPE}${ColourReset}) message(STATUS "${Blue}CMAKE_INSTALL_PREFIX${ColourReset} : " ${BoldMagenta}${CMAKE_INSTALL_PREFIX}${ColourReset}) message(STATUS "${Blue}CMAKE_BINARY_DIR${ColourReset} : " ${CMAKE_BINARY_DIR}) message(STATUS "${Blue}CMAKE_CURRENT_BINARY_DIR${ColourReset} : " ${CMAKE_CURRENT_BINARY_DIR}) message(STATUS "${Blue}CMAKE_CURRENT_SOURCE_DIR${ColourReset} : " ${CMAKE_CURRENT_SOURCE_DIR}) message(STATUS "${Blue}PROJECT_BINARY_DIR${ColourReset} : " ${PROJECT_BINARY_DIR}) message(STATUS "${Blue}PROJECT_SOURCE_DIR${ColourReset} : " ${PROJECT_SOURCE_DIR}) message(STATUS "${Blue}CMAKE_MODULE_PATH${ColourReset} : " ${CMAKE_MODULE_PATH}) message(STATUS "${Blue}CMAKE_SYSTEM_NAME${ColourReset} : " ${CMAKE_SYSTEM_NAME}) message(STATUS "${Blue}CMAKE_SYSTEM_VERSION${ColourReset} : " ${CMAKE_SYSTEM_VERSION}) message(STATUS "${Blue}CMAKE_C_COMPILER${ColourReset} : " ${CMAKE_C_COMPILER}) message(STATUS "${Blue}CMAKE_AR${ColourReset} : " ${CMAKE_AR}) message(STATUS "${Blue}CMAKE_RANLIB${ColourReset} : " ${CMAKE_RANLIB}) message(STATUS "${Blue}CFLAGS${ColourReset} : ${CMAKE_C_FLAGS}") if(CMAKE_BUILD_TYPE MATCHES Debug) message(" ${CMAKE_C_FLAGS_DEBUG}") else(CMAKE_BUILD_TYPE MATCHES Release) message(" ${CMAKE_C_FLAGS_RELEASE}") endif() message("") include(packaging) libevhtp-1.2.18/ChangeLog000066400000000000000000001445061342660753300152150ustar00rootroot00000000000000v1.2.18 o Add htp__evbuffer_add_iovec_ helper for libevent < 2.1 (8991567 Nathan French) o [#122] Fix compilation without deprecated OpenSSL 1.1 APIs (78e8e41 Rosen Penev) o [#122] Reorganize OpenSSL < 1.0.0 compatibility for greater readability. (8e543fe Rosen Penev) o [#123] add missing include for ssize_t (6a74ec7 maxice8) o [#123] include sys/types only if EVHTP_HAS_SYS_TYPES is set (0839f8e Nathan French) o [#122] cleanup for ssl locking changes (7d0fd5d Nathan French) o better get0_notBefore ssl defs (8ae5cdd Nathan French) o cleanup / optimization for iovec operations (874a225 Nathan French) v1.2.17 (alpha/beta) o [#111] assert frontends not compiled with -DNDEBUG (07d6f5f Nathan French) o [#111] Remove asserts for alloc functions. (#112) (114bf53 Nathan French) o [#108] do not include content-length with chunked (#113) (73255df Nathan French) o [#114] Switch to using OpenSSL's thread API for 1.0.2 (c659caa Rosen Penev) o [#119] allow out to report unescaped length (#119) (b2bc0b8 Derrick Lyndon Pallas) o [#120] fix inclusion of config.h before internal.h (517256c Nathan French) o [#114] update for older openssl compatability (c4abfa7 Nathan French) o [#114] set locking callback fix (c63269a Nathan French) o [PERF] use scatter/gather to construct HTTP reply header (c5e8dd0 Nathan French) o [PERF] use scatter/gather for htp__create_headers_ (4191716 Nathan French) o [#96] Fix cmake include dirs (b7df3fc Piotr Padlewski) o Change case of oniguruma in package_deps (b8ff92b Piotr Padlewski) o fix memory leak in evthr_free when compiled with EVHTP_THR_SHARED_PIPE (919e4ea t00416110) o cleanups / added uncrustify configurations (d008f21 Nathan French) o Feature/better thread distribution (#102) (1454b6c Nathan French) o add content-length for client requests (fb1e594 Nathan French) o add client buffer_out after headers (5c8640c Nathan French) o added changelog release script (845c193 Nathan French) o [unescape_string] do not set *out to NULL (reported by @ripulpatel) (606f3e3 Nathan French) o http access-log API (a0a8641 Nathan French) o fixed missing exported symbols for header_(new|find) (ec75536 Nathan French) o access-log API finalization / cleanups and overhauls (5f74f32 Nathan French) o evhtp_unescape_string tests (ae731f1 Nathan French) o Added test of a on_connection_finished hook (5a17742 Nathan French) o use evhtp_safe_free everywhere (cbafca7 Nathan French) o free fix for test_query.c (77f20e6 Nathan French) o [#76] keep consistent member type of struct evthr (aceb557 Nathan French) o Additions to make life easier fetching status codes + examples (f1e015d Nathan French) o Modernize CMake build (327bf14 Isaac Hier) o Minor fixes (e802dde Isaac Hier) o [#75] Update the request status in create_reply_ (9cbd30d Nathan French) o Fix target_compile_definitions syntax (8629100 Isaac Hier) o fix evhtp_set_max_body_size() not work. (da787e3 mlkt) o Fix CMake install of top-level header (52a1ccd Isaac Hier) o Fix thread header installation (678f9e2 Isaac Hier) o Check for htp == NULL before setting max_body_size (b1d0c5f Nathan French) o fix inclusion of sslutils.h into evhtp_dir_headers (f18f946 Nathan French) o [#81] use mutex_destroy on refcount.h (bb94f81 Nathan French) o Check the return of getsockname (8f3b3ec Nathan French) o Fix for eutils / fix off-by-one in htparser_get_strerror (09c07d2 Nathan French) o [#84] fix for hanging connections via resumption. (b8d5aad Nathan French) o [#84] include for older versions of libevent (182112d Nathan French) o Default to deferred callbacks / extensive resumtion logic (0dcd054 Nathan French) o [#84] Cleanup and formatting for develop merge (c728ccc Nathan French) o cbpf_scheduling (2a8b801 Nathan French) o Adding gittr to README (7ed9a42 Nathan French) o Added travis.ci gitter webhook. (5de5709 Nathan French) o [#90] evhtp_kv_free checks for just 1 then frees. Documentation updated (d9a40c0 Nathan French) o This disables the socket locality tests for now (027c518 Nathan French) o remove the -D for compile_definitions (9970980 Nathan French) o [#95] Fixes ignored return for s_body_read in parser.c (3fa62bf Nathan French) o broke out customized assertion functions (823a32f Nathan French) v1.2.16 o Added various SSL utility functions (ab190e1 Nathan French) o Fix compilation with shared libraries (6075565 Vincent Bernat) o Extensive example of streaming data without hogging memory (8f50b83 Nathan French) o Added EVHTP_FLAG_ENABLE_ALL to enable all socket flags (756a7e2 Nathan French) o only warn if setsockopt fails on EOPNOTSUPP (e5a3bdf Nathan French) o sslutil API updates [documentation / parsers / x-hdr helpers] (22207ad Nathan French) o OpenSSL 1.1.0 updates (load_*/_init/_add*) (3819073 Nathan French) o example_https_server now uses htp_sslutil_verify2opts (086afd1 Nathan French) o added example_https_client.c (933febf Nathan French) o [#69] Fix potential out of bound write to p->buf (8b68657 Nathan French) o [#72] Fix for oob read from htparser_get_strerror (75574ba Nathan French) o [#70] Do not disable EV_WRITE when pausing requests; The assumption that libevent would automatically start transferring pending data was wrong. (4cb782db77 Ultima1252) v1.2.15 o deprecated unset_hook and set_hook / cleanup (f1d2bd1 Nathan French) o updated travis configuration (45003e1 Nathan French) o Latest revision to support multiple SSL versions (4e353ba Tony Lambiris) o remove silly comment (d3da401 Nathan French) o check for errors in SSL RAND_(poll|bytes) calls (018dec8 Nathan French) o Added new virtualhost examples and functions (3467382 Nathan French) o added example request pausing app (282a1c9 Nathan French) o Added a more extensive SSL sandbox. (1e0c241 Nathan French) o Add examples/https/README (6ecf7e7 Nathan French) o exit failure in example_https when SSL is disabled (0848e08 Nathan French) o [#26] Use SSL_CTX_use_certificate_chain_file (4c4eb3a Nathan French) v1.2.14 - !!!! SECURITY UPDATE !!!! o Added doxygen tags. Updated Doxyfile to include more sources and headers. (50ab41d Dan Henderson) o SSL logging on handshake errors (0fff6bc Nathan French) o Remove built-in Oniguruma, now rely on system only (a31601c Nathan French) o FindLibevent now displays all deps (02a9e6c Nathan French) * THIS IS PRIMARILY A SECURITY UPDATE: Oniguruma (the regex library used by libevhtp) was packaged with the source; this was dumb. There were several CVE's recently published that made evhtp insecure. NOTE TO USERS: Libevhtp will no longer ship Oniguruma with the source. Instead, the build process will attempt and find a system-installed version instead. There is a big red warning if it is not found. A big thanks must go to @flokli (github) for pointing this out to me. v1.2.13 o Rename test->test_general (test is reservet target name in cmake) (d18c6d0 Vladimir Romanov) o Fix CMake files (a2fcb3d Vladimir Romanov) o Fix compilation (74537f5 Vladimir Romanov) o [#32] fix crash in evhtp_unbind_socket when evlistener fails (8149a84 Nathan French) o [#31] use CURRENT_SOURCE_DIR instead of SOURCE_DIR in cmake (aa8a989 Nathan French) o Added EVHTP_DEBUG option (with new logging) (f20e5cf Nathan French) o minor updates for pull #33 (ccf5ce4 Nathan French) o Make github stop bitching about newlines. (2c98506 Nathan French) o transparent reference counting API (6f4dc08 Nathan French) o Added some debug logs (0a79919 Nathan French) o use evhtp/onig (76ad658 Nathan French) o [#38] some extra debug logging (9db2231 Nathan French) o [#38] I'm an idiot, fix for parser_init. (7c13a57 Nathan French) o proper format str for logging in parser (de287b0 Nathan French) o Fix typo in `evthr_new()` function name (3cfeb03 Vincent Bernat) o Use evhtp/onig when system version isn't found. (a59b625 Nathan French) o fix build on SunOS (0c54892 Sebastian Wiedenroth) o Parser cleanup. (db040e9 Nathan French) o add onigposix to build dir evhtp if sys not found (7b9d1a2 Nathan French) o Ignore me, just formatting. (d911f58 Nathan French) o Update LICENSE file (new authors / dates), no other changes (12ed7e8 Nathan French) o fixes #43 (no need to check for -ldl or -lrt (bb0630c Nathan French) o Likely defect fix for issue 42. Check connection->type to ensure that it is not evhtp_type_client. Also checked for c->htp to not be null in a couple of ssl related functions. (77a0752 Dan Henderson) o Bugfixes and cleanup for htp__connection_writecb_ (440e5b9 Nathan French) o Update issues template. (960a788 Nathan French) o [Bugfix #51] htp__callback_find_ length check fix (44ddf9a Nathan French) o Better error handling for connection_ssl_new (369fe77 Nathan French) o connection_ssl_new log errors to stderr (a1f6926 Nathan French) o Add evhtp_query_free for test_query (b150ebe Nathan French) o formatting (2307737 Nathan French) o Add RPM to package generation (8479c02 Tony Lambiris) o replace cruft in evhtp_unset_all_hooks with for loop (4e62d69 Nathan French) o [#55] More flexible package generation (4c5dbe2 Nathan French) o added colors.cmake for pretty printing (bec3451 Nathan French) v1.2.12 o remove evhtp_heap, never used (d132721 Nathan French) o remove evhtp_json, never used (aec4b3c Nathan French) o [Issue#20] Include pathing (80a69cc Nathan French) o Fix deprecated usage (f4c42c7 Roman Gershman) o Create issue_template.md (8d1a95f Nathan French) o [Issue#25] Return NULL if callbacks == NULL for get_cb (cfcdba4 Nathan French) o adding more null checks. I wish __attr__(nonull) was portable (9b552b7 Nathan French) o adding some struct accessor defines to make life easier (613c5bf Nathan French) o I think I worked out most of the potential derefs (acb42cf Nathan French) o Fix potential null deref in htp__strndup_ (60b85ca Nathan French) o Fix dead assignments in parser.c (e0a48e1 Nathan French) o Eat our own dogfood (use_thread_wexit()) (ea37928 Nathan French) o No use for strlen() in a loop (6833080 Nathan French) o use PROJECT_BINARY_DIR for sys/ compat headers (012341c Nathan French) o return int for htp__path_new_ (bf59eb1 Nathan French) o more return normalization (91008a6 Nathan French) o allocation functions set *out to NULL (0305c10 Nathan French) o do not return on error from requie_uri (ddc2ce6 Nathan French) o added user-defined custom memory functions (73b54c6 Nathan French) o for some reason, htparser_init was being called manually with type_request?? (a153e5f Nathan French) o Add htp__strndup_ memfn wrapper (763168c Nathan French) o add a callback member in evhtp_ssl_cfg_t for customized decrypt privfile. fixes #16 (b3a4d42 h00360646) o Internalize some structs / deprecate evhtp_set_hook() (50ab327 Nathan French) o remove cruft (1b1a037 Nathan French) o add include directory for compat/sys headers (948c547 Nathan French) o export flag functions (3467cbb Nathan French) o formatting (4ec8dd3 Nathan French) o (evhtp_send_reply): Grab reference to bufferevent during write. (a976a2f Marcus Sundberg) o add thread exit callback for cleaning (0c7d9c4 jgli) o fix memory leak (a6b00cc jgli) o fix thread exit callback type (c8978b6 jgli) o Updates for threading functionality. (b634002 Mark Ellzey) o Added evhtp_accept_socket (a497a14 Mark Ellzey) o Forgot to export evhtp_accept_socke. (c94cb5b Mark Ellzey) o Formatting. (4a78297 Mark Ellzey) o Maybe I should spell rite. (2114210 Mark Ellzey) o Fix cmake compilation issue in centos7 (dfc8c2b kaustubh-d) o Remove double-free when SSL is used. (ee32b2a Jacob Marble) o Add CPack commands to build a debian package. (0c4a8ec Tom Pusateri) o [docs] added some doxygen groups (8a247f1 Mark Ellzey) o check res for bufferevent_socket_connect (#136) (70b68d4 mthomas) o Regression from commit 67ed0bc (c96c51e Ultima1252) o Added build/* to gitignore (c64f1dc Mark Ellzey) o Updating license to include Marcus Sundberg (801c52f Mark Ellzey) o [htparse] fix up some stuff to make coverity happy (abc7eb4 Mark Ellzey) o More coverity fixes (7d3cc52 Mark Ellzey) o check for sockopt returns (534bb48 Mark Ellzey) o master travis updates (2c6bb88 Mark Ellzey) o added testbigendian module for old cmake (fb6a866 Mark Ellzey) o Added initial evhtp_json API (6e48770 Mark Ellzey) o LICENSE update for ripping liblz json api (fb473ef Mark Ellzey) o Removing the SIGNED.md file (outdated anyway) (d4bcfa8 Mark Ellzey) o Update README (6ef073e Mark Ellzey) o updates / formatting / renames (926e355 Nathan French) o static funcs from now on will just return int (5112b6d Nathan French) o fix htp__use_threads_ call error (2ed2f7f weijiazhen) o FIX : Socket leakage on error #6 (d13b72b Nathan French) o Issue#6: make evhtp_accept_socket conform to api (d0347dc Nathan French) o Establish conformity through flags. (58da6dd Nathan French) o request flags (71341d9 Nathan French) o EVHTP_CONN flags (087e9a7 Nathan French) o Flag ops (and related functions) / cleanup (0abc96f Nathan French) o Add flags accessor (e0f04aa Nathan French) v1.2.11 o Grab reference to bufferevent during write. (a976a2f Marcus Sundberg) o add thread exit callback for cleaning (0c7d9c4 jgli) o fix memory leak (a6b00cc jgli) o fix thread exit callback type (c8978b6 jgli) o Updates for threading functionality. (b634002 Mark Ellzey) o Added evhtp_accept_socket (a497a14 Mark Ellzey) o Forgot to export evhtp_accept_socke. (c94cb5b Mark Ellzey) o Formatting. (4a78297 Mark Ellzey) o Maybe I should spell rite. (2114210 Mark Ellzey) o Fix cmake compilation issue in centos7 (dfc8c2b kaustubh-d) o Remove double-free when SSL is used. (ee32b2a Jacob Marble) o Add CPack commands to build a debian package. (0c4a8ec Tom Pusateri) o [docs] added some doxygen groups (8a247f1 Mark Ellzey) o check res for bufferevent_socket_connect (#136) (70b68d4 mthomas) o Regression from commit 67ed0bc (c96c51e Ultima1252) o Added build/* to gitignore (c64f1dc Mark Ellzey) o Updating license to include Marcus Sundberg (801c52f Mark Ellzey) o [htparse] fix up some stuff to make coverity happy (abc7eb4 Mark Ellzey) o Update README.markdown (3058a6e Mark Ellzey) o fixes and travis integration (ad92289 Mark Ellzey) o check for sockopt returns (534bb48 Mark Ellzey) o more travis updates (a9b4d38 Mark Ellzey) o travis (12a91ab Mark Ellzey) o Update README.markdown (b595a0d Mark Ellzey) o added initial lightweight heap allocator (6bf121b Mark Ellzey) o added initial lightweight heap allocator (5f634b6 Mark Ellzey) o Added initial evhtp_json API (6e48770 Mark Ellzey) o Removing the SIGNED.md file (outdated anyway) (d4bcfa8 Mark Ellzey) o updates / formatting / renames (926e355 Nathan French) o static funcs from now on will just return int (5112b6d Nathan French) o fix htp__use_threads_ call error (2ed2f7f weijiazhen) o FIX : Socket leakage on error #6 (d13b72b Nathan French) o Issue#6: make evhtp_accept_socket conform to api (d0347dc Nathan French) o Establish conformity through flags. (58da6dd Nathan French) o request flags (71341d9 Nathan French) o EVHTP_CONN flags (087e9a7 Nathan French) o Flag ops (and related functions) / cleanup (0abc96f Nathan French) o Add flags accessor (e0f04aa Nathan French) o Added missing extern "C"'s to public headers (9a0d250 Mark Ellzey) o CMakeLists cleanup, and pretty display (fc0f5da Mark Ellzey) o Oops, added back find_package() for libevent (9cdae63 Mark Ellzey) o Does image linking work here? (c10690b Mark Ellzey) o [htparse] have each state consume as needed (c34dba5 Mark Ellzey) o [htparse] formatting (4adcc67 Mark Ellzey) o Cleaning up things a bit (e7268d2 Mark Ellzey) o updated (718de1e Mark Ellzey) o Install into /include/evhtp/*.h (7065156 Mark Ellzey) o Optimizations / assertions / more safe_Free conversions (511fb19 Mark Ellzey) o Fix test_query tests (2d4c22f Mark Ellzey) o evthr initial shared pipe prototype (72f01f5 Mark Ellzey) o [evthr] shared pipe updates (0251481 Mark Ellzey) o Formatting. (f67f958 Mark Ellzey) o Added EVHTP_THR_SHARED_PIPE option (default ON) (d160452 Mark Ellzey) o formatting, fixes, assertions (b1cef41 Mark Ellzey) o Issue161 : deal with http 1.0 stuff (67ed0bc Mark Ellzey) o update test_basic (3ea2eba Mark Ellzey) o use evhtp_safe_free for authority (50dffb6 Mark Ellzey) o [htparse] keep-alive A_case fix (910137f Mark Ellzey) o sockflags, and more pipeline fixses (9b69ee7 Mark Ellzey) o Might want to make that listener nonblocking (d34a1d0 Mark Ellzey) o Coreent socket() assertion (d2263e0 Mark Ellzey) o htparse optimizations, backlog flag for test.c (9f5a38e Mark Ellzey) o scratch buffers / added test_perf for graphing (95e9ff3 Mark Ellzey) o rm'd ratelimit, added ability to use je/tc malloc (934cf5a Mark Ellzey) o Updating layouts - added lambda-pp code (8aef49d Mark Ellzey) o Cleanup time! (43005ad Mark Ellzey) o formatting (542a701 Mark Ellzey) o default response cb needs to set 0 len (2f1ecab Mark Ellzey) o use elif defined JE... (2682dca Mark Ellzey) o Added evhtp_get_cb (see full commit msg) (cdf5291 Mark Ellzey) o Various fixes / added SOVERSION'ing (73d7ee5 Mark Ellzey) o Move around tc/jemalloc ifdefs, btw tcmalloc is awful. (e456fe0 Mark Ellzey) o build: install evhtp.pc in /usr/lib/pkgconfig (0400ce0 Vincent Bernat) o jfkdlsa (fabe244 Mark Ellzey) o doxygen mod for evhtp.h (3d0e615 Mark Ellzey) o Default to EVHTP_PARSE_QUERY_FLAG_LENIENT for query parsing. (a462fc5 Mark Ellzey Thomas) o (evhtp_make_request): Add request->buffer_out as body if populated. (8bc4ab3 akalend) o Added setsockopt for ipv6 to only use ipv6 null check for ssl via akalend (acfc9dd Mark Ellzey Thomas) o modify str_to_uint64: uint64_t check can never be greater than UINT64_MAX (7d6135c xuhao) o use EVHTP_DISABLE_SSL instead of USE_SSL in header (c884191 Mark Ellzey) o export evhtp_connection_ssl_new via @rosenk, thanks! (c2168be Mark Ellzey) 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) o Fix up some compiler warnings. (37e0c4e Mark Ellzey) o Use snprintf for resp hdrs, fallback to evbuffer_add_printf (c27ac5a Mark Ellzey) o status_code_to_str no longer uses RB lookups (d143a85 Mark Ellzey) o Don't call bufferevent_set_timeouts read/write NULL (f372d9c Mark Ellzey) o add 'libdir' support (3b7e250 Christian) o Add INCLUDE_INSTALL_DIR support (19cab6f Mark Ellzey) o Add back in tree.h (0f69673 Mark Ellzey) o Fix a segfault in regex handling (d1a4240 f69m) o Formatting cleanup (c0c2055 Mark Ellzey) o No need for sys/tree include (0d3619f Mark Ellzey) v1.2.8 o There's no libdl on FreeBSD. (a683a1f Adrian Chadd) o FreeBSD handles the POSIX_C_SOURCE definition very strictly - strncmp() and a lot of other stuff isn't defined in the 2001 standards. Thus, the functions aren't declared when you include the library headers. (0354a67 Adrian Chadd) v1.2.7 o Fix double free for evthr at shutdown (a2c2826 Azat Khuzhin) o Workaround for odd recv()'s being done when not requested. (2abd591 Mark Ellzey) o Only return from BEV_EOF|READ if errno is EAGAIN (00eef42 Mark Ellzey) o Add connection-specific rate-limiting function. (32f4115 Mark Ellzey) o Fix compilation without threads support (104eafd Janusz Dziemidowicz) o Fix compilation without regex support (a71c736 Janusz Dziemidowicz) o Fix crash when unable to bind socket with TCP deferred accept (60f9d71 Janusz Dziemidowicz) o Add missing call to SSL_CTX_free() in evhtp_free(). (891d0cf Janusz Dziemidowicz) o Malloc with 1 instead of sizeof(char) to avoid warnings (de445f7 Mark Ellzey) o Don't check for bevssl if EVHTP_DISABLE_SSL enabled (9369afc Mark Ellzey) o Use SSL_OP_NO_COMPRESSION if possible (2b714c5 Janusz Dziemidowicz) o Support for DH parameters file (3d22d56 Janusz Dziemidowicz) o Use proper #ifndef for ECDH (OPENSSL_NO_ECDH instead of OPENSSL_NO_EC) (35c21c4 Janusz Dziemidowicz) o Build evhtp-config.h to make things easier to link (77552e4 Mark Ellzey) o Undef optional flags as to not screw things up (374a269 Mark Ellzey) o Disable OpenSSL support if libevent_ssl is not found (820d314 Mark Ellzey) o Clean up unused variables in onig (2d38112 Mark Ellzey) v1.2.6 o Add connection-specific rate-limiting function. (d0caf82 Mark Ellzey) o Fix double free for evthr at shutdown (a514e80 Azat Khuzhin) v1.2.5 o Port to MinGW (b0bd094 poet) o Only check for 100-continue is enabled for servers, not clients. (0cea847 Mark Ellzey) o Only decrement the thread backlog if it is a server. (ceb1e60 Mark Ellzey) o Use MSYS Makefiles instead of MinGW Makefiles (fd6ae5d poetwang) v1.2.4 o run on_headers_begin if request type is response (this happens when a 100 continue type thing happens). (c3775e0 Mark Ellzey) v1.2.3 o Added evhtp_disable_100_continue() which informs evhtp to not send a 100 continue response. (1d0eb84 Mark Ellzey) v1.2.1 o Add signal.h as reported by gnehzuil (1ac8706 Mark Ellzey) o evthr_*set_backlog for SO_RCVBUF sockopts (93f35d9 Mark Ellzey) o SO_RCVBUF should be multiple of sizeof(evthr_cmd_t) (32bd829 Mark Ellzey) o Install regex header if enabled (6d61db4 Mark Ellzey) o Fix missing symbol SIGTERM by including signal.h (c449ad8 Dennis van Dok) o Examples will only built if "make examples" is run. (2abe24f Mark Ellzey) o Fixed enum typedef issue reported by scott-- (fee4166 Mark Ellzey) o Use system onig if avail, install includes in include/evthr (56067a9 Mark Ellzey) o Added evhtp_request_status() to get client response code. (9008a0f Mark Ellzey) o Removed unused code. (a7dabaa Mark Ellzey) v1.2.0 o Initial client API (0bd23c9 Mark Ellzey) o updating test_client.c (b53f25f Mark Ellzey) o Added sample chunk callbacks for client (849bddb Mark Ellzey) o Updating test_client.c (081bcfe Mark Ellzey) o Merge from master fixups (97bf2d1 Aaron Stone) o Add test_proxy: example code for using evhtp_client functionality to implement an HTTP reverse proxy. (73fdd0c Aaron Stone) o Add evhtp_kvs_add_kvs / evhtp_headers_add_headers to copy headers from source to dest. (b0aefd4 Aaron Stone) o Update test_proxy: pause the frontend request while processing the backend request; pass headers through with evhtp_headers_add_he o Accept requests like :// That is, without an extra / after the host. Consider the path as o Enable search engine for Doxygen. (fa4c9cc Mark Ellzey) o Fixed bug in body-based query checking. (8635b2b Mark Ellzey) o use EVHTP_DISABLE_SSL instead of USE_SSL (5052107 Mark Ellzey) o Fix some generic warnings in test_proxy.c (de206a8 Mark Ellzey) o Add documentation on fallthrough state for method proto. (c1f5216 Mark Ellzey) o (htparse): Properly propagate errors from hook_on_hdrs_begin_run() (1bb641f Marcus Sundberg) o (htparse): Use correct arguments to hook_path_run(). (8aaa419 Marcus Sundberg) o (htparse): Use correct length parameter when calling hooks. (51f78e4 Marcus Sundberg) o (htparse): Handle query parameters according to RFC 3986. (0e7bdcd Marcus Sundberg) o (htparse): Properly handle abolute-URI with empty path. (657fe3b Marcus Sundberg) o (htparse): Use correct arguments to hook_port_run(). (1f60434 Marcus Sundberg) o (htparse): Handle literal IPv6 addresses in URIs. (b495e81 Marcus Sundberg) o (htparse): Fix parsing of ftp and nfs schemes. (9f53ee5 Marcus Sundberg) o (htparse): Avoid memset()ing entire parse buffer on init. (6f1b93f Marcus Sundberg) o (htparse): Add support for CONNECT and PATCH methods. (622256b Marcus Sundberg) o Changed host_ipv6_done state to host_done (93e4f0f Mark Ellzey) o Fixes for multiple requests, new htparse tests (59de92a Mark Ellzey) o evhtp_free now frees all associated callbacks. (34b4261 Mark Ellzey) o Error checking for header_val_add() (c8165a0 Mark Ellzey) o Fix memory leak on hdr_val (a3ccc1e Mark Ellzey) o Added htparser test for empty header keys. (d5c640a Mark Ellzey) o parser fixes / deal with paused responses better. (9d8e243 Mark Ellzey) o Request pausing + threading fixes. (0e774db Mark Ellzey) o Fix format warnings for test_client (680bce6 Mark Ellzey) o rm cruft that got merged in that shouldn't have. (4289914 Mark Ellzey) o Apprently rm'd some critical things, added them back in. (9052e56 Mark Ellzey) o Added -DEVHTP_BUILD_SHARED:STRING=[OFF:ON] (4da6965 Mark Ellzey) o Removed old contrib crap. (cb62ca0 Mark Ellzey) o Fixing the -DEVHTP_BUILD_SHARED:STRING=ON option to only build static-builds or dynamic-builds when set. Avoid mixing and also the o Fix for issues/69 for evthr memset() (f471298 Mark Ellzey) o Browsers can send more trailing data after the application/x-www-form-urlencoded content type, for example, which charset is used. o get_content_len now returns non-modified content_len var (bb6cdac Mark Ellzey) o Added htparser_get_content_pending for fetching bytes left to process. (28710f8 Mark Ellzey) v1.1.7 o NULL check for evhtp_kvs_for_each (37f3864 Mark Ellzey) o Parse body as the query args if content-type is url-encoded. (ee25209 Mark Ellzey) o Added a write hook. (c32c0ed Mark Ellzey) v1.1.6 o Added __cplusplus into evthr (31e3973 Mark Ellzey) o Use evhtp_hook_generic_cb function pointer prototype when setting a hook (59f01f3 Gian-Paolo Musumeci) o ISO C fixes. (d1627f7 Mark Ellzey) o ISO C fixes. (5be2662 Mark Ellzey) o Renaming evhtp_hook_generic_cb to evhtp_hook, more ISO compliance. (60b3502 Mark Ellzey) o Removing strict flags from BaseConfig. (bdc6265 Mark Ellzey) v1.1.5 o Don't treat non hex chars as errors. (d8c584d Mark Ellzey) o If non hex, replace idx-1 with % (2783983 Mark Ellzey) o Fix off-by-one in parse_query. (86881c1 Mark Ellzey) o Do not use dynamically allocated timevals in evhtp_t. (3398442 Marcus Sundberg) o Only initialize status_code_tree once. (da64395 Marcus Sundberg) o Add evhtp_free() matching evhtp_new(). (60b141a Marcus Sundberg) o (evhtp_callback_free): Free hooks as well, if set. (6fff598 Marcus Sundberg) o (evhtp_callback_free): Free regexp pointer, not only regexp. (ab9adc6 Marcus Sundberg) o (evthr_pool_stop): Do not read queue entry after freeing. (f7cc5f2 Marcus Sundberg) o Use TAILQ_FOREACH_SAFE() in evhthr_pool_free() as well. (5c2adcc Marcus Sundberg) o (evhtp_free): Stop and free thread pool, if started. (f13c207 Marcus Sundberg) o Use libevent signal handling instead of signal() in test.c (e0b68b4 Marcus Sundberg) o Properly deallocate resources in test programs. (ea1bd57 Marcus Sundberg) o Add test_vhost to .gitignore (2a596c2 Marcus Sundberg) o Fix issue where SSL is not disabled if not found. (6f25292 Mark Ellzey) o Forgot LIBEVENT_OPENSSL_LIBRARY (5a933c7 Mark Ellzey) o add upport multiline header values. (325c5c7 Mark Ellzey) v1.1.4 o Fixed potential deadlock - reported by zhangpeihao (36ee722 Mark Ellzey) o Removing conflicting code from master branch. (51feaa9 Mark Ellzey) o Assure SSL is disabled if not found. (d4f3f35 Mark Ellzey) v1.1.3 o Added evhtp_unbind_socket() (9c9b508 Mark Ellzey) o Fixed issue with query argument decoding. (8ff9cf3 Mark Ellzey) o Fix issue 53, error on LF without CR's (d51b526 Mark Ellzey) v1.1.2 o Manual verification modes for SNI vhosts. (5f9b3a5 Mark Ellzey) v1.1.1 o Added max-body functionality. (7f0052a Mark Ellzey) o Added evhtp_set_max_keepalive_requests() (503ef28 Mark Ellzey) o inherit parent settings when adding vhosts. (c66161b Mark Ellzey) o Check for NULL before request status switch/case (f943a41 Mark Ellzey) o call tlsext_servername_callback in bind_sockaddr (d1b5433 Mark Ellzey) o Swap callback finder for path+file first. (2c30003 Mark Ellzey) o Disable BEV_WRITE when htparser_run() is active (414814a Mark Ellzey) v1.1.0 o Add ability for rules to be ordered. (ad8c468 Mark Ellzey) o Cleanup the old rule code. (fbfda73 Mark Ellzey) o Fixed missing variable in callback_find (4eb5e87 Mark Ellzey) v1.0.1 o Fixes to make coverity happy. (6776d85 Mark Ellzey) o Add support for empty header values. (7ec160f Mark Ellzey) v1.0.0 o Added some function documentation. (adc3ac6 Mark Ellzey) o Check for null deref on request check. (a722fc4 Mark Ellzey) o Fix for state set override for headers. (87fb859 Mark Ellzey) o No need for null checks before free(). (7783ca2 Mark Ellzey) o Bumping this up to 1.0.0 since it's been in production for a while. v0.4.17 o Added cmake option for disabling regex. (5ab2e4b Mark Ellzey) o Cleanup compiler warnings. (770a344 Mark Ellzey) o Fixed EVHTP_DISABLE_SSL compile. (e088f2b Mark Ellzey) v0.4.16 o Use POSIX 200112 for *NIX based builds. (09dbb0f Mark Ellzey) o Drain before letting ownership change. (cc50f84 Mark Ellzey) o jfkdal (5817bd3 Mark Ellzey) o Document evhtp_set_timeouts(). (acc3ed2 Mark Ellzey) v0.4.15 o Override global timeouts on a per-connection basis. (156f5ef Mark Ellzey) o Use evhtp_connection_set_timeouts in accept. (8eb5aa9 Mark Ellzey) o Remove use for alloca.h (339f9f2 Mark Ellzey) o Added a max-backlog for evthr/pools (e8ac1f9 Mark Ellzey) o Added evthr max backlog options. (a44d1c0 Mark Ellzey) o Initial commit of vhost feature. (8fb43e0 Mark Ellzey) o SSL SNI support. (4dcc68e Mark Ellzey) o Added test_vhost.c (c5a698e Mark Ellzey) o _evhtp_ssl_servername now uses wildcard matching. (00d0488 Mark Ellzey) o Added vhost aliases functionality. (bf5766f Mark Ellzey) o Readd inc/dec counter for evthr. (022ecf3 Mark Ellzey) o Make evhtp_request_free public. (9046466 Mark Ellzey) o Added a EVHTP_RES_USER evhtp_res type. (6b41cee Mark Ellzey) o Add support for ECDH ciphers (via cfg->named_curve string) (131cae3 b) o finding cbs/hook additions, hostname parse hooks (361a068 Mark Ellzey) o Optimizations. (caddf10 Mark Ellzey) o hook_on_hdrs_complete runs before last LF (2f77428 Mark Ellzey) v0.4.14 o Added example and documentation on proper thread-safe design with evhtp. (154017e Mark Ellzey) o Added glob/wildcard callback patterns. (d33a416 Mark Ellzey) o Fixed issue with path matching. (d0648ff Mark Ellzey) v0.4.13 o Decrement pending count when defer fails. (4e99de9 Mark Ellzey) o Updating documentation to say there is not much documentation. (184f33e Mark Ellzey) v0.4.11 o Added #ifndef EVHTP_DISABLE_EVTHR flags for compilation withotu threading support (d5bcc33 Thibaut Le Guilly) o Modify evthr_lock() macro previous patch for less ifdefs. (c800fe9 Mark Ellzey) o Took out more disable thread ifdefs. (b5de21e Mark Ellzey) o Remove use of bev_flush for evhtp_send_reply_end (9f2f31f Mark Ellzey) o Modify evthr_lock() macro previous patch for less ifdefs. (aa24e6a Mark Ellzey) o Took out more disable thread ifdefs. (accbe88 Mark Ellzey) o Bugfixes, additions, cleanup, see full commit msg. (e6155d8 Mark Ellzey) o Added evhtp_set_bev_flags() (a342903 Mark Ellzey) o Prep release v0.4.12 (acda3c8 Mark Ellzey) v0.4.10 o Add compile time EVHTP_DISABLE_REGEX option. (eab0d0f Andy Hochhaus) o Use evbuffer_add in lieu of add_reference for short writes. (2df5fbd Joe Nardone) o Added take_ownership functionality which frees resources but gives the underlying bufferevent to the caller. (75050f8 Mark Ellzey) o Release v0.4.11 (5d6339f Mark Ellzey) v0.4.9 o Fixed memleak with evthr_free() (10ad15a Mark Ellzey) o Correct SSL_Shutdown() usage. (f5f97ee Mark Ellzey) o Added total bytes read function in htparser. (4a9eefb Mark Ellzey) o Added rate-limiting option in test.c (b9e10c1 Mark Ellzey) o Don't add aux headers if content-type is multipart. (6be91ca Mark Ellzey) o Removed silly compile-time flags for OSX (0dd14a9 Mark Ellzey) o Ignore deprecated ssl warnings on OSX until further notice. (0aa4fb6 Mark Ellzey) o Prep release v0.4.10 (60d0f25 Mark Ellzey) v0.4.8 o Fix for non-system strndup. (d7486b4 Mark Ellzey) o Prepping release v0.4.9 (4ef6362 Mark Ellzey) v0.4.7 o Place _evhtp_run_pre_accept() into _evhtp_connection_accept() (e45adbd Mark Ellzey) o pre_accept_cb argument changes. (c2fbb86 Mark Ellzey) o Fixed test.c for pre_accept changes. (671a911 Mark Ellzey) o testing client ssl socket error log for debugging. (265437b Mark Ellzey) o Remove some extra logging. (dd45fb8 Mark Ellzey) o SSL client error handling debugging. (5c26eaf Mark Ellzey) o err (8f5013e Mark Ellzey) o removed some dbg msgs (84bec53 Mark Ellzey) o adding error to connection on eventcb (0c9690c Mark Ellzey) o Remove debug abort. (ca8089f Mark Ellzey) o Remove debug msg. (9a41148 Mark Ellzey) o Prepping release v0.4.8 (c140e2f Mark Ellzey) v0.4.6 o Fix to be able to set a verification "mode" to the SSL_CTX_set_verify() function without having to set a custom (*verify_callback). (f3c3f37 Oscar Koeroo) o Allow SSL_CTX_set_timeout value to be passed in via config. (4f775bd Stephen Cox) o Fixed issue with _evhtp_request_parser_path with no matched callbacks where the end offset was never being set. (Reported by snnn119@gmail.com) (0d20de9 Mark Ellzey) o evhtp_parse_query() is no longer limited to 1024 byte key/val (a8179a2 Mark Ellzey) o set request status to PAUSE if evhtp_request_pause() is called manually. (4d64111 Mark Ellzey) o Prepping release v0.4.7 (12d7cc4 Mark Ellzey) v0.4.5 o Don't treat EOF eventcb flags for ssl enabled connections as errors. (49c98b1 Mark Ellzey) o Add HTTP/1.1 chunked encoding interface. (69a29d3 Andy Hochhaus) o Added test-case for chunking API usage. (939517a Mark Ellzey) o Chunk API modifications, formatting cleanup. (b805dbd Mark Ellzey) o Added SSL_CTX_set_timeout() for openssl >= 1.0 (e6fa029 Mark Ellzey) o Prepping release 0.4.6 (81c493d Mark Ellzey) v0.4.3 o Added api docs (57370c1 Mark Ellzey) o Added various accessor functions, see full commitlog (16d3fdc Mark Ellzey) o Added evhtp_request_get_connection() (db1f023 Mark Ellzey) o Moved callback locking as optional via evhtp_use_callback_locks() (7a028eb Mark Ellzey) o Removing API docs. Generate yourself! (5bc036d Mark Ellzey) o added local PF_UNIX socket listen support (f6ef167 Mark Ellzey) o Adding sys/un.h checks. (6ec70e5 Mark Ellzey) o Added evhtp_bind_sockaddr() (8f38b01 Mark Ellzey) o Add a .gitignore file (09ac468 Nick Mathewson) o Stop using the deprecated event.h; use event2/event.h instead (c60715d Nick Mathewson) o Set -fno-strict-aliasing using gcc (4a43c0b Mark Ellzey) o Picked DISABLE_SSL fixes from nmathewson into develop (b2bfb2f Mark Ellzey) o Detect non-ascii hosts and refuse to build on them (b0c2267 Nick Mathewson) o Eliminate use of ctype.h (7d0084e Nick Mathewson) o Add checks for sys/queue.h and use compat when missing (f05b54a Mark Ellzey) o compat queue.h BSD-only move. (2b49db6 Mark Ellzey) o Removed README and added changelog generator. (219f2eb Mark Ellzey) o Prepping release v0.4.4 (d335798 Mark Ellzey) o ChangeLog for v0.4.4 (c5c6d19 Mark Ellzey) o Fixing size_t printf format issues. (c75bc5b Mark Ellzey) o Added support for the -C option in the test.c The internals were there, it just covers the getops. (e3636d5 Oscar Koeroo) o The struct evhtp_connection_s has a member ssl_ctx of type evhtp_ssl_t (a typedef of an SSL*) which by name is confusing with respect to the evhtp_ssl_ctx_t (a typedef of an SSL_CTX*). The member of struct evhtp_connection_s is now rename from ssl_ctx to ssl. (ca42b4d Oscar Koeroo) o Initial markdown-based API documentation. (c16b551 Mark Ellzey) o Documentation updates. (f097558 Mark Ellzey) o Documentation updates (98c8ff0 Mark Ellzey) o Added evhtp_unescape_string() to unescape query type strings. (d75904f Mark Ellzey) o added on_headers_start hook (before header parsing, post requestline parsing). (7076b8e Mark Ellzey) o Added htparser_set_(major|minor). fixed edgecase where major/minor is not yet set. (137aa19 Mark Ellzey) o Fix C++/clang++ build. (af2a0dd Andy Hochhaus) o Added #ifndef _GNU_SOURCE before setting it again. (f8a2308 Mark Ellzey) o Changes to DISABLE to EVHTP_DISABLE, also fixed enum hook missing from last merge. (0ab23de Mark Ellzey) o inline enum's should not be static. (5efd199 Mark Ellzey) o Set libevent as a required dependency (d08f4fd Mark Ellzey) o Prepping release v0.4.5 (a3731d7 Mark Ellzey) v0.4.2 o Moving libhtparse to just htparse (bf2e43a Mark Ellzey) o Thread-safe add/remove callback additions. (d916366 Mark Ellzey) o strn* compat functions set to static (cab9503 Mark Ellzey) o Fixed issue with OPTIONS requests. (a3487fd Mark Ellzey) o arg parsing fixes (8627d0d Mark Ellzey) o Added a basic example (c06ef72 Mark Ellzey) o Added IPv6 listener support. (c1482a2 Mark Ellzey) o Prepping release v0.4.3 (952baa9 Mark Ellzey) v0.4.1 o Removing tabs from ChangeLog (3f6f220 Mark Ellzey) o Add checks for sys/tree.h and compat when missing (40c87e5 Jason L. Shiffer) o Fix strdup build warnings/errors on OSX (84e17c1 Jason L. Shiffer) o SSL Threading changes (2631e7f Mark Ellzey) o Increasing the parser stack size. (b0df5a8 Mark Ellzey) o Added chunk hooking stuff. (2bcba66 Mark Ellzey) o jfdsla (1fa36b9 Mark Ellzey) o x509 updates (089bd8a Mark Ellzey) o Revert "x509 updates" (2cc9195 Mark Ellzey) o Add checks for sys/tree.h and compat when missing (8fa7e11 Jason L. Shiffer) o Fix strdup build warnings/errors on OSX (95cbcf3 Jason L. Shiffer) o Reworked SSL Thread initialization (38aee17 Mark Ellzey) o Increased default stack limit in htparse (48f338a Mark Ellzey) o chunk hooks, proper status code strings. (dbca386 Mark Ellzey) o Moved ./libhtparse to ./htparse (44c77d3 Mark Ellzey) o Prepping release v0.4.2 (815b023 Mark Ellzey) v0.4.0 o Deal with 100 return status with responses correctly (bb86d09 Mark Ellzey) o more 100 fixes (3ef168a Mark Ellzey) o hert pup (a637672 Mark Ellzey) o updates (83abb12 Mark Ellzey) o Adding struct sockaddr to connection_t (45c30c7 Mark Ellzey) o Swapping around SSL init globals (3327f44 Mark Ellzey) o Adding -lpthread for thread enabled configuration (80fff45 Mark Ellzey) o Added include_dir for libevent pathing (4c6dbc2 Mark Ellzey) o turn off compression by default (6244108 Mark Ellzey) o Cleanup (275329a Mark Ellzey) o static again (8796e10 Mark Ellzey) o testing timeouts (ac70f3c Mark Ellzey) o fix (1aa2d79 Mark Ellzey) o more timeout tests (7f29ff6 Mark Ellzey) o SSL 1.x optimizations / timeouts (b693c7f Mark Ellzey) o Adding LICENSE (c1ba152 Mark Ellzey) o blerp (1623480 Mark Ellzey) o hrm (964aab3 Mark Ellzey) o added backlog arg to evhtp_bind_socket() (584e73e Mark Ellzey) o Adding backlog to bufferevent (c740b0c Mark Ellzey) o blerp (7c8fe61 Mark Ellzey) o htparser fix (c76e3ea Mark Ellzey) o Re-added user-set timeouts (ec246ff Mark Ellzey) o Changing static len checks with sizeof() (7c3a79f Mark Ellzey) o Fixed over/underflow condition in str_to_uint64 (c72deee Mark Ellzey) o Fixed issue with state transition into read_body (0fc1897 Mark Ellzey) o added static rt (1910c19 Mark Ellzey) o Fixed incorrect integer conversion which misses zero edge case. (c562ede Mark Ellzey) o Added correct status code string definitions. (a5bc03d Mark Ellzey) o Include RT and DL if avail when linking test. SSL needs them. (29e35fb Mark Ellzey) o default cb now returns 404 (fd4e3cb Mark Ellzey) o Prepping release v0.4.1 (246a5da Mark Ellzey) v0.3.7 o linking ChangeLog to README (4da3a26 Mark Ellzey) o updated htparse (1706b82 Mark Ellzey) o removing cruft (ac3b4f4 Mark Ellzey) o killkillkill (45d0dfa Mark Ellzey) o More logical structure (3ee2531 Mark Ellzey) o cleanup (f7e3af2 Mark Ellzey) o blerp (2833315 Mark Ellzey) o getting better (6daf3ca Mark Ellzey) o Request pipeline now functional. (99554e3 Mark Ellzey) o derpityderp (1bd1b4e Mark Ellzey) o cruft (45e5a33 Mark Ellzey) o Fixed an issue with the body parser callback (f130965 Mark Ellzey) o Major cleanup / re-factor (0752eef Mark Ellzey) o fixups (0cc0980 Mark Ellzey) o Added some more documentation (56c08d6 Mark Ellzey) o documentation updates (3715c81 Mark Ellzey) o Added better functionality, more docs - see full commit log (8a8e555 Mark Ellzey) o Added Basic reply functions (b5434de Mark Ellzey) o More updates - perf updates - bug fixes (0b050cc Mark Ellzey) o updating ssl and test.c (37191f9 Mark Ellzey) o bugfix in kv_add (3a18380 Mark Ellzey) o pausing / fixes / request and connection fini hooks (3e98351 Mark Ellzey) o fixed all the pause issues.. (658650e Mark Ellzey) o threading fixes (97f18c5 Mark Ellzey) o some optimizations (6a747c6 Mark Ellzey) o blerp (f04c39a Mark Ellzey) o Added CA Path option for ssl_cfg. (Thanks Oscar Koeroo) (8ace875 Mark Ellzey) o Added x509_verify_cb, max_verify_depth, verify_peer and store_flags option to the struct evhtp_ssl_cfg_s. And also added HTTP return code 418 (e92e882 Oscar Koeroo) o Adding dummy callbacks and values to the test.c program. (e703100 Oscar Koeroo) o max_verify_depth -> verify_depth (556d722 Mark Ellzey) o OSX Compat / fixes (e844f9a Mark Ellzey) o cleanup (f7f25ef Mark Ellzey) o docs, cleanup (022d424 Mark Ellzey) o created verify and verify depth callbacks types (instead of using void *) (8a2f6d8 Mark Ellzey) o cleanup (aee4707 Mark Ellzey) o HTTP response parsing in libhtparser (6855a08 Mark Ellzey) o fix with evthr (d4a63d8 Mark Ellzey) o Added htparser_get_status (dae487a Mark Ellzey) o fixes (7c6f5ab Mark Ellzey) o send_reply start/body/end (33bfade Mark Ellzey) o thread initialization functionality (362f902 Mark Ellzey) o Content-Length duplicate header fix (8f74408 Mark Ellzey) o Added chunk_complete and chunks_complete callback hooks (f391855 Mark Ellzey) o Added some documentation (7853017 Mark Ellzey) o fixed issue with bufferevent SSL events (a34b413 Mark Ellzey) o on_new_chunk bugfix (de39114 Mark Ellzey) o Additions for HTTP/1.1 / other additions / fixes (f865ff4 Mark Ellzey) o Fixed conditional bug (c5c4aa4 Mark Ellzey) o fixed some bugs dealing with parsing and schemes (258cbec Mark Ellzey) o SSL shutdown / bugfixes / see commit log (c850692 Mark Ellzey) o Making static (add8058 Mark Ellzey) o linking ChangeLog to README (61866b8 Mark Ellzey) o fixes, docs, features: (see full commit log) (01e61e2 Mark Ellzey) o SSL updates (see full commit log) (b3cfb24 Mark Ellzey) o bugfix in kv_add (ed5699e Mark Ellzey) o Hook macros, fixes, additions (see full commit log) (ef3fee3 Mark Ellzey) o Added CA Path option for ssl_cfg. (Thanks Oscar Koeroo) (7b79640 Mark Ellzey) o SSL verification configuration options (62c07ff Oscar Koeroo) o OSX Compatability fixes (955133d Mark Ellzey) o Created verify and verify depth callbacks types (instead of using void *) (a2a0f09 Mark Ellzey) o HTTP response parsing in libhtparser (29ac2e7 Mark Ellzey) o evthr bugfix in evthr_new() args (17aede7 Mark Ellzey) o Added htparser_get_status to libhtparse (7b16bd1 Mark Ellzey) o Added streaming reply functionality (f0a8ed3 Mark Ellzey) o thread initialization functionality (78a6863 Mark Ellzey) o Content-Length duplicate header fix (b7d283f Mark Ellzey) o Additional libevhtparse chunk-specific hooks and documentation. (c0c6e59 Mark Ellzey) o fixed issue with bufferevent SSL events (fbe3c8b Mark Ellzey) o libhtparse on_new_chunk bugfix (d352dd1 Mark Ellzey) o Additions for HTTP/1.1 / other additions / fixes (c2883eb Mark Ellzey) o Fixed conditional bug for chunked responses (b159c32 Mark Ellzey) o libhtparse fixes when dealing with requests with schema data. (efdc171 Mark Ellzey) o SSL shutdown / bugfixes / see commit log (482d0bd Mark Ellzey) o Modified so that libevhtp creates a static library instead of shared. (ba170f1 Mark Ellzey) o Rebase fix for htparser_init() (ce446b0 Mark Ellzey) o Updating for release 0.4.0 (872c243 Mark Ellzey) v0.3.6 o Matched callback hooking (5b385a6 Mark Ellzey) o Drop connection with invalid requests. (e4cd611 Mark Ellzey) o added some more requests accessors (d9cf7d5 Mark Ellzey) o added a finished hook (c04e61b Mark Ellzey) o added evhtp_request_set_cbargs() (28a22dd Mark Ellzey) o Bunch of stuff - see full commit message. (ae49ecf Mark Ellzey) o If compiling as debug, http-parser will be pre-processed then compiled. (easier to debug the shitty and unnecessary macro-based function prototypes). (c378db3 Mark Ellzey) o Mods to pass -Wextra (2b44c46 Mark Ellzey) o More -Wextra mods (f2d6c9e Mark Ellzey) o a bit broken (17d1572 Mark Ellzey) o adding gitignore (b8eba44 Mark Ellzey) o Removing dep for http_parser over to my libhtparse codebase (11b484e Mark Ellzey) o Adding libhtparse.... (dd61c20 Mark Ellzey) o Prepping release v0.3.7 (64db298 Mark Ellzey) v0.3.5 o Added various SSL information accessors (8db938e Mark Ellzey) o evhtp_hdr functions / default 404 cb / fixes (37dbe4f Mark Ellzey) o Prepping the removal of submodules (354e71d Mark Ellzey) o No more submodules (1e0ec98 Mark Ellzey) o updating release_prep (5a5e495 Mark Ellzey) o Prepping release v0.3.6 (e582c7c Mark Ellzey) o Updated ChangeLog (03a8536 Mark Ellzey) v0.3.4 o Added evhtp_set_regex_cb for matching URI with a regex. (99e8e64 Mark Ellzey) o Adding oniguruma submodule (30adea5 Mark Ellzey) o .. (2161264 Mark Ellzey) o Switched over to oniguruma for regex (b80820b Mark Ellzey) o updates (f2bb622 Mark Ellzey) o httparser updates (1befd5c Mark Ellzey) o make install rules, cleanup of dependencies (68cb269 Mark Ellzey) o cmake onig test compile (75ede66 Mark Ellzey) o Added find_callbacks_woffsets (0174055 Mark Ellzey) o evhtp_request_t is now private. (c2cadcd Mark Ellzey) o added various request accessors (6964387 Mark Ellzey) o Even more evhtp_request_t accessors. (8c45457 Mark Ellzey) o Better error / response handling. (6e869fc Mark Ellzey) o Prepping release v0.3.5 (6a84efe Mark Ellzey) v0.3.3 o SSL and other various changes (see commit log) (76dc58d Mark Ellzey) o Added ChangeLog (33bab50 Mark Ellzey) o Updated version information. (df80701 Mark Ellzey) o Added contrib section with misc patches. (0b2bb36 Mark Ellzey) o And place it in the right directory :) (e337cb7 Mark Ellzey) o Fixing up problems with the conflict resolution (6a606a4 Mark Ellzey) v0.3.2 o initial SSL support, junk is brizzoke (fbbc2fa Mark Ellzey) o cleanup (57e309c Mark Ellzey) o not working as intended, REBASE THIS JUNK (f645813 Mark Ellzey) o SSL session caching. (590e226 Mark Ellzey) o Adding a builtin cache (0e1d01e Mark Ellzey) o Added SSL thread-safe functionality. (a9db78b Mark Ellzey) o cleanup (6f71526 Mark Ellzey) o Properly expire cache entries. (65f017b Mark Ellzey) o Cleanup (edd1b44 Mark Ellzey) o Prepping v0.3.3 (67307f1 Mark Ellzey) v0.3.1 o Modifying to use bufferevents (0c4b22e Mark Ellzey) o Converting back to bevents after perf issue solved (6771104 Mark Ellzey) o Prepping release 0.3.2 (758bb16 Mark Ellzey) v0.3.0 o Optional evthr support (5392bc9 Mark Ellzey) o Prep v0.3.1 (8a6c836 Mark Ellzey) o Prep release 0.3.1 (33f1a82 Mark Ellzey) libevhtp-1.2.18/Doxyfile000066400000000000000000000123641342660753300151450ustar00rootroot00000000000000OUTPUT_DIRECTORY = ./html/docs/ DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = Libevhtp PROJECT_NUMBER = 1.2.13 CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES QT_AUTOBRIEF = YES MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 8 ALIASES = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES TYPEDEF_HIDES_STRUCT = YES EXTRACT_ALL = YES EXTRACT_PRIVATE = NO EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_DIRECTORIES = NO FILE_VERSION_FILTER = QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = INPUT = evhtp.c parser.c thread.c log.h refcount.h ./include/evhtp.h ./include/internal.h ./include/numtoa.h ./include/evhtp/evhtp.h include/evhtp/config.h include/evhtp/parser.h INPUT_ENCODING = UTF-8 RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = */.git/* .*.d EXAMPLE_PATH = EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO SOURCE_BROWSER = YES INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO REFERENCES_LINK_SOURCE = YES USE_HTAGS = NO VERBATIM_HEADERS = YES ALPHABETICAL_INDEX = NO COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project HTML_DYNAMIC_SECTIONS = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO BINARY_TOC = NO TOC_EXPAND = NO ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = GENERATE_MAN = YES MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES SEARCH_INCLUDES = YES INCLUDE_PATH = ./ ./include ./include/evhtp INCLUDE_FILE_PATTERNS = *.h PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = YES MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = YES CALLER_GRAPH = YES GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = YES DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES SEARCHENGINE = NO DISABLE_INDEX = YES GENERATE_TREEVIEW = YES libevhtp-1.2.18/LICENSE000066400000000000000000000030141342660753300144340ustar00rootroot00000000000000BSD 3-Clause License Copyright (c) 2010-2018, Mark Ellzey, Nathan French, Marcus Sundberg All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. libevhtp-1.2.18/README.markdown000066400000000000000000000137451342660753300161440ustar00rootroot00000000000000| ![LOGO](http://i.imgur.com/uBd4iIz.png) |

Libevhtp

| | :------------- | -------------: | [![Build Status](https://travis-ci.org/criticalstack/libevhtp.svg?branch=develop)](https://travis-ci.org/criticalstack/libevhtp) [![Gitter](https://badges.gitter.im/criticalstack/libevhtp.svg)](https://gitter.im/criticalstack/libevhtp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) Coverity Scan Build Status This document describes details on using the evhtp API. This document is probably not very awesome, it's best to look at test.c to see advanced usage. # Prebuilt Packages [![Package Versions](https://repology.org/badge/vertical-allrepos/libevhtp.svg)](https://repology.org/metapackage/libevhtp) ## Required Dependencies * [gcc](http://gcc.gnu.org/) * [Libevent2](http://libevent.org) ## Optional Dependencies * [OpenSSL](http://openssl.org) * pthreads * [onig (regex)](https://github.com/kkos/oniguruma) ## Building * cd build * cmake .. * make * make examples ## Overview *** Libevhtp was created as a replacement API for Libevent's current HTTP API. The reality of libevent's http interface is that it was created as a JIT server, meaning the developer never thought of it being used for creating a full-fledged HTTP service. Infact I am under the impression that the libevent http API was designed almost as an example of what you can do with libevent. It's not Apache in a box, but more and more developers are attempting to use it as so. ### Libevent's HTTP pitfalls *** * It was not designed to be a fully functional HTTP server. * The code is messy, abstractions are almost non-existent, and feature-creep has made long-term maintainability very hard. * The parsing code is slow and requires data to be buffered before a full parse can be completed. This results in extranious memory usage and lots of string comparison functions. * There is no method for a user to access various parts of the request processing cycle. For example if the "Content-Length" header has a value of 50000, your callback is not executed until all 50000 bytes have been read. * Setting callback URI's do exact matches; meaning if you set a callback for "/foo/", requests for "/foo/bar/" are ignored. * Creating an HTTPS server is hard, it requires a bunch of work to be done on the underlying bufferevents. * As far as I know, streaming data back to a client is hard, if not impossible without messing with underlying bufferevents. * It's confusing to work with, this is probably due to the lack of proper documentation. Libevhtp attempts to address these problems along with a wide variety of cool mechanisms allowing a developer to have complete control over your server operations. This is not to say the API cannot be used in a very simplistic manner - a developer can easily create a backwards compatible version of libevent's HTTP server to libevhtp. ### A bit about the architecture of libevhtp *** #### Bootstrapping 1. Create a parent evhtp_t structure. 2. Assign callbacks to the parent for specific URIs or posix-regex based URI's 3. Optionally assign per-connection hooks (see hooks) to the callbacks. 4. Optionally assign pre-accept and post-accept callbacks for incoming connections. 5. Optionally enable built-in threadpool for connection handling (lock-free, and non-blocking). 6. Optionally morph your server to HTTPS. 7. Start the evhtp listener. #### Request handling. 1. Optionally deal with pre-accept and post-accept callbacks if they exist, allowing for a connection to be rejected if the function deems it as unacceptable. 2. Optionally assign per-request hooks (see hooks) for a request (the most optimal place for setting these hooks is on a post-accept callback). 3. Deal with either per-connection or per-request hook callbacks if they exist. 4. Once the request has been fully processed, inform evhtp to send a reply. ##### A very basic example with no optional conditions. #include #include void testcb(evhtp_request_t * req, void * a) { evbuffer_add_reference(req->buffer_out, "foobar", 6, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_OK); } int main(int argc, char ** argv) { evbase_t * evbase = event_base_new(); evhtp_t * htp = evhtp_new(evbase, NULL); evhtp_set_cb(htp, "/test", testcb, NULL); evhtp_bind_socket(htp, "0.0.0.0", 8080, 1024); event_base_loop(evbase, 0); return 0; } ## Is evhtp thread-safe? For simple usage with evhtp_use_threads(), yes. But for more extreme cases: sorta, you are bound to the thread mechanisms of libevent itself. But with proper design around libevhtp, thread issues can be out-of-sight, out-of-mind. What do you mean by this "proper design" statement? Refer to the code in ./examples/thread_design.c. The comments go into great detail of the hows and whys for proper design using libevhtp's threading model. This example uses redis, mainly because most people who have asked me "is evhtp thread-safe" were attempting to *other things* before sending a response to a request. And on more than one occasion, those *other things* were communicating with redis. ## For Windows MinGW cmake -G "MSYS Makefiles" -DCMAKE_INCLUDE_PATH=/mingw/include -DCMAKE_LIBRARY_PATH=/mingw/lib -DCMAKE_INSTALL_PREFIX=/mingw . make ## Performance stuff While we never documented any benchmark publically, the popular open source project [ZIMG](http://zimg.buaa.us) did a bit of that for us.The ZIMG team decided to move away from NGINX to libevhtp for their software, and the results were pretty outstanding. Here is a graph showing their application under very high load ![ZIMG GRAPH](/zimg_vs_nginx.png) The X-axis is the number of connections, while the Y-axis is requests per second. You can read the whole article here: [Architecture Design of an Image Server](http://zimg.buaa.us/documents/Architecture_Design_of_Image_Server/) Slightly outdated (Now faster!) ![HI NGINX](http://i.imgur.com/kiSkSLH.png) libevhtp-1.2.18/build/000077500000000000000000000000001342660753300145305ustar00rootroot00000000000000libevhtp-1.2.18/build/.gitkeep000066400000000000000000000000001342660753300161470ustar00rootroot00000000000000libevhtp-1.2.18/cmake/000077500000000000000000000000001342660753300145115ustar00rootroot00000000000000libevhtp-1.2.18/cmake/Config.cmake.in000066400000000000000000000003611342660753300173250ustar00rootroot00000000000000@PACKAGE_INIT@ set(package_deps @package_deps@) foreach(dep IN LISTS package_deps) find_package(${dep} REQUIRED) endforeach() include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") check_required_components("@PROJECT_NAME@") libevhtp-1.2.18/cmake/FindLibEvent.cmake000066400000000000000000000020651342660753300200270ustar00rootroot00000000000000# - Try to find the LibEvent config processing library # Once done this will define # # LIBEVENT_FOUND - System has LibEvent # LIBEVENT_INCLUDE_DIR - the LibEvent include directory # LIBEVENT_LIBRARIES 0 The libraries needed to use LibEvent find_path (LIBEVENT_INCLUDE_DIR NAMES event.h) find_library (LIBEVENT_LIBRARY NAMES event) find_library (LIBEVENT_CORE NAMES event_core) find_library (LIBEVENT_EXTRA NAMES event_extra) if (NOT EVHTP_DISABLE_EVTHR) find_library (LIBEVENT_THREAD NAMES event_pthreads) endif() if (NOT EVHTP_DISABLE_SSL) find_library (LIBEVENT_SSL NAMES event_openssl) endif() include (FindPackageHandleStandardArgs) set (LIBEVENT_INCLUDE_DIRS ${LIBEVENT_INCLUDE_DIR}) set (LIBEVENT_LIBRARIES ${LIBEVENT_LIBRARY} ${LIBEVENT_SSL} ${LIBEVENT_CORE} ${LIBEVENT_EXTRA} ${LIBEVENT_THREAD} ${LIBEVENT_EXTRA}) find_package_handle_standard_args (LIBEVENT DEFAULT_MSG LIBEVENT_LIBRARIES LIBEVENT_INCLUDE_DIR) mark_as_advanced(LIBEVENT_INCLUDE_DIRS LIBEVENT_LIBRARIES) libevhtp-1.2.18/cmake/FindOniguruma.cmake000066400000000000000000000027271342660753300202720ustar00rootroot00000000000000# Copyright (C) 2007-2009 LuaDist. # Created by Peter Kapec # Redistribution and use of this file is allowed according to the terms of the MIT license. # For details see the COPYRIGHT file distributed with LuaDist. # Note: # Searching headers and libraries is very simple and is NOT as powerful as scripts # distributed with CMake, because LuaDist defines directories to search for. # Everyone is encouraged to contact the author with improvements. Maybe this file # becomes part of CMake distribution sometimes. # - Find oniguruma # Find the native ONIGURUMA headers and libraries. # # ONIGURUMA_INCLUDE_DIRS - where to find oniguruma.h, etc. # ONIGURUMA_LIBRARIES - List of libraries when using onig. # ONIGURUMA_FOUND - True if oniguruma found. # Look for the header file. FIND_PATH(ONIGURUMA_INCLUDE_DIR NAMES oniguruma.h) # Look for the library. FIND_LIBRARY(ONIGURUMA_LIBRARY NAMES onig) # Handle the QUIETLY and REQUIRED arguments and set ONIGURUMA_FOUND to TRUE if all listed variables are TRUE. INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(ONIGURUMA DEFAULT_MSG ONIGURUMA_LIBRARY ONIGURUMA_INCLUDE_DIR) # Copy the results to the output variables. IF(ONIGURUMA_FOUND) SET(ONIGURUMA_LIBRARIES ${ONIGURUMA_LIBRARY}) SET(ONIGURUMA_INCLUDE_DIRS ${ONIGURUMA_INCLUDE_DIR}) ELSE(ONIGURUMA_FOUND) SET(ONIGURUMA_LIBRARIES) SET(ONIGURUMA_INCLUDE_DIRS) ENDIF(ONIGURUMA_FOUND) MARK_AS_ADVANCED(ONIGURUMA_INCLUDE_DIRS ONIGURUMA_LIBRARIES) libevhtp-1.2.18/cmake/TestBigEndian.cmake000066400000000000000000000110401342660753300201670ustar00rootroot00000000000000#.rst: # TestBigEndian # ------------- # # Define macro to determine endian type # # Check if the system is big endian or little endian # # :: # # TEST_BIG_ENDIAN(VARIABLE) # VARIABLE - variable to store the result to #============================================================================= # Copyright 2002-2009 Kitware, Inc. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) macro(TEST_BIG_ENDIAN VARIABLE) if(NOT DEFINED HAVE_${VARIABLE}) message(STATUS "Check if the system is big endian") message(STATUS "Searching 16 bit integer") include(CheckTypeSize) CHECK_TYPE_SIZE("unsigned short" CMAKE_SIZEOF_UNSIGNED_SHORT) if(CMAKE_SIZEOF_UNSIGNED_SHORT EQUAL 2) message(STATUS "Using unsigned short") set(CMAKE_16BIT_TYPE "unsigned short") else() CHECK_TYPE_SIZE("unsigned int" CMAKE_SIZEOF_UNSIGNED_INT) if(CMAKE_SIZEOF_UNSIGNED_INT) message(STATUS "Using unsigned int") set(CMAKE_16BIT_TYPE "unsigned int") else() CHECK_TYPE_SIZE("unsigned long" CMAKE_SIZEOF_UNSIGNED_LONG) if(CMAKE_SIZEOF_UNSIGNED_LONG) message(STATUS "Using unsigned long") set(CMAKE_16BIT_TYPE "unsigned long") else() message(FATAL_ERROR "no suitable type found") endif() endif() endif() configure_file("${CMAKE_ROOT}/Modules/TestEndianess.c.in" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/TestEndianess.c" @ONLY) file(READ "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/TestEndianess.c" TEST_ENDIANESS_FILE_CONTENT) try_compile(HAVE_${VARIABLE} "${CMAKE_BINARY_DIR}" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/TestEndianess.c" OUTPUT_VARIABLE OUTPUT COPY_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestEndianess.bin" ) if(HAVE_${VARIABLE}) file(STRINGS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestEndianess.bin" CMAKE_TEST_ENDIANESS_STRINGS_LE LIMIT_COUNT 1 REGEX "THIS IS LITTLE ENDIAN") file(STRINGS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestEndianess.bin" CMAKE_TEST_ENDIANESS_STRINGS_BE LIMIT_COUNT 1 REGEX "THIS IS BIG ENDIAN") # on mac, if there are universal binaries built both will be true # return the result depending on the machine on which cmake runs if(CMAKE_TEST_ENDIANESS_STRINGS_BE AND CMAKE_TEST_ENDIANESS_STRINGS_LE) if(CMAKE_SYSTEM_PROCESSOR MATCHES powerpc) set(CMAKE_TEST_ENDIANESS_STRINGS_BE TRUE) set(CMAKE_TEST_ENDIANESS_STRINGS_LE FALSE) else() set(CMAKE_TEST_ENDIANESS_STRINGS_BE FALSE) set(CMAKE_TEST_ENDIANESS_STRINGS_LE TRUE) endif() message(STATUS "TEST_BIG_ENDIAN found different results, consider setting CMAKE_OSX_ARCHITECTURES or CMAKE_TRY_COMPILE_OSX_ARCHITECTURES to one or no architecture !") endif() if(CMAKE_TEST_ENDIANESS_STRINGS_LE) set(${VARIABLE} 0 CACHE INTERNAL "Result of TEST_BIG_ENDIAN" FORCE) message(STATUS "Check if the system is big endian - little endian") endif() if(CMAKE_TEST_ENDIANESS_STRINGS_BE) set(${VARIABLE} 1 CACHE INTERNAL "Result of TEST_BIG_ENDIAN" FORCE) message(STATUS "Check if the system is big endian - big endian") endif() if(NOT CMAKE_TEST_ENDIANESS_STRINGS_BE AND NOT CMAKE_TEST_ENDIANESS_STRINGS_LE) message(SEND_ERROR "TEST_BIG_ENDIAN found no result!") endif() file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "Determining if the system is big endian passed with the following output:\n${OUTPUT}\nTestEndianess.c:\n${TEST_ENDIANESS_FILE_CONTENT}\n\n") else() message(STATUS "Check if the system is big endian - failed") file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "Determining if the system is big endian failed with the following output:\n${OUTPUT}\nTestEndianess.c:\n${TEST_ENDIANESS_FILE_CONTENT}\n\n") set(${VARIABLE}) endif() endif() endmacro() libevhtp-1.2.18/cmake/colors.cmake000066400000000000000000000011331342660753300170120ustar00rootroot00000000000000if(NOT WIN32) string(ASCII 27 Esc) set(ColourReset "${Esc}[m") set(ColourBold "${Esc}[1m") set(Red "${Esc}[31m") set(Green "${Esc}[32m") set(Yellow "${Esc}[33m") set(Blue "${Esc}[34m") set(Magenta "${Esc}[35m") set(Cyan "${Esc}[36m") set(White "${Esc}[37m") set(BoldRed "${Esc}[1;31m") set(BoldGreen "${Esc}[1;32m") set(BoldYellow "${Esc}[1;33m") set(BoldBlue "${Esc}[1;34m") set(BoldMagenta "${Esc}[1;35m") set(BoldCyan "${Esc}[1;36m") set(BoldWhite "${Esc}[1;37m") endif() libevhtp-1.2.18/cmake/options.cmake000066400000000000000000000012411342660753300172040ustar00rootroot00000000000000# -DEVHTP_DISABLE_SSL=ON option (EVHTP_DISABLE_SSL "Disable ssl support" OFF) # -DEVHTP_DISABLE_EVTHR=ON option (EVHTP_DISABLE_EVTHR "Disable evthread support" OFF) # -DEVHTP_DISABLE_REGEX=ON find_package(Oniguruma) option (EVHTP_DISABLE_REGEX "Disable regex support" OFF) # -DEVHTP_DEBUG=ON option (EVHTP_DEBUG "Enable verbose debug logging" OFF) # can be overwritten by new set_alloc functions set(EVHTP_ALLOCATOR CACHE STRING "Allocator library") set_property(CACHE EVHTP_ALLOCATOR PROPERTY STRINGS "jemalloc;tcmalloc") # disable ability to wrap memory functions option (EVHTP_DISABLE_MEMFUNCTIONS "Disable custom allocators" OFF) libevhtp-1.2.18/cmake/packaging.cmake000066400000000000000000000051431342660753300174420ustar00rootroot00000000000000 # ${PROJECT_MAJOR_VERSION}.${PROJECT_MINOR_VERSION}.${PROJECT_PATCH_VERSION} set (CPACK_GENERATOR "TGZ;TZ") set (CPACK_SET_DESTDIR ON) set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Embedded Libevent based HTTP API") set (CPACK_PACKAGE_DESCRIPTION "Very fast HTTP server API, developed as a very flexible and replacement for Libevent's evhttp API. Create a multi-threaded, SSL aware, secure, HTTP server using only a minimal amount of code.") set (CPACK_PACKAGE_NAME "libevhtp") set (CPACK_PACKAGE_CONTACT "nate@cl0d.com") set (CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_MAJOR_VERSION}) set (CPACK_PACKAGE_VERSION_MINOR ${PROJECT_MINOR_VERSION}) set (CPACK_PACKAGE_VERSION_PATCH ${PROJECT_PATCH_VERSION}) if(X86) set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE "i386") set (CPACK_RPM_PACKAGE_ARCHITECTURE "i686") elseif(X86_64) set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64") set (CPACK_RPM_PACKAGE_ARCHITECTURE "x86_64") elseif(ARM) set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE "armhf") set (CPACK_RPM_PACKAGE_ARCHITECTURE "armhf") elseif(AARCH64) set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE "arm64") set (CPACK_RPM_PACKAGE_ARCHITECTURE "aarch64") elseif(PPC64LE) set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE "ppc64el") set (CPACK_RPM_PACKAGE_ARCHITECTURE "ppc64le") else() set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) set (CPACK_RPM_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) endif() if(CPACK_GENERATOR STREQUAL "DEB") set (LIBEVHTP_PACKAGE_ARCH_SUFFIX ${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}) elseif (CPACK_GENERATOR STREQUAL "RPM") set (LIBEVHTP_PACKAGE_ARCH_SUFFIX ${CPACK_RPM_PACKAGE_ARCHITECTURE}) else() set (LIBEVHTP_PACKAGE_ARCH_SUFFIX ${CMAKE_SYSTEM_PROCESSOR}) endif() set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${PROJECT_VERSION}-${LIBEVHTP_PACKAGE_ARCH_SUFFIX}") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${PROJECT_VERSION}-${LIBEVHTP_PACKAGE_ARCH_SUFFIX}") # RPM STUFF set(CPACK_RPM_COMPONENT_INSTALL TRUE) set(CPACK_RPM_PACKAGE_SUMMARY ${CPACK_PACKAGE_DESCRIPTION_SUMMARY}) set(CPACK_RPM_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION}) set(CPACK_RPM_PACKAGE_URL "http://github.com/criticalstack/libevhtp") set(CPACK_RPM_PACKAGE_LICENSE "BSD") # DEB STUFF set(CPACK_DEB_COMPONENT_INSTALL TRUE) set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") set(CPACK_DEBIAN_PACKAGE_SECTION "universe/libdevel") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://github.com/criticalstack/libevhtp") include (CPack) libevhtp-1.2.18/compat/000077500000000000000000000000001342660753300147145ustar00rootroot00000000000000libevhtp-1.2.18/compat/sys/000077500000000000000000000000001342660753300155325ustar00rootroot00000000000000libevhtp-1.2.18/compat/sys/queue.h.in000066400000000000000000000404421342660753300174400ustar00rootroot00000000000000/* $OpenBSD: queue.h,v 1.16 2000/09/07 19:47:59 art Exp $ */ /* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ #ifndef _SYS_QUEUE_H_ #define _SYS_QUEUE_H_ /* * This file defines five types of data structures: singly-linked lists, * lists, simple queues, tail queues, and circular queues. * * * A singly-linked list is headed by a single forward pointer. The elements * are singly linked for minimum space and pointer manipulation overhead at * the expense of O(n) removal for arbitrary elements. New elements can be * added to the list after an existing element or at the head of the list. * Elements being removed from the head of the list should use the explicit * macro for this purpose for optimum efficiency. A singly-linked list may * only be traversed in the forward direction. Singly-linked lists are ideal * for applications with large datasets and few or no removals or for * implementing a LIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A simple queue is headed by a pair of pointers, one the head of the * list and the other to the tail of the list. The elements are singly * linked to save space, so elements can only be removed from the * head of the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the * list. A simple queue may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * A circle queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the list. * A circle queue may be traversed in either direction, but has a more * complex end of list detection. * * For details on the use of these macros, see the queue(3) manual page. */ /* * Singly-linked List definitions. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #ifndef _WIN32 #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } #endif /* * Singly-linked List access methods. */ #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_END(head) NULL #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_FOREACH(var, head, field) \ for((var) = SLIST_FIRST(head); \ (var) != SLIST_END(head); \ (var) = SLIST_NEXT(var, field)) /* * Singly-linked List functions. */ #define SLIST_INIT(head) { \ SLIST_FIRST(head) = SLIST_END(head); \ } #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ (elm)->field.sle_next = (slistelm)->field.sle_next; \ (slistelm)->field.sle_next = (elm); \ } while (0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ (elm)->field.sle_next = (head)->slh_first; \ (head)->slh_first = (elm); \ } while (0) #define SLIST_REMOVE_HEAD(head, field) do { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (0) /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List access methods */ #define LIST_FIRST(head) ((head)->lh_first) #define LIST_END(head) NULL #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) #define LIST_NEXT(elm, field) ((elm)->field.le_next) #define LIST_FOREACH(var, head, field) \ for((var) = LIST_FIRST(head); \ (var)!= LIST_END(head); \ (var) = LIST_NEXT(var, field)) /* * List functions. */ #define LIST_INIT(head) do { \ LIST_FIRST(head) = LIST_END(head); \ } while (0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } while (0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.le_prev = (listelm)->field.le_prev; \ (elm)->field.le_next = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &(elm)->field.le_next; \ } while (0) #define LIST_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (0) #define LIST_REMOVE(elm, field) do { \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ } while (0) #define LIST_REPLACE(elm, elm2, field) do { \ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ (elm2)->field.le_next->field.le_prev = \ &(elm2)->field.le_next; \ (elm2)->field.le_prev = (elm)->field.le_prev; \ *(elm2)->field.le_prev = (elm2); \ } while (0) /* * Simple queue definitions. */ #define SIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqh_first; /* first element */ \ struct type **sqh_last; /* addr of last next element */ \ } #define SIMPLEQ_HEAD_INITIALIZER(head) \ { NULL, &(head).sqh_first } #define SIMPLEQ_ENTRY(type) \ struct { \ struct type *sqe_next; /* next element */ \ } /* * Simple queue access methods. */ #define SIMPLEQ_FIRST(head) ((head)->sqh_first) #define SIMPLEQ_END(head) NULL #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) #define SIMPLEQ_FOREACH(var, head, field) \ for((var) = SIMPLEQ_FIRST(head); \ (var) != SIMPLEQ_END(head); \ (var) = SIMPLEQ_NEXT(var, field)) /* * Simple queue functions. */ #define SIMPLEQ_INIT(head) do { \ (head)->sqh_first = NULL; \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ (head)->sqh_first = (elm); \ } while (0) #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqe_next = NULL; \ *(head)->sqh_last = (elm); \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ (head)->sqh_last = &(elm)->field.sqe_next; \ (listelm)->field.sqe_next = (elm); \ } while (0) #define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \ if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) /* * Tail queue definitions. */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ } /* * tail queue access methods */ #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_END(head) NULL #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) /* XXX */ #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_EMPTY(head) \ (TAILQ_FIRST(head) == TAILQ_END(head)) #define TAILQ_FOREACH(var, head, field) \ for((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head); \ (var) = TAILQ_NEXT(var, field)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head); \ (var) = TAILQ_PREV(var, headname, field)) /* * Tail queue functions. */ #define TAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_REMOVE(head, elm, field) do { \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ } while (0) #define TAILQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ (elm2)->field.tqe_next->field.tqe_prev = \ &(elm2)->field.tqe_next; \ else \ (head)->tqh_last = &(elm2)->field.tqe_next; \ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ *(elm2)->field.tqe_prev = (elm2); \ } while (0) /* * Circular queue definitions. */ #define CIRCLEQ_HEAD(name, type) \ struct name { \ struct type *cqh_first; /* first element */ \ struct type *cqh_last; /* last element */ \ } #define CIRCLEQ_HEAD_INITIALIZER(head) \ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } #define CIRCLEQ_ENTRY(type) \ struct { \ struct type *cqe_next; /* next element */ \ struct type *cqe_prev; /* previous element */ \ } /* * Circular queue access methods */ #define CIRCLEQ_FIRST(head) ((head)->cqh_first) #define CIRCLEQ_LAST(head) ((head)->cqh_last) #define CIRCLEQ_END(head) ((void *)(head)) #define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) #define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) #define CIRCLEQ_EMPTY(head) \ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) #define CIRCLEQ_FOREACH(var, head, field) \ for((var) = CIRCLEQ_FIRST(head); \ (var) != CIRCLEQ_END(head); \ (var) = CIRCLEQ_NEXT(var, field)) #define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ for((var) = CIRCLEQ_LAST(head); \ (var) != CIRCLEQ_END(head); \ (var) = CIRCLEQ_PREV(var, field)) /* * Circular queue functions. */ #define CIRCLEQ_INIT(head) do { \ (head)->cqh_first = CIRCLEQ_END(head); \ (head)->cqh_last = CIRCLEQ_END(head); \ } while (0) #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm)->field.cqe_next; \ (elm)->field.cqe_prev = (listelm); \ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm); \ else \ (listelm)->field.cqe_next->field.cqe_prev = (elm); \ (listelm)->field.cqe_next = (elm); \ } while (0) #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm); \ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm); \ else \ (listelm)->field.cqe_prev->field.cqe_next = (elm); \ (listelm)->field.cqe_prev = (elm); \ } while (0) #define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ (elm)->field.cqe_next = (head)->cqh_first; \ (elm)->field.cqe_prev = CIRCLEQ_END(head); \ if ((head)->cqh_last == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm); \ else \ (head)->cqh_first->field.cqe_prev = (elm); \ (head)->cqh_first = (elm); \ } while (0) #define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.cqe_next = CIRCLEQ_END(head); \ (elm)->field.cqe_prev = (head)->cqh_last; \ if ((head)->cqh_first == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm); \ else \ (head)->cqh_last->field.cqe_next = (elm); \ (head)->cqh_last = (elm); \ } while (0) #define CIRCLEQ_REMOVE(head, elm, field) do { \ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm)->field.cqe_prev; \ else \ (elm)->field.cqe_next->field.cqe_prev = \ (elm)->field.cqe_prev; \ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm)->field.cqe_next; \ else \ (elm)->field.cqe_prev->field.cqe_next = \ (elm)->field.cqe_next; \ } while (0) #define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ CIRCLEQ_END(head)) \ (head).cqh_last = (elm2); \ else \ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ CIRCLEQ_END(head)) \ (head).cqh_first = (elm2); \ else \ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ } while (0) #endif /* !_SYS_QUEUE_H_ */ libevhtp-1.2.18/compat/sys/tree.h.in000066400000000000000000002207201342660753300172520ustar00rootroot00000000000000/* * OPENBSD ORIGINAL: sys/sys/tree.h */ /* * $OpenBSD: tree.h,v 1.12 2009/03/02 09:42:55 mikeb Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _SYS_TREE_H_ #define _SYS_TREE_H_ /* * This file defines data structures for different types of trees: * splay trees and red-black trees. * * A splay tree is a self-organizing data structure. Every operation * on the tree causes a splay to happen. The splay moves the requested * node to the root of the tree and partly rebalances it. * * This has the benefit that request locality causes faster lookups as * the requested nodes move to the top of the tree. On the other hand, * every lookup causes memory writes. * * The Balance Theorem bounds the total access time for m operations * and n inserts on an initially empty tree as O((m + n)lg n). The * amortized cost for a sequence of m accesses to a splay tree is O(lg n); * * A red-black tree is a binary search tree with the node color as an * extra attribute. It fulfills a set of conditions: * - every search path from the root to a leaf consists of the * same number of black nodes, * - each red node (except for the root) has a black parent, * - each leaf node is black. * * Every operation on a red-black tree is bounded as O(lg n). * The maximum height of a red-black tree is 2lg (n+1). */ #define SPLAY_HEAD(name, type) \ struct name { \ struct type *sph_root; /* root of the tree */ \ } #define SPLAY_INITIALIZER(root) \ { NULL } #define SPLAY_INIT(root) do { \ (root)->sph_root = NULL; \ } while (0) #define SPLAY_ENTRY(type) \ struct { \ struct type *spe_left; /* left element */ \ struct type *spe_right; /* right element */ \ } #define SPLAY_LEFT(elm, field) (elm)->field.spe_left #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right #define SPLAY_ROOT(head) (head)->sph_root #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) /* * SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_LINKLEFT(head, tmp, field) do { \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ } while (0) #define SPLAY_LINKRIGHT(head, tmp, field) do { \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } while (0) #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field); \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ } while (0) /* * Generates prototypes and inline functions */ #define SPLAY_PROTOTYPE(name, type, field, cmp) \ void name ## _SPLAY(struct name *, struct type *); \ void name ## _SPLAY_MINMAX(struct name *, int); \ struct type *name ## _SPLAY_INSERT(struct name *, struct type *); \ struct type *name ## _SPLAY_REMOVE(struct name *, struct type *); \ \ /* Finds the node with the same key as elm */ \ static __inline struct type * \ name ## _SPLAY_FIND(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) { \ return (NULL); } \ name ## _SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) { \ return (head->sph_root); } \ return (NULL); \ } \ \ static __inline struct type * \ name ## _SPLAY_NEXT(struct name *head, struct type *elm) \ { \ name ## _SPLAY(head, elm); \ if (SPLAY_RIGHT(elm, field) != NULL) { \ elm = SPLAY_RIGHT(elm, field); \ while (SPLAY_LEFT(elm, field) != NULL) { \ elm = SPLAY_LEFT(elm, field); \ } \ } else{ \ elm = NULL; } \ return (elm); \ } \ \ static __inline struct type * \ name ## _SPLAY_MIN_MAX(struct name *head, int val) \ { \ name ## _SPLAY_MINMAX(head, val); \ return (SPLAY_ROOT(head)); \ } /* * Main splay operation. Moves node close to the key of elm to top */ #define SPLAY_GENERATE(name, type, field, cmp) \ struct type * \ name ## _SPLAY_INSERT(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) { \ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ } else { \ int __comp; \ name ## _SPLAY(head, elm); \ __comp = (cmp)(elm, (head)->sph_root); \ if (__comp < 0) { \ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_RIGHT(elm, field) = (head)->sph_root; \ SPLAY_LEFT((head)->sph_root, field) = NULL; \ } else if (__comp > 0) { \ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field); \ SPLAY_LEFT(elm, field) = (head)->sph_root; \ SPLAY_RIGHT((head)->sph_root, field) = NULL; \ } else{ \ return ((head)->sph_root); } \ } \ (head)->sph_root = (elm); \ return (NULL); \ } \ \ struct type * \ name ## _SPLAY_REMOVE(struct name *head, struct type *elm) \ { \ struct type *__tmp; \ if (SPLAY_EMPTY(head)) { \ return (NULL); } \ name ## _SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) { \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } else { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ name ## _SPLAY(head, elm); \ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ } \ return (elm); \ } \ return (NULL); \ } \ \ void \ name ## _SPLAY(struct name *head, struct type *elm) \ { \ struct type __node, *__left, *__right, *__tmp; \ int __comp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ __left = __right = &__node; \ \ while ((__comp = (cmp)(elm, (head)->sph_root))) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) { \ break; } \ if ((cmp)(elm, __tmp) < 0) { \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ break; } \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) { \ break; } \ if ((cmp)(elm, __tmp) > 0) { \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL) { \ break; } \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } \ \ /* Splay with either the minimum or the maximum element \ * Used to find minimum or maximum element in tree. \ */ \ void name ## _SPLAY_MINMAX(struct name *head, int __comp) \ { \ struct type __node, *__left, *__right, *__tmp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ __left = __right = &__node; \ \ while (1) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) { \ break; } \ if (__comp < 0) { \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ break; } \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) { \ break; } \ if (__comp > 0) { \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL) { \ break; } \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } #define SPLAY_NEGINF -1 #define SPLAY_INF 1 #define SPLAY_INSERT(name, x, y) name ## _SPLAY_INSERT(x, y) #define SPLAY_REMOVE(name, x, y) name ## _SPLAY_REMOVE(x, y) #define SPLAY_FIND(name, x, y) name ## _SPLAY_FIND(x, y) #define SPLAY_NEXT(name, x, y) name ## _SPLAY_NEXT(x, y) #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ : name ## _SPLAY_MIN_MAX(x, SPLAY_NEGINF)) #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ : name ## _SPLAY_MIN_MAX(x, SPLAY_INF)) #define SPLAY_FOREACH(x, name, head) \ for ((x) = SPLAY_MIN(name, head); \ (x) != NULL; \ (x) = SPLAY_NEXT(name, head, x)) /* * Macros that define a red-black tree */ #define RB_HEAD(name, type) \ struct name { \ struct type *rbh_root; /* root of the tree */ \ } #define RB_INITIALIZER(root) \ { NULL } #define RB_INIT(root) do { \ (root)->rbh_root = NULL; \ } while (0) #define RB_BLACK 0 #define RB_RED 1 #define RB_ENTRY(type) \ struct { \ struct type *rbe_left; /* left element */ \ struct type *rbe_right; /* right element */ \ struct type *rbe_parent; /* parent element */ \ int rbe_color; /* node color */ \ } #define RB_LEFT(elm, field) (elm)->field.rbe_left #define RB_RIGHT(elm, field) (elm)->field.rbe_right #define RB_PARENT(elm, field) (elm)->field.rbe_parent #define RB_COLOR(elm, field) (elm)->field.rbe_color #define RB_ROOT(head) (head)->rbh_root #define RB_EMPTY(head) (RB_ROOT(head) == NULL) #define RB_SET(elm, parent, field) do { \ RB_PARENT(elm, field) = parent; \ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ RB_COLOR(elm, field) = RB_RED; \ } while (0) #define RB_SET_BLACKRED(black, red, field) do { \ RB_COLOR(black, field) = RB_BLACK; \ RB_COLOR(red, field) = RB_RED; \ } while (0) #ifndef RB_AUGMENT #define RB_AUGMENT(x) do {} while (0) #endif #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ (tmp) = RB_RIGHT(elm, field); \ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) { \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); } \ else{ \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); } \ } else{ \ (head)->rbh_root = (tmp); } \ RB_LEFT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) { \ RB_AUGMENT(RB_PARENT(tmp, field)); } \ } while (0) #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ (tmp) = RB_LEFT(elm, field); \ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) { \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); } \ else{ \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); } \ } else{ \ (head)->rbh_root = (tmp); } \ RB_RIGHT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) { \ RB_AUGMENT(RB_PARENT(tmp, field)); } \ } while (0) /* * Generates prototypes and inline functions */ #define RB_PROTOTYPE(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, ) #define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ attr void name ## _RB_INSERT_COLOR(struct name *, struct type *); \ attr void name ## _RB_REMOVE_COLOR(struct name *, struct type *, struct type *); \ attr struct type *name ## _RB_REMOVE(struct name *, struct type *); \ attr struct type *name ## _RB_INSERT(struct name *, struct type *); \ attr struct type *name ## _RB_FIND(struct name *, struct type *); \ attr struct type *name ## _RB_NFIND(struct name *, struct type *); \ attr struct type *name ## _RB_NEXT(struct type *); \ attr struct type *name ## _RB_PREV(struct type *); \ attr struct type *name ## _RB_MINMAX(struct name *, int); \ \ /* * Main rb operation. Moves node close to the key of elm to top */ #define RB_GENERATE(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp, ) #define RB_GENERATE_STATIC(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ attr void \ name ## _RB_INSERT_COLOR(struct name *head, struct type *elm) \ { \ struct type *parent, *gparent, *tmp; \ while ((parent = RB_PARENT(elm, field)) && \ RB_COLOR(parent, field) == RB_RED) { \ gparent = RB_PARENT(parent, field); \ if (parent == RB_LEFT(gparent, field)) { \ tmp = RB_RIGHT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field); \ elm = gparent; \ continue; \ } \ if (RB_RIGHT(parent, field) == elm) { \ RB_ROTATE_LEFT(head, parent, tmp, field); \ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_RIGHT(head, gparent, tmp, field); \ } else { \ tmp = RB_LEFT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field); \ elm = gparent; \ continue; \ } \ if (RB_LEFT(parent, field) == elm) { \ RB_ROTATE_RIGHT(head, parent, tmp, field); \ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_LEFT(head, gparent, tmp, field); \ } \ } \ RB_COLOR(head->rbh_root, field) = RB_BLACK; \ } \ \ attr void \ name ## _RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ { \ struct type *tmp; \ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ elm != RB_ROOT(head)) { \ if (RB_LEFT(parent, field) == elm) { \ tmp = RB_RIGHT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_LEFT(head, parent, tmp, field); \ tmp = RB_RIGHT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) { \ struct type *oleft; \ if ((oleft = RB_LEFT(tmp, field))) { \ RB_COLOR(oleft, field) = RB_BLACK; } \ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_RIGHT(head, tmp, oleft, field); \ tmp = RB_RIGHT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field); \ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_RIGHT(tmp, field)) { \ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK; } \ RB_ROTATE_LEFT(head, parent, tmp, field); \ elm = RB_ROOT(head); \ break; \ } \ } else { \ tmp = RB_LEFT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_RIGHT(head, parent, tmp, field); \ tmp = RB_LEFT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) { \ struct type *oright; \ if ((oright = RB_RIGHT(tmp, field))) { \ RB_COLOR(oright, field) = RB_BLACK; } \ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_LEFT(head, tmp, oright, field); \ tmp = RB_LEFT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field); \ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_LEFT(tmp, field)) { \ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK; } \ RB_ROTATE_RIGHT(head, parent, tmp, field); \ elm = RB_ROOT(head); \ break; \ } \ } \ } \ if (elm) { \ RB_COLOR(elm, field) = RB_BLACK; } \ } \ \ attr struct type * \ name ## _RB_REMOVE(struct name *head, struct type *elm) \ { \ struct type *child, *parent, *old = elm; \ int color; \ if (RB_LEFT(elm, field) == NULL) { \ child = RB_RIGHT(elm, field); } \ else if (RB_RIGHT(elm, field) == NULL) { \ child = RB_LEFT(elm, field); } \ else { \ struct type *left; \ elm = RB_RIGHT(elm, field); \ while ((left = RB_LEFT(elm, field))) { \ elm = left; } \ child = RB_RIGHT(elm, field); \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) { \ RB_PARENT(child, field) = parent; } \ if (parent) { \ if (RB_LEFT(parent, field) == elm) { \ RB_LEFT(parent, field) = child; } \ else{ \ RB_RIGHT(parent, field) = child; } \ RB_AUGMENT(parent); \ } else{ \ RB_ROOT(head) = child; } \ if (RB_PARENT(elm, field) == old) { \ parent = elm; } \ (elm)->field = (old)->field; \ if (RB_PARENT(old, field)) { \ if (RB_LEFT(RB_PARENT(old, field), field) == old) { \ RB_LEFT(RB_PARENT(old, field), field) = elm; } \ else{ \ RB_RIGHT(RB_PARENT(old, field), field) = elm; } \ RB_AUGMENT(RB_PARENT(old, field)); \ } else{ \ RB_ROOT(head) = elm; } \ RB_PARENT(RB_LEFT(old, field), field) = elm; \ if (RB_RIGHT(old, field)) { \ RB_PARENT(RB_RIGHT(old, field), field) = elm; } \ if (parent) { \ left = parent; \ do { \ RB_AUGMENT(left); \ } while ((left = RB_PARENT(left, field))); \ } \ goto color; \ } \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) { \ RB_PARENT(child, field) = parent; } \ if (parent) { \ if (RB_LEFT(parent, field) == elm) { \ RB_LEFT(parent, field) = child; } \ else{ \ RB_RIGHT(parent, field) = child; } \ RB_AUGMENT(parent); \ } else{ \ RB_ROOT(head) = child; } \ color: \ if (color == RB_BLACK) { \ name ## _RB_REMOVE_COLOR(head, parent, child); } \ return (old); \ } \ \ /* Inserts a node into the RB tree */ \ attr struct type * \ name ## _RB_INSERT(struct name *head, struct type *elm) \ { \ struct type *tmp; \ struct type *parent = NULL; \ int comp = 0; \ tmp = RB_ROOT(head); \ while (tmp) { \ parent = tmp; \ comp = (cmp)(elm, parent); \ if (comp < 0) { \ tmp = RB_LEFT(tmp, field); } \ else if (comp > 0) { \ tmp = RB_RIGHT(tmp, field); } \ else{ \ return (tmp); } \ } \ RB_SET(elm, parent, field); \ if (parent != NULL) { \ if (comp < 0) { \ RB_LEFT(parent, field) = elm; } \ else{ \ RB_RIGHT(parent, field) = elm; } \ RB_AUGMENT(parent); \ } else{ \ RB_ROOT(head) = elm; } \ name ## _RB_INSERT_COLOR(head, elm); \ return (NULL); \ } \ \ /* Finds the node with the same key as elm */ \ attr struct type * \ name ## _RB_FIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) { \ tmp = RB_LEFT(tmp, field); } \ else if (comp > 0) { \ tmp = RB_RIGHT(tmp, field); } \ else{ \ return (tmp); } \ } \ return (NULL); \ } \ \ /* Finds the first node greater than or equal to the search key */ \ attr struct type * \ name ## _RB_NFIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *res = NULL; \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) { \ res = tmp; \ tmp = RB_LEFT(tmp, field); \ } \ else if (comp > 0) { \ tmp = RB_RIGHT(tmp, field); } \ else{ \ return (tmp); } \ } \ return (res); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name ## _RB_NEXT(struct type *elm) \ { \ if (RB_RIGHT(elm, field)) { \ elm = RB_RIGHT(elm, field); \ while (RB_LEFT(elm, field)) { \ elm = RB_LEFT(elm, field); } \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) { \ elm = RB_PARENT(elm, field); } \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) { \ elm = RB_PARENT(elm, field); } \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name ## _RB_PREV(struct type *elm) \ { \ if (RB_LEFT(elm, field)) { \ elm = RB_LEFT(elm, field); \ while (RB_RIGHT(elm, field)) { \ elm = RB_RIGHT(elm, field); } \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) { \ elm = RB_PARENT(elm, field); } \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) { \ elm = RB_PARENT(elm, field); } \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ attr struct type * \ name ## _RB_MINMAX(struct name *head, int val) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *parent = NULL; \ while (tmp) { \ parent = tmp; \ if (val < 0) { \ tmp = RB_LEFT(tmp, field); } \ else{ \ tmp = RB_RIGHT(tmp, field); } \ } \ return (parent); \ } #define RB_NEGINF -1 #define RB_INF 1 #define RB_INSERT(name, x, y) name ## _RB_INSERT(x, y) #define RB_REMOVE(name, x, y) name ## _RB_REMOVE(x, y) #define RB_FIND(name, x, y) name ## _RB_FIND(x, y) #define RB_NFIND(name, x, y) name ## _RB_NFIND(x, y) #define RB_NEXT(name, x, y) name ## _RB_NEXT(y) #define RB_PREV(name, x, y) name ## _RB_PREV(y) #define RB_MIN(name, x) name ## _RB_MINMAX(x, RB_NEGINF) #define RB_MAX(name, x) name ## _RB_MINMAX(x, RB_INF) #define RB_FOREACH(x, name, head) \ for ((x) = RB_MIN(name, head); \ (x) != NULL; \ (x) = name ## _RB_NEXT(x)) #define RB_FOREACH_REVERSE(x, name, head) \ for ((x) = RB_MAX(name, head); \ (x) != NULL; \ (x) = name ## _RB_PREV(x)) #endif /* _SYS_TREE_H_ */ libevhtp-1.2.18/dist/000077500000000000000000000000001342660753300143745ustar00rootroot00000000000000libevhtp-1.2.18/dist/make_changelog.sh000077500000000000000000000025661342660753300176700ustar00rootroot00000000000000#!/usr/bin/env bash old_tag= new_tag= regen=0 usage() { cat << EOF Usage: $0 [opts] OPTS: -h This help text -F Regenerate the entire ChangeLog -o Old git tag (default action is to automagically find your last tag) -n New git tag (default action is to use git-flow release to find your new tag name) EOF } while getopts "hFo:n:" OPTION do case $OPTION in h) usage exit 0 ;; F) regen=1 ;; o) old_tag=$OPTARG ;; n) new_tag=$OPTARG ;; ?) echo "No such option $OPTARG" usage exit -1 ;; esac done function generate_changelog() { old=$1 new=$2 git --no-pager log --no-merges --reverse --pretty='format: o %s (%h %an)' $old..$new echo "" echo "" } use_head=0 if [ -z $old_tag ] then l_sha=`git rev-list --tags --max-count=1..HEAD` old_tag=`git describe --tags $l_sha` fi if [ -z $new_tag ] then new_tag=`git flow release | awk '{print $2}'` use_head=1 fi echo v$new_tag if [ $use_head -eq 1 ] then generate_changelog $old_tag HEAD fi if [ $regen -eq 1 ] then tags=() for tag in `git for-each-ref --sort='*authordate' --format='%(refname)' refs/tags | awk -F/ '{print $3}'` ; do tags+=("$tag") done last_tag= for ((i=${#tags[@]-1}; i>=0; i--)); do if [ -z $last_tag ] then last_tag=${tags[$i]} continue fi echo v${tags[$i]} generate_changelog ${tags[$i]} $last_tag last_tag=${tags[$i]} done fi libevhtp-1.2.18/dist/uncrustify.cfg000066400000000000000000002662651342660753300173110ustar00rootroot00000000000000# Uncrustify-0.65_f # # General options # # The type of line endings. Default=Auto. newlines = auto # auto/lf/crlf/cr # The original size of tabs in the input. Default=8. input_tab_size = 8 # unsigned number # The size of tabs in the output (only used if align_with_tabs=true). Default=8. output_tab_size = 4 # unsigned number # The ASCII value of the string escape char, usually 92 (\) or 94 (^). (Pawn). string_escape_char = 92 # unsigned number # Alternate string escape char for Pawn. Only works right before the quote char. string_escape_char2 = 0 # unsigned number # Replace tab characters found in string literals with the escape sequence \t instead. string_replace_tab_chars = false # false/true # Allow interpreting '>=' and '>>=' as part of a template in 'void f(list>=val);'. # If True, 'assert(x<0 && y>=3)' will be broken. Default=False # Improvements to template detection may make this option obsolete. tok_split_gte = false # false/true # Override the default ' *INDENT-OFF*' in comments for disabling processing of part of the file. disable_processing_cmt = "" # string # Override the default ' *INDENT-ON*' in comments for enabling processing of part of the file. enable_processing_cmt = "" # string # Enable parsing of digraphs. Default=False. enable_digraphs = false # false/true # Control what to do with the UTF-8 BOM (recommend 'remove'). utf8_bom = ignore # ignore/add/remove/force # If the file contains bytes with values between 128 and 255, but is not UTF-8, then output as UTF-8. utf8_byte = false # false/true # Force the output encoding to UTF-8. utf8_force = false # false/true # # Spacing options # # Add or remove space around arithmetic operator '+', '-', '/', '*', etc # also '>>>' '<<' '>>' '%' '|'. sp_arith = force # ignore/add/remove/force # Add or remove space around assignment operator '=', '+=', etc. sp_assign = force # ignore/add/remove/force # Add or remove space around '=' in C++11 lambda capture specifications. Overrides sp_assign. sp_cpp_lambda_assign = ignore # ignore/add/remove/force # Add or remove space after the capture specification in C++11 lambda. sp_cpp_lambda_paren = ignore # ignore/add/remove/force # Add or remove space around assignment operator '=' in a prototype. sp_assign_default = ignore # ignore/add/remove/force # Add or remove space before assignment operator '=', '+=', etc. Overrides sp_assign. sp_before_assign = ignore # ignore/add/remove/force # Add or remove space after assignment operator '=', '+=', etc. Overrides sp_assign. sp_after_assign = ignore # ignore/add/remove/force # Add or remove space in 'NS_ENUM ('. sp_enum_paren = ignore # ignore/add/remove/force # Add or remove space around assignment '=' in enum. sp_enum_assign = ignore # ignore/add/remove/force # Add or remove space before assignment '=' in enum. Overrides sp_enum_assign. sp_enum_before_assign = ignore # ignore/add/remove/force # Add or remove space after assignment '=' in enum. Overrides sp_enum_assign. sp_enum_after_assign = ignore # ignore/add/remove/force # Add or remove space around assignment ':' in enum. sp_enum_colon = ignore # ignore/add/remove/force # Add or remove space around preprocessor '##' concatenation operator. Default=Add. sp_pp_concat = add # ignore/add/remove/force # Add or remove space after preprocessor '#' stringify operator. Also affects the '#@' charizing operator. sp_pp_stringify = ignore # ignore/add/remove/force # Add or remove space before preprocessor '#' stringify operator as in '#define x(y) L#y'. sp_before_pp_stringify = ignore # ignore/add/remove/force # Add or remove space around boolean operators '&&' and '||'. sp_bool = force # ignore/add/remove/force # Add or remove space around compare operator '<', '>', '==', etc. sp_compare = force # ignore/add/remove/force # Add or remove space inside '(' and ')'. sp_inside_paren = remove # ignore/add/remove/force # Add or remove space between nested parens: '((' vs ') )'. sp_paren_paren = remove # ignore/add/remove/force # Add or remove space between back-to-back parens: ')(' vs ') ('. sp_cparen_oparen = ignore # ignore/add/remove/force # Whether to balance spaces inside nested parens. sp_balance_nested_parens = false # false/true # Add or remove space between ')' and '{'. sp_paren_brace = force # ignore/add/remove/force # Add or remove space before pointer star '*'. sp_before_ptr_star = force # ignore/add/remove/force # Add or remove space before pointer star '*' that isn't followed by a variable name # If set to 'ignore', sp_before_ptr_star is used instead. sp_before_unnamed_ptr_star = add # ignore/add/remove/force # Add or remove space between pointer stars '*'. sp_between_ptr_star = remove # ignore/add/remove/force # Add or remove space after pointer star '*', if followed by a word. sp_after_ptr_star = force # ignore/add/remove/force # Add or remove space after pointer star '*', if followed by a qualifier. sp_after_ptr_star_qualifier = ignore # ignore/add/remove/force # Add or remove space after a pointer star '*', if followed by a func proto/def. sp_after_ptr_star_func = ignore # ignore/add/remove/force # Add or remove space after a pointer star '*', if followed by an open paren (function types). sp_ptr_star_paren = ignore # ignore/add/remove/force # Add or remove space before a pointer star '*', if followed by a func proto/def. sp_before_ptr_star_func = ignore # ignore/add/remove/force # Add or remove space before a reference sign '&'. sp_before_byref = ignore # ignore/add/remove/force # Add or remove space before a reference sign '&' that isn't followed by a variable name. # If set to 'ignore', sp_before_byref is used instead. sp_before_unnamed_byref = ignore # ignore/add/remove/force # Add or remove space after reference sign '&', if followed by a word. sp_after_byref = ignore # ignore/add/remove/force # Add or remove space after a reference sign '&', if followed by a func proto/def. sp_after_byref_func = ignore # ignore/add/remove/force # Add or remove space before a reference sign '&', if followed by a func proto/def. sp_before_byref_func = ignore # ignore/add/remove/force # Add or remove space between type and word. Default=Force. sp_after_type = force # ignore/add/remove/force # Add or remove space before the paren in the D constructs 'template Foo(' and 'class Foo('. sp_before_template_paren = ignore # ignore/add/remove/force # Add or remove space in 'template <' vs 'template<'. # If set to ignore, sp_before_angle is used. sp_template_angle = remove # ignore/add/remove/force # Add or remove space before '<>'. sp_before_angle = ignore # ignore/add/remove/force # Add or remove space inside '<' and '>'. sp_inside_angle = ignore # ignore/add/remove/force # Add or remove space after '<>'. sp_after_angle = ignore # ignore/add/remove/force # Add or remove space between '<>' and '(' as found in 'new List(foo);'. sp_angle_paren = ignore # ignore/add/remove/force # Add or remove space between '<>' and '()' as found in 'new List();'. sp_angle_paren_empty = ignore # ignore/add/remove/force # Add or remove space between '<>' and a word as in 'List m;' or 'template static ...'. sp_angle_word = ignore # ignore/add/remove/force # Add or remove space between '>' and '>' in '>>' (template stuff C++/C# only). Default=Add. sp_angle_shift = remove # ignore/add/remove/force # Permit removal of the space between '>>' in 'foo >' (C++11 only). Default=False. # sp_angle_shift cannot remove the space without this option. sp_permit_cpp11_shift = true # false/true # Add or remove space before '(' of 'if', 'for', 'switch', 'while', etc. sp_before_sparen = force # ignore/add/remove/force # Add or remove space inside if-condition '(' and ')'. sp_inside_sparen = remove # ignore/add/remove/force # Add or remove space before if-condition ')'. Overrides sp_inside_sparen. sp_inside_sparen_close = remove # ignore/add/remove/force # Add or remove space after if-condition '('. Overrides sp_inside_sparen. sp_inside_sparen_open = ignore # ignore/add/remove/force # Add or remove space after ')' of 'if', 'for', 'switch', and 'while', etc. sp_after_sparen = force # ignore/add/remove/force # Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while', etc. sp_sparen_brace = force # ignore/add/remove/force # Add or remove space between 'invariant' and '(' in the D language. sp_invariant_paren = ignore # ignore/add/remove/force # Add or remove space after the ')' in 'invariant (C) c' in the D language. sp_after_invariant_paren = ignore # ignore/add/remove/force # Add or remove space before empty statement ';' on 'if', 'for' and 'while'. sp_special_semi = ignore # ignore/add/remove/force # Add or remove space before ';'. Default=Remove. sp_before_semi = remove # ignore/add/remove/force # Add or remove space before ';' in non-empty 'for' statements. sp_before_semi_for = ignore # ignore/add/remove/force # Add or remove space before a semicolon of an empty part of a for statement. sp_before_semi_for_empty = ignore # ignore/add/remove/force # Add or remove space after ';', except when followed by a comment. Default=Add. sp_after_semi = add # ignore/add/remove/force # Add or remove space after ';' in non-empty 'for' statements. Default=Force. sp_after_semi_for = force # ignore/add/remove/force # Add or remove space after the final semicolon of an empty part of a for statement: for ( ; ; ). sp_after_semi_for_empty = ignore # ignore/add/remove/force # Add or remove space before '[' (except '[]'). sp_before_square = ignore # ignore/add/remove/force # Add or remove space before '[]'. sp_before_squares = ignore # ignore/add/remove/force # Add or remove space inside a non-empty '[' and ']'. sp_inside_square = ignore # ignore/add/remove/force # Add or remove space after ',', 'a,b' vs 'a, b'. sp_after_comma = force # ignore/add/remove/force # Add or remove space before ','. Default=Remove. sp_before_comma = remove # ignore/add/remove/force # Add or remove space between ',' and ']' in multidimensional array type 'int[,,]'. Only for C#. sp_after_mdatype_commas = ignore # ignore/add/remove/force # Add or remove space between '[' and ',' in multidimensional array type 'int[,,]'. Only for C#. sp_before_mdatype_commas = ignore # ignore/add/remove/force # Add or remove space between ',' in multidimensional array type 'int[,,]'. Only for C#. sp_between_mdatype_commas = ignore # ignore/add/remove/force # Add or remove space between an open paren and comma: '(,' vs '( ,'. Default=Force. sp_paren_comma = force # ignore/add/remove/force # Add or remove space before the variadic '...' when preceded by a non-punctuator. sp_before_ellipsis = ignore # ignore/add/remove/force # Add or remove space after class ':'. sp_after_class_colon = ignore # ignore/add/remove/force # Add or remove space before class ':'. sp_before_class_colon = ignore # ignore/add/remove/force # Add or remove space after class constructor ':'. sp_after_constr_colon = ignore # ignore/add/remove/force # Add or remove space before class constructor ':'. sp_before_constr_colon = ignore # ignore/add/remove/force # Add or remove space before case ':'. Default=Remove. sp_before_case_colon = remove # ignore/add/remove/force # Add or remove space between 'operator' and operator sign. sp_after_operator = ignore # ignore/add/remove/force # Add or remove space between the operator symbol and the open paren, as in 'operator ++('. sp_after_operator_sym = ignore # ignore/add/remove/force # Add or remove space between the operator symbol and the open paren when the operator has no arguments, as in 'operator *()'. sp_after_operator_sym_empty = ignore # ignore/add/remove/force # Add or remove space after C/D cast, i.e. 'cast(int)a' vs 'cast(int) a' or '(int)a' vs '(int) a'. sp_after_cast = remove # ignore/add/remove/force # Add or remove spaces inside cast parens. sp_inside_paren_cast = ignore # ignore/add/remove/force # Add or remove space between the type and open paren in a C++ cast, i.e. 'int(exp)' vs 'int (exp)'. sp_cpp_cast_paren = ignore # ignore/add/remove/force # Add or remove space between 'sizeof' and '('. sp_sizeof_paren = remove # ignore/add/remove/force # Add or remove space after the tag keyword (Pawn). sp_after_tag = ignore # ignore/add/remove/force # Add or remove space inside enum '{' and '}'. sp_inside_braces_enum = force # ignore/add/remove/force # Add or remove space inside struct/union '{' and '}'. sp_inside_braces_struct = force # ignore/add/remove/force # Add or remove space after open brace in an unnamed temporary direct-list-initialization. sp_after_type_brace_init_lst_open = ignore # ignore/add/remove/force # Add or remove space before close brace in an unnamed temporary direct-list-initialization. sp_before_type_brace_init_lst_close = ignore # ignore/add/remove/force # Add or remove space inside an unnamed temporary direct-list-initialization. sp_inside_type_brace_init_lst = ignore # ignore/add/remove/force # Add or remove space inside '{' and '}'. sp_inside_braces = force # ignore/add/remove/force # Add or remove space inside '{}'. sp_inside_braces_empty = ignore # ignore/add/remove/force # Add or remove space between return type and function name # A minimum of 1 is forced except for pointer return types. sp_type_func = add # ignore/add/remove/force # Add or remove space between type and open brace of an unnamed temporary direct-list-initialization. sp_type_brace_init_lst = ignore # ignore/add/remove/force # Add or remove space between function name and '(' on function declaration. sp_func_proto_paren = remove # ignore/add/remove/force # Add or remove space between function name and '()' on function declaration without parameters. sp_func_proto_paren_empty = ignore # ignore/add/remove/force # Add or remove space between function name and '(' on function definition. sp_func_def_paren = remove # ignore/add/remove/force # Add or remove space between function name and '()' on function definition without parameters. sp_func_def_paren_empty = ignore # ignore/add/remove/force # Add or remove space inside empty function '()'. sp_inside_fparens = ignore # ignore/add/remove/force # Add or remove space inside function '(' and ')'. sp_inside_fparen = ignore # ignore/add/remove/force # Add or remove space inside the first parens in the function type: 'void (*x)(...)'. sp_inside_tparen = ignore # ignore/add/remove/force # Add or remove between the parens in the function type: 'void (*x)(...)'. sp_after_tparen_close = ignore # ignore/add/remove/force # Add or remove space between ']' and '(' when part of a function call. sp_square_fparen = ignore # ignore/add/remove/force # Add or remove space between ')' and '{' of function. sp_fparen_brace = force # ignore/add/remove/force # Java: Add or remove space between ')' and '{{' of double brace initializer. sp_fparen_dbrace = ignore # ignore/add/remove/force # Add or remove space between function name and '(' on function calls. sp_func_call_paren = remove # ignore/add/remove/force # Add or remove space between function name and '()' on function calls without parameters. # If set to 'ignore' (the default), sp_func_call_paren is used. sp_func_call_paren_empty = ignore # ignore/add/remove/force # Add or remove space between the user function name and '(' on function calls # You need to set a keyword to be a user function, like this: 'set func_call_user _' in the config file. sp_func_call_user_paren = ignore # ignore/add/remove/force # Add or remove space between a constructor/destructor and the open paren. sp_func_class_paren = ignore # ignore/add/remove/force # Add or remove space between a constructor without parameters or destructor and '()'. sp_func_class_paren_empty = ignore # ignore/add/remove/force # Add or remove space between 'return' and '('. sp_return_paren = ignore # ignore/add/remove/force # Add or remove space between '__attribute__' and '('. sp_attribute_paren = ignore # ignore/add/remove/force # Add or remove space between 'defined' and '(' in '#if defined (FOO)'. sp_defined_paren = ignore # ignore/add/remove/force # Add or remove space between 'throw' and '(' in 'throw (something)'. sp_throw_paren = ignore # ignore/add/remove/force # Add or remove space between 'throw' and anything other than '(' as in '@throw [...];'. sp_after_throw = ignore # ignore/add/remove/force # Add or remove space between 'catch' and '(' in 'catch (something) { }' # If set to ignore, sp_before_sparen is used. sp_catch_paren = ignore # ignore/add/remove/force # Add or remove space between 'version' and '(' in 'version (something) { }' (D language) # If set to ignore, sp_before_sparen is used. sp_version_paren = ignore # ignore/add/remove/force # Add or remove space between 'scope' and '(' in 'scope (something) { }' (D language) # If set to ignore, sp_before_sparen is used. sp_scope_paren = ignore # ignore/add/remove/force # Add or remove space between 'super' and '(' in 'super (something)'. Default=Remove. sp_super_paren = remove # ignore/add/remove/force # Add or remove space between 'this' and '(' in 'this (something)'. Default=Remove. sp_this_paren = remove # ignore/add/remove/force # Add or remove space between macro and value. sp_macro = force # ignore/add/remove/force # Add or remove space between macro function ')' and value. sp_macro_func = ignore # ignore/add/remove/force # Add or remove space between 'else' and '{' if on the same line. sp_else_brace = add # ignore/add/remove/force # Add or remove space between '}' and 'else' if on the same line. sp_brace_else = add # ignore/add/remove/force # Add or remove space between '}' and the name of a typedef on the same line. sp_brace_typedef = ignore # ignore/add/remove/force # Add or remove space between 'catch' and '{' if on the same line. sp_catch_brace = ignore # ignore/add/remove/force # Add or remove space between '}' and 'catch' if on the same line. sp_brace_catch = ignore # ignore/add/remove/force # Add or remove space between 'finally' and '{' if on the same line. sp_finally_brace = ignore # ignore/add/remove/force # Add or remove space between '}' and 'finally' if on the same line. sp_brace_finally = ignore # ignore/add/remove/force # Add or remove space between 'try' and '{' if on the same line. sp_try_brace = ignore # ignore/add/remove/force # Add or remove space between get/set and '{' if on the same line. sp_getset_brace = ignore # ignore/add/remove/force # Add or remove space between a variable and '{' for C++ uniform initialization. Default=Add. sp_word_brace = add # ignore/add/remove/force # Add or remove space between a variable and '{' for a namespace. Default=Add. sp_word_brace_ns = add # ignore/add/remove/force # Add or remove space before the '::' operator. sp_before_dc = ignore # ignore/add/remove/force # Add or remove space after the '::' operator. sp_after_dc = ignore # ignore/add/remove/force # Add or remove around the D named array initializer ':' operator. sp_d_array_colon = ignore # ignore/add/remove/force # Add or remove space after the '!' (not) operator. Default=Remove. sp_not = remove # ignore/add/remove/force # Add or remove space after the '~' (invert) operator. Default=Remove. sp_inv = remove # ignore/add/remove/force # Add or remove space after the '&' (address-of) operator. Default=Remove # This does not affect the spacing after a '&' that is part of a type. sp_addr = remove # ignore/add/remove/force # Add or remove space around the '.' or '->' operators. Default=Remove. sp_member = remove # ignore/add/remove/force # Add or remove space after the '*' (dereference) operator. Default=Remove # This does not affect the spacing after a '*' that is part of a type. sp_deref = remove # ignore/add/remove/force # Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. Default=Remove. sp_sign = remove # ignore/add/remove/force # Add or remove space before or after '++' and '--', as in '(--x)' or 'y++;'. Default=Remove. sp_incdec = remove # ignore/add/remove/force # Add or remove space before a backslash-newline at the end of a line. Default=Add. sp_before_nl_cont = add # ignore/add/remove/force # Add or remove space after the scope '+' or '-', as in '-(void) foo;' or '+(int) bar;'. sp_after_oc_scope = ignore # ignore/add/remove/force # Add or remove space after the colon in message specs # '-(int) f:(int) x;' vs '-(int) f: (int) x;'. sp_after_oc_colon = ignore # ignore/add/remove/force # Add or remove space before the colon in message specs # '-(int) f: (int) x;' vs '-(int) f : (int) x;'. sp_before_oc_colon = ignore # ignore/add/remove/force # Add or remove space after the colon in immutable dictionary expression # 'NSDictionary *test = @{@"foo" :@"bar"};'. sp_after_oc_dict_colon = ignore # ignore/add/remove/force # Add or remove space before the colon in immutable dictionary expression # 'NSDictionary *test = @{@"foo" :@"bar"};'. sp_before_oc_dict_colon = ignore # ignore/add/remove/force # Add or remove space after the colon in message specs # '[object setValue:1];' vs '[object setValue: 1];'. sp_after_send_oc_colon = ignore # ignore/add/remove/force # Add or remove space before the colon in message specs # '[object setValue:1];' vs '[object setValue :1];'. sp_before_send_oc_colon = ignore # ignore/add/remove/force # Add or remove space after the (type) in message specs # '-(int)f: (int) x;' vs '-(int)f: (int)x;'. sp_after_oc_type = ignore # ignore/add/remove/force # Add or remove space after the first (type) in message specs # '-(int) f:(int)x;' vs '-(int)f:(int)x;'. sp_after_oc_return_type = ignore # ignore/add/remove/force # Add or remove space between '@selector' and '(' # '@selector(msgName)' vs '@selector (msgName)' # Also applies to @protocol() constructs. sp_after_oc_at_sel = ignore # ignore/add/remove/force # Add or remove space between '@selector(x)' and the following word # '@selector(foo) a:' vs '@selector(foo)a:'. sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force # Add or remove space inside '@selector' parens # '@selector(foo)' vs '@selector( foo )' # Also applies to @protocol() constructs. sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force # Add or remove space before a block pointer caret # '^int (int arg){...}' vs. ' ^int (int arg){...}'. sp_before_oc_block_caret = ignore # ignore/add/remove/force # Add or remove space after a block pointer caret # '^int (int arg){...}' vs. '^ int (int arg){...}'. sp_after_oc_block_caret = ignore # ignore/add/remove/force # Add or remove space between the receiver and selector in a message. # '[receiver selector ...]'. sp_after_oc_msg_receiver = ignore # ignore/add/remove/force # Add or remove space after @property. sp_after_oc_property = ignore # ignore/add/remove/force # Add or remove space around the ':' in 'b ? t : f'. sp_cond_colon = add # ignore/add/remove/force # Add or remove space before the ':' in 'b ? t : f'. Overrides sp_cond_colon. sp_cond_colon_before = add # ignore/add/remove/force # Add or remove space after the ':' in 'b ? t : f'. Overrides sp_cond_colon. sp_cond_colon_after = add # ignore/add/remove/force # Add or remove space around the '?' in 'b ? t : f'. sp_cond_question = add # ignore/add/remove/force # Add or remove space before the '?' in 'b ? t : f'. Overrides sp_cond_question. sp_cond_question_before = ignore # ignore/add/remove/force # Add or remove space after the '?' in 'b ? t : f'. Overrides sp_cond_question. sp_cond_question_after = ignore # ignore/add/remove/force # In the abbreviated ternary form (a ?: b), add/remove space between ? and :.'. Overrides all other sp_cond_* options. sp_cond_ternary_short = ignore # ignore/add/remove/force # Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make sense here. sp_case_label = ignore # ignore/add/remove/force # Control the space around the D '..' operator. sp_range = ignore # ignore/add/remove/force # Control the spacing after ':' in 'for (TYPE VAR : EXPR)'. Only JAVA. sp_after_for_colon = ignore # ignore/add/remove/force # Control the spacing before ':' in 'for (TYPE VAR : EXPR)'. Only JAVA. sp_before_for_colon = ignore # ignore/add/remove/force # Control the spacing in 'extern (C)' (D). sp_extern_paren = ignore # ignore/add/remove/force # Control the space after the opening of a C++ comment '// A' vs '//A'. sp_cmt_cpp_start = ignore # ignore/add/remove/force # True: If space is added with sp_cmt_cpp_start, do it after doxygen sequences like '///', '///<', '//!' and '//!<'. sp_cmt_cpp_doxygen = false # false/true # True: If space is added with sp_cmt_cpp_start, do it after Qt translator or meta-data comments like '//:', '//=', and '//~'. sp_cmt_cpp_qttr = false # false/true # Controls the spaces between #else or #endif and a trailing comment. sp_endif_cmt = ignore # ignore/add/remove/force # Controls the spaces after 'new', 'delete' and 'delete[]'. sp_after_new = ignore # ignore/add/remove/force # Controls the spaces between new and '(' in 'new()'. sp_between_new_paren = ignore # ignore/add/remove/force # Controls the spaces between ')' and 'type' in 'new(foo) BAR'. sp_after_newop_paren = ignore # ignore/add/remove/force # Controls the spaces inside paren of the new operator: 'new(foo) BAR'. sp_inside_newop_paren = ignore # ignore/add/remove/force # Controls the space after open paren of the new operator: 'new(foo) BAR'. sp_inside_newop_paren_open = ignore # ignore/add/remove/force # Controls the space before close paren of the new operator: 'new(foo) BAR'. sp_inside_newop_paren_close = ignore # ignore/add/remove/force # Controls the spaces before a trailing or embedded comment. sp_before_tr_emb_cmt = ignore # ignore/add/remove/force # Number of spaces before a trailing or embedded comment. sp_num_before_tr_emb_cmt = 0 # unsigned number # Control space between a Java annotation and the open paren. sp_annotation_paren = ignore # ignore/add/remove/force # If True, vbrace tokens are dropped to the previous token and skipped. sp_skip_vbrace_tokens = false # false/true # If True, a is inserted after #define. force_tab_after_define = false # false/true # # Indenting # # The number of columns to indent per level. # Usually 2, 3, 4, or 8. Default=8. indent_columns = 4 # unsigned number # The continuation indent. If non-zero, this overrides the indent of '(' and '=' continuation indents. # For FreeBSD, this is set to 4. Negative value is absolute and not increased for each '(' level. indent_continue = 0 # number # The continuation indent for func_*_param if they are true. # If non-zero, this overrides the indent. indent_param = 0 # unsigned number # How to use tabs when indenting code # 0=spaces only # 1=indent with tabs to brace level, align with spaces (default) # 2=indent and align with tabs, using spaces when not on a tabstop indent_with_tabs = 0 # unsigned number # Comments that are not a brace level are indented with tabs on a tabstop. # Requires indent_with_tabs=2. If false, will use spaces. indent_cmt_with_tabs = false # false/true # Whether to indent strings broken by '\' so that they line up. indent_align_string = true # false/true # The number of spaces to indent multi-line XML strings. # Requires indent_align_string=True. indent_xml_string = 0 # unsigned number # Spaces to indent '{' from level. indent_brace = 0 # unsigned number # Whether braces are indented to the body level. indent_braces = false # false/true # Disabled indenting function braces if indent_braces is True. indent_braces_no_func = false # false/true # Disabled indenting class braces if indent_braces is True. indent_braces_no_class = false # false/true # Disabled indenting struct braces if indent_braces is True. indent_braces_no_struct = false # false/true # Indent based on the size of the brace parent, i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. indent_brace_parent = false # false/true # Indent based on the paren open instead of the brace open in '({\n', default is to indent by brace. indent_paren_open_brace = false # false/true # indent a C# delegate by another level, default is to not indent by another level. indent_cs_delegate_brace = false # false/true # Whether the 'namespace' body is indented. indent_namespace = false # false/true # Only indent one namespace and no sub-namespaces. # Requires indent_namespace=True. indent_namespace_single_indent = false # false/true # The number of spaces to indent a namespace block. indent_namespace_level = 0 # unsigned number # If the body of the namespace is longer than this number, it won't be indented. # Requires indent_namespace=True. Default=0 (no limit) indent_namespace_limit = 0 # unsigned number # Whether the 'extern "C"' body is indented. indent_extern = false # false/true # Whether the 'class' body is indented. indent_class = false # false/true # Whether to indent the stuff after a leading base class colon. indent_class_colon = false # false/true # Indent based on a class colon instead of the stuff after the colon. # Requires indent_class_colon=True. Default=False. indent_class_on_colon = false # false/true # Whether to indent the stuff after a leading class initializer colon. indent_constr_colon = false # false/true # Virtual indent from the ':' for member initializers. Default=2. indent_ctor_init_leading = 2 # unsigned number # Additional indent for constructor initializer list. # Negative values decrease indent down to the first column. Default=0. indent_ctor_init = 0 # number # False=treat 'else\nif' as 'else if' for indenting purposes # True=indent the 'if' one level. indent_else_if = false # false/true # Amount to indent variable declarations after a open brace. neg=relative, pos=absolute. indent_var_def_blk = 0 # number # Indent continued variable declarations instead of aligning. indent_var_def_cont = false # false/true # Indent continued shift expressions ('<<' and '>>') instead of aligning. # Turn align_left_shift off when enabling this. indent_shift = false # false/true # True: force indentation of function definition to start in column 1 # False: use the default behavior. indent_func_def_force_col1 = false # false/true # True: indent continued function call parameters one indent level # False: align parameters under the open paren. indent_func_call_param = false # false/true # Same as indent_func_call_param, but for function defs. indent_func_def_param = false # false/true # Same as indent_func_call_param, but for function protos. indent_func_proto_param = false # false/true # Same as indent_func_call_param, but for class declarations. indent_func_class_param = false # false/true # Same as indent_func_call_param, but for class variable constructors. indent_func_ctor_var_param = true # false/true # Same as indent_func_call_param, but for templates. indent_template_param = false # false/true # Double the indent for indent_func_xxx_param options. # Use both values of the options indent_columns and indent_param. indent_func_param_double = false # false/true # Indentation column for standalone 'const' function decl/proto qualifier. indent_func_const = 0 # unsigned number # Indentation column for standalone 'throw' function decl/proto qualifier. indent_func_throw = 0 # unsigned number # The number of spaces to indent a continued '->' or '.' # Usually set to 0, 1, or indent_columns. indent_member = 0 # unsigned number # Spaces to indent single line ('//') comments on lines before code. indent_sing_line_comments = 0 # unsigned number # If set, will indent trailing single line ('//') comments relative # to the code instead of trying to keep the same absolute column. indent_relative_single_line_comments = false # false/true # Spaces to indent 'case' from 'switch' # Usually 0 or indent_columns. indent_switch_case = 4 # unsigned number # Whether to indent preproccesor statements inside of switch statements. indent_switch_pp = true # false/true # Spaces to shift the 'case' line, without affecting any other lines # Usually 0. indent_case_shift = 0 # unsigned number # Spaces to indent '{' from 'case'. # By default, the brace will appear under the 'c' in case. # Usually set to 0 or indent_columns. # negative value are OK. indent_case_brace = 0 # number # Whether to indent comments found in first column. indent_col1_comment = false # false/true # How to indent goto labels # >0: absolute column where 1 is the leftmost column # <=0: subtract from brace indent # Default=1 indent_label = 1 # number # Same as indent_label, but for access specifiers that are followed by a colon. Default=1 indent_access_spec = 1 # number # Indent the code after an access specifier by one level. # If set, this option forces 'indent_access_spec=0'. indent_access_spec_body = true # false/true # If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended). indent_paren_nl = false # false/true # Controls the indent of a close paren after a newline. # 0: Indent to body level # 1: Align under the open paren # 2: Indent to the brace level indent_paren_close = 0 # unsigned number # Controls the indent of the open paren of a function definition, if on it's own line.If True, indents the open paren indent_paren_after_func_def = false # false/true # Controls the indent of the open paren of a function declaration, if on it's own line.If True, indents the open paren indent_paren_after_func_decl = false # false/true # Controls the indent of the open paren of a function call, if on it's own line.If True, indents the open paren indent_paren_after_func_call = false # false/true # Controls the indent of a comma when inside a paren.If True, aligns under the open paren. indent_comma_paren = false # false/true # Controls the indent of a BOOL operator when inside a paren.If True, aligns under the open paren. indent_bool_paren = false # false/true # If 'indent_bool_paren' is True, controls the indent of the first expression. If True, aligns the first expression to the following ones. indent_first_bool_expr = false # false/true # If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended). indent_square_nl = false # false/true # Don't change the relative indent of ESQL/C 'EXEC SQL' bodies. indent_preserve_sql = false # false/true # Align continued statements at the '='. Default=True # If False or the '=' is followed by a newline, the next line is indent one tab. indent_align_assign = true # false/true # Indent OC blocks at brace level instead of usual rules. indent_oc_block = false # false/true # Indent OC blocks in a message relative to the parameter name. # 0=use indent_oc_block rules, 1+=spaces to indent indent_oc_block_msg = 0 # unsigned number # Minimum indent for subsequent parameters indent_oc_msg_colon = 0 # unsigned number # If True, prioritize aligning with initial colon (and stripping spaces from lines, if necessary). # Default=True. indent_oc_msg_prioritize_first_colon = true # false/true # If indent_oc_block_msg and this option are on, blocks will be indented the way that Xcode does by default (from keyword if the parameter is on its own line; otherwise, from the previous indentation level). indent_oc_block_msg_xcode_style = false # false/true # If indent_oc_block_msg and this option are on, blocks will be indented from where the brace is relative to a msg keyword. indent_oc_block_msg_from_keyword = false # false/true # If indent_oc_block_msg and this option are on, blocks will be indented from where the brace is relative to a msg colon. indent_oc_block_msg_from_colon = false # false/true # If indent_oc_block_msg and this option are on, blocks will be indented from where the block caret is. indent_oc_block_msg_from_caret = false # false/true # If indent_oc_block_msg and this option are on, blocks will be indented from where the brace is. indent_oc_block_msg_from_brace = false # false/true # When identing after virtual brace open and newline add further spaces to reach this min. indent. indent_min_vbrace_open = 0 # unsigned number # True: When identing after virtual brace open and newline add further spaces after regular indent to reach next tabstop. indent_vbrace_open_on_tabstop = false # false/true # If True, a brace followed by another token (not a newline) will indent all contained lines to match the token.Default=True. indent_token_after_brace = true # false/true # If True, cpp lambda body will be indentedDefault=False. indent_cpp_lambda_body = false # false/true # indent (or not) an using block if no braces are used. Only for C#.Default=True. indent_using_block = true # false/true # indent the continuation of ternary operator. # 0: (Default) off # 1: When the `if_false` is a continuation, indent it under `if_false` # 2: When the `:` is a continuation, indent it under `?` indent_ternary_operator = 0 # unsigned number # # Newline adding and removing options # # Whether to collapse empty blocks between '{' and '}'. nl_collapse_empty_body = false # false/true # Don't split one-line braced assignments - 'foo_t f = { 1, 2 };'. nl_assign_leave_one_liners = false # false/true # Don't split one-line braced statements inside a class xx { } body. nl_class_leave_one_liners = false # false/true # Don't split one-line enums: 'enum foo { BAR = 15 };' nl_enum_leave_one_liners = false # false/true # Don't split one-line get or set functions. nl_getset_leave_one_liners = false # false/true # Don't split one-line function definitions - 'int foo() { return 0; }'. nl_func_leave_one_liners = false # false/true # Don't split one-line C++11 lambdas - '[]() { return 0; }'. nl_cpp_lambda_leave_one_liners = false # false/true # Don't split one-line if/else statements - 'if(a) b++;'. nl_if_leave_one_liners = false # false/true # Don't split one-line while statements - 'while(a) b++;'. nl_while_leave_one_liners = false # false/true # Don't split one-line OC messages. nl_oc_msg_leave_one_liner = false # false/true # Add or remove newline between Objective-C block signature and '{'. nl_oc_block_brace = ignore # ignore/add/remove/force # Add or remove newlines at the start of the file. nl_start_of_file = ignore # ignore/add/remove/force # The number of newlines at the start of the file (only used if nl_start_of_file is 'add' or 'force'. nl_start_of_file_min = 0 # unsigned number # Add or remove newline at the end of the file. nl_end_of_file = ignore # ignore/add/remove/force # The number of newlines at the end of the file (only used if nl_end_of_file is 'add' or 'force'). nl_end_of_file_min = 0 # unsigned number # Add or remove newline between '=' and '{'. nl_assign_brace = remove # ignore/add/remove/force # Add or remove newline between '=' and '[' (D only). nl_assign_square = ignore # ignore/add/remove/force # Add or remove newline after '= [' (D only). Will also affect the newline before the ']'. nl_after_square_assign = ignore # ignore/add/remove/force # The number of blank lines after a block of variable definitions at the top of a function body # 0 = No change (default). nl_func_var_def_blk = 1 # unsigned number # The number of newlines before a block of typedefs # 0 = No change (default) # the option 'nl_after_access_spec' takes preference over 'nl_typedef_blk_start'. nl_typedef_blk_start = 0 # unsigned number # The number of newlines after a block of typedefs # 0 = No change (default). nl_typedef_blk_end = 0 # unsigned number # The maximum consecutive newlines within a block of typedefs # 0 = No change (default). nl_typedef_blk_in = 0 # unsigned number # The number of newlines before a block of variable definitions not at the top of a function body # 0 = No change (default) # the option 'nl_after_access_spec' takes preference over 'nl_var_def_blk_start'. nl_var_def_blk_start = 0 # unsigned number # The number of newlines after a block of variable definitions not at the top of a function body # 0 = No change (default). nl_var_def_blk_end = 0 # unsigned number # The maximum consecutive newlines within a block of variable definitions # 0 = No change (default). nl_var_def_blk_in = 0 # unsigned number # Add or remove newline between a function call's ')' and '{', as in: # list_for_each(item, &list) { }. ### -ellzey ### nl_fcall_brace = remove # ignore/add/remove/force # Add or remove newline between 'enum' and '{'. ### ellzey ### nl_enum_brace = remove # ignore/add/remove/force # Add or remove newline between 'enum' and 'class'. nl_enum_class = ignore # ignore/add/remove/force # Add or remove newline between 'enum class' and the identifier. nl_enum_class_identifier = ignore # ignore/add/remove/force # Add or remove newline between 'enum class' type and ':'. nl_enum_identifier_colon = ignore # ignore/add/remove/force # Add or remove newline between 'enum class identifier :' and 'type' and/or 'type'. nl_enum_colon_type = ignore # ignore/add/remove/force # Add or remove newline between 'struct and '{'. ### -ellzey nl_struct_brace = remove # ignore/add/remove/force # Add or remove newline between 'union' and '{'. ### -ellzey ### nl_union_brace = remove # ignore/add/remove/force # Add or remove newline between 'if' and '{'. #### -ellzey nl_if_brace = remove # ignore/add/remove/force # Add or remove newline between '}' and 'else'. ### -ellzey nl_brace_else = remove # ignore/add/remove/force # Add or remove newline between 'else if' and '{' # If set to ignore, nl_if_brace is used instead. nl_elseif_brace = remove # ignore/add/remove/force # Add or remove newline between 'else' and '{'. ### -ellzey nl_else_brace = remove # ignore/add/remove/force # Add or remove newline between 'else' and 'if'. ### -ellzey nl_else_if = remove # ignore/add/remove/force # Add or remove newline before 'if'/'else if' closing parenthesis. nl_before_if_closing_paren = ignore # ignore/add/remove/force # Add or remove newline between '}' and 'finally'. nl_brace_finally = ignore # ignore/add/remove/force # Add or remove newline between 'finally' and '{'. nl_finally_brace = ignore # ignore/add/remove/force # Add or remove newline between 'try' and '{'. nl_try_brace = ignore # ignore/add/remove/force # Add or remove newline between get/set and '{'. nl_getset_brace = ignore # ignore/add/remove/force # Add or remove newline between 'for' and '{'. nl_for_brace = ignore # ignore/add/remove/force # Add or remove newline between 'catch' and '{'. nl_catch_brace = ignore # ignore/add/remove/force # Add or remove newline between '}' and 'catch'. nl_brace_catch = ignore # ignore/add/remove/force # Add or remove newline between '}' and ']'. nl_brace_square = ignore # ignore/add/remove/force # Add or remove newline between '}' and ')' in a function invocation. nl_brace_fparen = ignore # ignore/add/remove/force # Add or remove newline between 'while' and '{'. nl_while_brace = ignore # ignore/add/remove/force # Add or remove newline between 'scope (x)' and '{' (D). nl_scope_brace = ignore # ignore/add/remove/force # Add or remove newline between 'unittest' and '{' (D). nl_unittest_brace = ignore # ignore/add/remove/force # Add or remove newline between 'version (x)' and '{' (D). nl_version_brace = ignore # ignore/add/remove/force # Add or remove newline between 'using' and '{'. nl_using_brace = ignore # ignore/add/remove/force # Add or remove newline between two open or close braces. # Due to general newline/brace handling, REMOVE may not work. nl_brace_brace = ignore # ignore/add/remove/force # Add or remove newline between 'do' and '{'. ### -ellzey ### nl_do_brace = remove # ignore/add/remove/force # Add or remove newline between '}' and 'while' of 'do' statement. nl_brace_while = ignore # ignore/add/remove/force # Add or remove newline between 'switch' and '{'. ### -ellzey ### nl_switch_brace = remove # ignore/add/remove/force # Add or remove newline between 'synchronized' and '{'. nl_synchronized_brace = ignore # ignore/add/remove/force # Add a newline between ')' and '{' if the ')' is on a different line than the if/for/etc. # Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch and nl_catch_brace. nl_multi_line_cond = false # false/true # Force a newline in a define after the macro name for multi-line defines. nl_multi_line_define = false # false/true # Whether to put a newline before 'case' statement, not after the first 'case'. nl_before_case = false # false/true # Add or remove newline between ')' and 'throw'. nl_before_throw = ignore # ignore/add/remove/force # Whether to put a newline after 'case' statement. nl_after_case = false # false/true # Add or remove a newline between a case ':' and '{'. Overrides nl_after_case. nl_case_colon_brace = ignore # ignore/add/remove/force # Newline between namespace and {. nl_namespace_brace = ignore # ignore/add/remove/force # Add or remove newline between 'template<>' and whatever follows. nl_template_class = ignore # ignore/add/remove/force # Add or remove newline between 'class' and '{'. nl_class_brace = ignore # ignore/add/remove/force # Add or remove newline before/after each ',' in the base class list, # (tied to pos_class_comma). nl_class_init_args = ignore # ignore/add/remove/force # Add or remove newline after each ',' in the constructor member initialization. # Related to nl_constr_colon, pos_constr_colon and pos_constr_comma. nl_constr_init_args = ignore # ignore/add/remove/force # Add or remove newline before first element, after comma, and after last element in enum. nl_enum_own_lines = ignore # ignore/add/remove/force # Add or remove newline between return type and function name in a function definition. # -ellzey nl_func_type_name = add # ignore/add/remove/force # Add or remove newline between return type and function name inside a class {} # Uses nl_func_type_name or nl_func_proto_type_name if set to ignore. nl_func_type_name_class = ignore # ignore/add/remove/force # Add or remove newline between class specification and '::' in 'void A::f() { }' # Only appears in separate member implementation (does not appear with in-line implmementation). nl_func_class_scope = ignore # ignore/add/remove/force # Add or remove newline between function scope and name # Controls the newline after '::' in 'void A::f() { }'. nl_func_scope_name = ignore # ignore/add/remove/force # Add or remove newline between return type and function name in a prototype. nl_func_proto_type_name = ignore # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' in the declaration. nl_func_paren = ignore # ignore/add/remove/force # Overrides nl_func_paren for functions with no parameters. nl_func_paren_empty = ignore # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' in the definition. nl_func_def_paren = ignore # ignore/add/remove/force # Overrides nl_func_def_paren for functions with no parameters. nl_func_def_paren_empty = ignore # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' in the call nl_func_call_paren = ignore # ignore/add/remove/force # Add or remove newline after '(' in a function declaration. nl_func_decl_start = ignore # ignore/add/remove/force # Add or remove newline after '(' in a function definition. nl_func_def_start = ignore # ignore/add/remove/force # Overrides nl_func_decl_start when there is only one parameter. nl_func_decl_start_single = ignore # ignore/add/remove/force # Overrides nl_func_def_start when there is only one parameter. nl_func_def_start_single = ignore # ignore/add/remove/force # Whether to add newline after '(' in a function declaration if '(' and ')' are in different lines. nl_func_decl_start_multi_line = false # false/true # Whether to add newline after '(' in a function definition if '(' and ')' are in different lines. nl_func_def_start_multi_line = false # false/true # Add or remove newline after each ',' in a function declaration. nl_func_decl_args = ignore # ignore/add/remove/force # Add or remove newline after each ',' in a function definition. nl_func_def_args = ignore # ignore/add/remove/force # Whether to add newline after each ',' in a function declaration if '(' and ')' are in different lines. nl_func_decl_args_multi_line = false # false/true # Whether to add newline after each ',' in a function definition if '(' and ')' are in different lines. nl_func_def_args_multi_line = false # false/true # Add or remove newline before the ')' in a function declaration. nl_func_decl_end = ignore # ignore/add/remove/force # Add or remove newline before the ')' in a function definition. nl_func_def_end = ignore # ignore/add/remove/force # Overrides nl_func_decl_end when there is only one parameter. nl_func_decl_end_single = ignore # ignore/add/remove/force # Overrides nl_func_def_end when there is only one parameter. nl_func_def_end_single = ignore # ignore/add/remove/force # Whether to add newline before ')' in a function declaration if '(' and ')' are in different lines. nl_func_decl_end_multi_line = false # false/true # Whether to add newline before ')' in a function definition if '(' and ')' are in different lines. nl_func_def_end_multi_line = false # false/true # Add or remove newline between '()' in a function declaration. nl_func_decl_empty = ignore # ignore/add/remove/force # Add or remove newline between '()' in a function definition. nl_func_def_empty = ignore # ignore/add/remove/force # Whether to add newline after '(' in a function call if '(' and ')' are in different lines. nl_func_call_start_multi_line = false # false/true # Whether to add newline after each ',' in a function call if '(' and ')' are in different lines. nl_func_call_args_multi_line = false # false/true # Whether to add newline before ')' in a function call if '(' and ')' are in different lines. nl_func_call_end_multi_line = false # false/true # Whether to put each OC message parameter on a separate line # See nl_oc_msg_leave_one_liner. nl_oc_msg_args = false # false/true # Add or remove newline between function signature and '{'. # -ellzey nl_fdef_brace = add # ignore/add/remove/force # Add or remove newline between C++11 lambda signature and '{'. nl_cpp_ldef_brace = ignore # ignore/add/remove/force # Add or remove a newline between the return keyword and return expression. nl_return_expr = ignore # ignore/add/remove/force # Whether to put a newline after semicolons, except in 'for' statements. nl_after_semicolon = false # false/true # Java: Control the newline between the ')' and '{{' of the double brace initializer. nl_paren_dbrace_open = ignore # ignore/add/remove/force # Whether to put a newline after the type in an unnamed temporary direct-list-initialization. nl_type_brace_init_lst = ignore # ignore/add/remove/force # Whether to put a newline after open brace in an unnamed temporary direct-list-initialization. nl_type_brace_init_lst_open = ignore # ignore/add/remove/force # Whether to put a newline before close brace in an unnamed temporary direct-list-initialization. nl_type_brace_init_lst_close = ignore # ignore/add/remove/force # Whether to put a newline after brace open. # This also adds a newline before the matching brace close. nl_after_brace_open = false # false/true # If nl_after_brace_open and nl_after_brace_open_cmt are True, a newline is # placed between the open brace and a trailing single-line comment. nl_after_brace_open_cmt = false # false/true # Whether to put a newline after a virtual brace open with a non-empty body. # These occur in un-braced if/while/do/for statement bodies. nl_after_vbrace_open = false # false/true # Whether to put a newline after a virtual brace open with an empty body. # These occur in un-braced if/while/do/for statement bodies. nl_after_vbrace_open_empty = false # false/true # Whether to put a newline after a brace close. # Does not apply if followed by a necessary ';'. nl_after_brace_close = false # false/true # Whether to put a newline after a virtual brace close. # Would add a newline before return in: 'if (foo) a++; return;'. nl_after_vbrace_close = false # false/true # Control the newline between the close brace and 'b' in: 'struct { int a; } b;' # Affects enums, unions and structures. If set to ignore, uses nl_after_brace_close. nl_brace_struct_var = ignore # ignore/add/remove/force # Whether to alter newlines in '#define' macros. nl_define_macro = true # false/true # Whether to remove blanks after '#ifxx' and '#elxx', or before '#elxx' and '#endif'. Does not affect top-level #ifdefs. nl_squeeze_ifdef = false # false/true # Makes the nl_squeeze_ifdef option affect the top-level #ifdefs as well. nl_squeeze_ifdef_top_level = false # false/true # Add or remove blank line before 'if'. nl_before_if = ignore # ignore/add/remove/force # Add or remove blank line after 'if' statement. # Add/Force work only if the next token is not a closing brace. # -ellzey nl_after_if = add # ignore/add/remove/force # Add or remove blank line before 'for'. nl_before_for = ignore # ignore/add/remove/force # Add or remove blank line after 'for' statement. nl_after_for = ignore # ignore/add/remove/force # Add or remove blank line before 'while'. nl_before_while = ignore # ignore/add/remove/force # Add or remove blank line after 'while' statement. nl_after_while = ignore # ignore/add/remove/force # Add or remove blank line before 'switch'. nl_before_switch = ignore # ignore/add/remove/force # Add or remove blank line after 'switch' statement. nl_after_switch = ignore # ignore/add/remove/force # Add or remove blank line before 'synchronized'. nl_before_synchronized = ignore # ignore/add/remove/force # Add or remove blank line after 'synchronized' statement. nl_after_synchronized = ignore # ignore/add/remove/force # Add or remove blank line before 'do'. nl_before_do = ignore # ignore/add/remove/force # Add or remove blank line after 'do/while' statement. nl_after_do = ignore # ignore/add/remove/force # Whether to double-space commented-entries in struct/union/enum. nl_ds_struct_enum_cmt = false # false/true # force nl before } of a struct/union/enum # (lower priority than 'eat_blanks_before_close_brace'). nl_ds_struct_enum_close_brace = false # false/true # Add or remove blank line before 'func_class_def'. nl_before_func_class_def = 0 # unsigned number # Add or remove blank line before 'func_class_proto'. nl_before_func_class_proto = 0 # unsigned number # Add or remove a newline before/after a class colon, # (tied to pos_class_colon). nl_class_colon = ignore # ignore/add/remove/force # Add or remove a newline around a class constructor colon. # Related to nl_constr_init_args, pos_constr_colon and pos_constr_comma. nl_constr_colon = ignore # ignore/add/remove/force # Change simple unbraced if statements into a one-liner # 'if(b)\n i++;' => 'if(b) i++;'. nl_create_if_one_liner = false # false/true # Change simple unbraced for statements into a one-liner # 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);'. nl_create_for_one_liner = false # false/true # Change simple unbraced while statements into a one-liner # 'while (i<5)\n foo(i++);' => 'while (i<5) foo(i++);'. nl_create_while_one_liner = false # false/true # Change a one-liner if statement into simple unbraced if # 'if(b) i++;' => 'if(b)\n i++;'. nl_split_if_one_liner = false # false/true # Change a one-liner for statement into simple unbraced for # 'for (i=0;<5;i++) foo(i);' => 'for (i=0;<5;i++)\n foo(i);'. nl_split_for_one_liner = false # false/true # Change a one-liner while statement into simple unbraced while # 'while (i<5) foo(i++);' => 'while (i<5)\n foo(i++);'. nl_split_while_one_liner = false # false/true # # Blank line options # # The maximum consecutive newlines (3 = 2 blank lines). nl_max = 0 # unsigned number # The maximum consecutive newlines in function. nl_max_blank_in_func = 0 # unsigned number # The number of newlines after a function prototype, if followed by another function prototype. nl_after_func_proto = 0 # unsigned number # The number of newlines after a function prototype, if not followed by another function prototype. nl_after_func_proto_group = 0 # unsigned number # The number of newlines after a function class prototype, if followed by another function class prototype. nl_after_func_class_proto = 0 # unsigned number # The number of newlines after a function class prototype, if not followed by another function class prototype. nl_after_func_class_proto_group = 0 # unsigned number # The number of newlines before a multi-line function def body. nl_before_func_body_def = 0 # unsigned number # The number of newlines before a multi-line function prototype body. nl_before_func_body_proto = 0 # unsigned number # The number of newlines after '}' of a multi-line function body. nl_after_func_body = 2 # unsigned number # The number of newlines after '}' of a multi-line function body in a class declaration. nl_after_func_body_class = 0 # unsigned number # The number of newlines after '}' of a single line function body. nl_after_func_body_one_liner = 0 # unsigned number # The minimum number of newlines before a multi-line comment. # Doesn't apply if after a brace open or another multi-line comment. nl_before_block_comment = 0 # unsigned number # The minimum number of newlines before a single-line C comment. # Doesn't apply if after a brace open or other single-line C comments. nl_before_c_comment = 0 # unsigned number # The minimum number of newlines before a CPP comment. # Doesn't apply if after a brace open or other CPP comments. nl_before_cpp_comment = 0 # unsigned number # Whether to force a newline after a multi-line comment. nl_after_multiline_comment = false # false/true # Whether to force a newline after a label's colon. nl_after_label_colon = true # false/true # The number of newlines after '}' or ';' of a struct/enum/union definition. nl_after_struct = 0 # unsigned number # The number of newlines before a class definition. nl_before_class = 0 # unsigned number # The number of newlines after '}' or ';' of a class definition. nl_after_class = 0 # unsigned number # The number of newlines before a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. # Will not change the newline count if after a brace open. # 0 = No change. nl_before_access_spec = 1 # unsigned number # The number of newlines after a 'private:', 'public:', 'protected:', 'signals:' or 'slots:' label. # 0 = No change. # the option 'nl_after_access_spec' takes preference over 'nl_typedef_blk_start' and 'nl_var_def_blk_start'. nl_after_access_spec = 1 # unsigned number # The number of newlines between a function def and the function comment. # 0 = No change. nl_comment_func_def = 0 # unsigned number # The number of newlines after a try-catch-finally block that isn't followed by a brace close. # 0 = No change. nl_after_try_catch_finally = 0 # unsigned number # The number of newlines before and after a property, indexer or event decl. # 0 = No change. nl_around_cs_property = 0 # unsigned number # The number of newlines between the get/set/add/remove handlers in C#. # 0 = No change. nl_between_get_set = 0 # unsigned number # Add or remove newline between C# property and the '{'. nl_property_brace = ignore # ignore/add/remove/force # Whether to remove blank lines after '{'. eat_blanks_after_open_brace = true # false/true # Whether to remove blank lines before '}'. eat_blanks_before_close_brace = true # false/true # How aggressively to remove extra newlines not in preproc. # 0: No change # 1: Remove most newlines not handled by other config # 2: Remove all newlines and reformat completely by config nl_remove_extra_newlines = 0 # unsigned number # Whether to put a blank line before 'return' statements, unless after an open brace. nl_before_return = false # false/true # Whether to put a blank line after 'return' statements, unless followed by a close brace. nl_after_return = false # false/true # Whether to put a newline after a Java annotation statement. # Only affects annotations that are after a newline. nl_after_annotation = ignore # ignore/add/remove/force # Controls the newline between two annotations. nl_between_annotation = ignore # ignore/add/remove/force # # Positioning options # # The position of arithmetic operators in wrapped expressions. pos_arith = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of assignment in wrapped expressions. # Do not affect '=' followed by '{'. pos_assign = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of boolean operators in wrapped expressions. pos_bool = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of comparison operators in wrapped expressions. pos_compare = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of conditional (b ? t : f) operators in wrapped expressions. pos_conditional = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of the comma in wrapped expressions. pos_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of the comma in enum entries. pos_enum_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of the comma in the base class list if there are more than one line, # (tied to nl_class_init_args). pos_class_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of the comma in the constructor initialization list. # Related to nl_constr_colon, nl_constr_init_args and pos_constr_colon. pos_constr_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of trailing/leading class colon, between class and base class list # (tied to nl_class_colon). pos_class_colon = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # The position of colons between constructor and member initialization, # (tied to nl_constr_colon). # Related to nl_constr_colon, nl_constr_init_args and pos_constr_comma. pos_constr_colon = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force # # Line Splitting options # # Try to limit code width to N number of columns code_width = 100 # unsigned number # Whether to fully split long 'for' statements at semi-colons. ls_for_split_full = false # false/true # Whether to fully split long function protos/calls at commas. ls_func_split_full = true # false/true # Whether to split lines as close to code_width as possible and ignore some groupings. ls_code_width = false # false/true # # Code alignment (not left column spaces/tabs) # # Whether to keep non-indenting tabs. align_keep_tabs = false # false/true # Whether to use tabs for aligning. align_with_tabs = false # false/true # Whether to bump out to the next tab when aligning. align_on_tabstop = false # false/true # Whether to left-align numbers. # align_number_left = false # false/true # Whether to keep whitespace not required for alignment. align_keep_extra_space = true # false/true # Align variable definitions in prototypes and functions. # ellzey align_func_params = true # false/true # Align parameters in single-line functions that have the same name. # The function names must already be aligned with each other. align_same_func_call_params = false # false/true # The span for aligning variable definitions (0=don't align) align_var_def_span = 4 # unsigned number # How to align the star in variable definitions. # 0=Part of the type 'void * foo;' # 1=Part of the variable 'void *foo;' # 2=Dangling 'void *foo;' align_var_def_star_style = 2 # unsigned number # How to align the '&' in variable definitions. # 0=Part of the type # 1=Part of the variable # 2=Dangling align_var_def_amp_style = 0 # unsigned number # The threshold for aligning variable definitions (0=no limit) align_var_def_thresh = 0 # unsigned number # The gap for aligning variable definitions. align_var_def_gap = 0 # unsigned number # Whether to align the colon in struct bit fields. align_var_def_colon = true # false/true # align variable defs gap for bit colons. align_var_def_colon_gap = 0 # unsigned number # Whether to align any attribute after the variable name. align_var_def_attribute = false # false/true # Whether to align inline struct/enum/union variable definitions. align_var_def_inline = true # false/true # The span for aligning on '=' in assignments (0=don't align) align_assign_span = 3 # unsigned number # The threshold for aligning on '=' in assignments (0=no limit) align_assign_thresh = 10 # unsigned number # The span for aligning on '=' in enums (0=don't align) align_enum_equ_span = 1 # unsigned number # The threshold for aligning on '=' in enums (0=no limit) align_enum_equ_thresh = 0 # unsigned number # The span for aligning class (0=don't align) align_var_class_span = 0 # unsigned number # The threshold for aligning class member definitions (0=no limit). align_var_class_thresh = 0 # unsigned number # The gap for aligning class member definitions. align_var_class_gap = 0 # unsigned number # The span for aligning struct/union (0=don't align) align_var_struct_span = 1 # unsigned number # The threshold for aligning struct/union member definitions (0=no limit) align_var_struct_thresh = 0 # unsigned number # The gap for aligning struct/union member definitions. align_var_struct_gap = 0 # unsigned number # The span for aligning struct initializer values (0=don't align) align_struct_init_span = 1 # unsigned number # The minimum space between the type and the synonym of a typedef. align_typedef_gap = 0 # unsigned number # The span for aligning single-line typedefs (0=don't align). align_typedef_span = 1 # unsigned number # How to align typedef'd functions with other typedefs # 0: Don't mix them at all # 1: align the open paren with the types # 2: align the function type name with the other type names align_typedef_func = 0 # unsigned number # Controls the positioning of the '*' in typedefs. Just try it. # 0: Align on typedef type, ignore '*' # 1: The '*' is part of type name: typedef int *pint; # 2: The '*' is part of the type, but dangling: typedef int *pint; align_typedef_star_style = 2 # unsigned number # Controls the positioning of the '&' in typedefs. Just try it. # 0: Align on typedef type, ignore '&' # 1: The '&' is part of type name: typedef int &pint; # 2: The '&' is part of the type, but dangling: typedef int &pint; align_typedef_amp_style = 0 # unsigned number # The span for aligning comments that end lines (0=don't align) align_right_cmt_span = 4 # unsigned number # If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment. align_right_cmt_mix = false # false/true # If a trailing comment is more than this number of columns away from the text it follows, # it will qualify for being aligned. This has to be > 0 to do anything. align_right_cmt_gap = 0 # unsigned number # Align trailing comment at or beyond column N; 'pulls in' comments as a bonus side effect (0=ignore) align_right_cmt_at_col = 0 # unsigned number # The span for aligning function prototypes (0=don't align). align_func_proto_span = 0 # unsigned number # Minimum gap between the return type and the function name. align_func_proto_gap = 0 # unsigned number # Align function protos on the 'operator' keyword instead of what follows. align_on_operator = false # false/true # Whether to mix aligning prototype and variable declarations. # If True, align_var_def_XXX options are used instead of align_func_proto_XXX options. align_mix_var_proto = true # false/true # Align single-line functions with function prototypes, uses align_func_proto_span. align_single_line_func = false # false/true # Aligning the open brace of single-line functions. # Requires align_single_line_func=True, uses align_func_proto_span. align_single_line_brace = false # false/true # Gap for align_single_line_brace. align_single_line_brace_gap = 0 # unsigned number # The span for aligning ObjC msg spec (0=don't align) align_oc_msg_spec_span = 0 # unsigned number # Whether to align macros wrapped with a backslash and a newline. # This will not work right if the macro contains a multi-line comment. align_nl_cont = true # false/true # # Align macro functions and variables together. align_pp_define_together = false # false/true # The minimum space between label and value of a preprocessor define. align_pp_define_gap = 0 # unsigned number # The span for aligning on '#define' bodies (0=don't align, other=number of lines including comments between blocks) align_pp_define_span = 20 # unsigned number # Align lines that start with '<<' with previous '<<'. Default=True. align_left_shift = true # false/true # Align text after asm volatile () colons. align_asm_colon = false # false/true # Span for aligning parameters in an Obj-C message call on the ':' (0=don't align) align_oc_msg_colon_span = 0 # unsigned number # If True, always align with the first parameter, even if it is too short. align_oc_msg_colon_first = false # false/true # Aligning parameters in an Obj-C '+' or '-' declaration on the ':'. align_oc_decl_colon = false # false/true # # Comment modifications # # Try to wrap comments at cmt_width columns cmt_width = 0 # unsigned number # Set the comment reflow mode (Default=0) # 0: no reflowing (apart from the line wrapping due to cmt_width) # 1: no touching at all # 2: full reflow cmt_reflow_mode = 0 # unsigned number # Whether to convert all tabs to spaces in comments. Default is to leave tabs inside comments alone, unless used for indenting. cmt_convert_tab_to_spaces = false # false/true # If False, disable all multi-line comment changes, including cmt_width. keyword substitution and leading chars. # Default=True. cmt_indent_multi = true # false/true # Whether to group c-comments that look like they are in a block. cmt_c_group = true # false/true # Whether to put an empty '/*' on the first line of the combined c-comment. cmt_c_nl_start = true # false/true # Whether to put a newline before the closing '*/' of the combined c-comment. cmt_c_nl_end = true # false/true # Whether to group cpp-comments that look like they are in a block. cmt_cpp_group = false # false/true # Whether to put an empty '/*' on the first line of the combined cpp-comment. cmt_cpp_nl_start = false # false/true # Whether to put a newline before the closing '*/' of the combined cpp-comment. cmt_cpp_nl_end = false # false/true # Whether to change cpp-comments into c-comments. cmt_cpp_to_c = true # false/true # Whether to put a star on subsequent comment lines. cmt_star_cont = true # false/true # The number of spaces to insert at the start of subsequent comment lines. cmt_sp_before_star_cont = 0 # unsigned number # The number of spaces to insert after the star on subsequent comment lines. cmt_sp_after_star_cont = 0 # number # For multi-line comments with a '*' lead, remove leading spaces if the first and last lines of # the comment are the same length. Default=True. cmt_multi_check_last = true # false/true # For multi-line comments with a '*' lead, remove leading spaces if the first and last lines of # the comment are the same length AND if the length is bigger as the first_len minimum. Default=4 cmt_multi_first_len_minimum = 4 # unsigned number # The filename that contains text to insert at the head of a file if the file doesn't start with a C/C++ comment. # Will substitute $(filename) with the current file's name. cmt_insert_file_header = "" # string # The filename that contains text to insert at the end of a file if the file doesn't end with a C/C++ comment. # Will substitute $(filename) with the current file's name. cmt_insert_file_footer = "" # string # The filename that contains text to insert before a function implementation if the function isn't preceded with a C/C++ comment. # Will substitute $(function) with the function name and $(javaparam) with the javadoc @param and @return stuff. # Will also substitute $(fclass) with the class name: void CFoo::Bar() { ... }. cmt_insert_func_header = "" # string # The filename that contains text to insert before a class if the class isn't preceded with a C/C++ comment. # Will substitute $(class) with the class name. cmt_insert_class_header = "" # string # The filename that contains text to insert before a Obj-C message specification if the method isn't preceded with a C/C++ comment. # Will substitute $(message) with the function name and $(javaparam) with the javadoc @param and @return stuff. cmt_insert_oc_msg_header = "" # string # If a preprocessor is encountered when stepping backwards from a function name, then # this option decides whether the comment should be inserted. # Affects cmt_insert_oc_msg_header, cmt_insert_func_header and cmt_insert_class_header. cmt_insert_before_preproc = false # false/true # If a function is declared inline to a class definition, then # this option decides whether the comment should be inserted. # Affects cmt_insert_func_header. cmt_insert_before_inlines = true # false/true # If the function is a constructor/destructor, then # this option decides whether the comment should be inserted. # Affects cmt_insert_func_header. cmt_insert_before_ctor_dtor = false # false/true # # Code modifying options (non-whitespace) # # Add or remove braces on single-line 'do' statement. mod_full_brace_do = add # ignore/add/remove/force # Add or remove braces on single-line 'for' statement. mod_full_brace_for = add # ignore/add/remove/force # Add or remove braces on single-line function definitions. (Pawn). #mod_full_brace_function = add # ignore/add/remove/force # Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'. mod_full_brace_if = force # ignore/add/remove/force # Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if. # If any must be braced, they are all braced. If all can be unbraced, then the braces are removed. # mod_full_brace_if_chain = false # false/true # Make all if/elseif/else statements with at least one 'else' or 'else if' fully braced. # If mod_full_brace_if_chain is used together with this option, all if-else chains will get braces, # and simple 'if' statements will lose them (if possible). #mod_full_brace_if_chain_only = false # false/true # Don't remove braces around statements that span N newlines # mod_full_brace_nl = 3 # unsigned number # Blocks removal of braces if the parenthesis of if/for/while/.. span multiple lines. mod_full_brace_nl_block_rem_mlcond = false # false/true # Add or remove braces on single-line 'while' statement. mod_full_brace_while = add # ignore/add/remove/force # Add or remove braces on single-line 'using ()' statement. mod_full_brace_using = ignore # ignore/add/remove/force # Add or remove unnecessary paren on 'return' statement. mod_paren_on_return = remove # ignore/add/remove/force # Whether to change optional semicolons to real semicolons. mod_pawn_semicolon = false # false/true # Add parens on 'while' and 'if' statement around bools. mod_full_paren_if_bool = false # false/true # Whether to remove superfluous semicolons. mod_remove_extra_semicolon = false # false/true # If a function body exceeds the specified number of newlines and doesn't have a comment after # the close brace, a comment will be added. mod_add_long_function_closebrace_comment = 40 # unsigned number # If a namespace body exceeds the specified number of newlines and doesn't have a comment after # the close brace, a comment will be added. mod_add_long_namespace_closebrace_comment = 0 # unsigned number # If a class body exceeds the specified number of newlines and doesn't have a comment after # the close brace, a comment will be added. mod_add_long_class_closebrace_comment = 0 # unsigned number # If a switch body exceeds the specified number of newlines and doesn't have a comment after # the close brace, a comment will be added. mod_add_long_switch_closebrace_comment = 20 # unsigned number # If an #ifdef body exceeds the specified number of newlines and doesn't have a comment after # the #endif, a comment will be added. mod_add_long_ifdef_endif_comment = 0 # unsigned number # If an #ifdef or #else body exceeds the specified number of newlines and doesn't have a comment after # the #else, a comment will be added. mod_add_long_ifdef_else_comment = 0 # unsigned number # If True, will sort consecutive single-line 'import' statements [Java, D]. mod_sort_import = false # false/true # If True, will sort consecutive single-line 'using' statements [C#]. mod_sort_using = false # false/true # If True, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C] # This is generally a bad idea, as it may break your code. mod_sort_include = false # false/true # If True, it will move a 'break' that appears after a fully braced 'case' before the close brace. mod_move_case_break = false # false/true # Will add or remove the braces around a fully braced case statement. # Will only remove the braces if there are no variable declarations in the block. mod_case_brace = ignore # ignore/add/remove/force # If True, it will remove a void 'return;' that appears as the last statement in a function. mod_remove_empty_return = false # false/true # If True, it will organize the properties (Obj-C). mod_sort_oc_properties = false # false/true # Determines weight of class property modifier (Obj-C). mod_sort_oc_property_class_weight = 0 # number # Determines weight of atomic, nonatomic (Obj-C). mod_sort_oc_property_thread_safe_weight = 0 # number # Determines weight of readwrite (Obj-C). mod_sort_oc_property_readwrite_weight = 0 # number # Determines weight of reference type (retain, copy, assign, weak, strong) (Obj-C). mod_sort_oc_property_reference_weight = 0 # number # Determines weight of getter type (getter=) (Obj-C). mod_sort_oc_property_getter_weight = 0 # number # Determines weight of setter type (setter=) (Obj-C). mod_sort_oc_property_setter_weight = 0 # number # Determines weight of nullability type (nullable, nonnull, null_unspecified, null_resettable) (Obj-C). mod_sort_oc_property_nullability_weight = 0 # number # # Preprocessor options # # Control indent of preprocessors inside #if blocks at brace level 0 (file-level). pp_indent = ignore # ignore/add/remove/force # Whether to indent #if/#else/#endif at the brace level (True) or from column 1 (False). pp_indent_at_level = false # false/true # Specifies the number of columns to indent preprocessors per level at brace level 0 (file-level). # If pp_indent_at_level=False, specifies the number of columns to indent preprocessors per level at brace level > 0 (function-level). # Default=1. pp_indent_count = 1 # unsigned number # Add or remove space after # based on pp_level of #if blocks. pp_space = ignore # ignore/add/remove/force # Sets the number of spaces added with pp_space. pp_space_count = 0 # unsigned number # The indent for #region and #endregion in C# and '#pragma region' in C/C++. pp_indent_region = 0 # number # Whether to indent the code between #region and #endregion. pp_region_indent_code = false # false/true # If pp_indent_at_level=True, sets the indent for #if, #else and #endif when not at file-level. # 0: indent preprocessors using output_tab_size. # >0: column at which all preprocessors will be indented. pp_indent_if = 0 # number # Control whether to indent the code between #if, #else and #endif. pp_if_indent_code = false # false/true # Whether to indent '#define' at the brace level (True) or from column 1 (false). pp_define_at_level = true # false/true # Whether to ignore the '#define' body while formatting. pp_ignore_define_body = false # false/true # Whether to indent case statements between #if, #else, and #endif. # Only applies to the indent of the preprocesser that the case statements directly inside of. pp_indent_case = true # false/true # Whether to indent whole function definitions between #if, #else, and #endif. # Only applies to the indent of the preprocesser that the function definition is directly inside of. pp_indent_func_def = true # false/true # Whether to indent extern C blocks between #if, #else, and #endif. # Only applies to the indent of the preprocesser that the extern block is directly inside of. pp_indent_extern = true # false/true # Whether to indent braces directly inside #if, #else, and #endif. # Only applies to the indent of the preprocesser that the braces are directly inside of. pp_indent_brace = true # false/true # # Sort includes options # # The regex for include category with priority 0. include_category_0 = "" # string # The regex for include category with priority 1. include_category_1 = "" # string # The regex for include category with priority 2. include_category_2 = "" # string # # Use or Do not Use options # # True: indent_func_call_param will be used (default) # False: indent_func_call_param will NOT be used. use_indent_func_call_param = false # false/true # The value of the indentation for a continuation line is calculate differently if the line is: # a declaration :your case with QString fileName ... # an assignment :your case with pSettings = new QSettings( ... # At the second case the option value might be used twice: # at the assignment # at the function call (if present) # To prevent the double use of the option value, use this option with the value 'True'. # True: indent_continue will be used only once # False: indent_continue will be used every time (default). use_indent_continue_only_once = false # false/true # SIGNAL/SLOT Qt macros have special formatting options. See options_for_QT.cpp for details. # Default=True. use_options_overriding_for_qt_macros = false # false/true # # Warn levels - 1: error, 2: warning (default), 3: note # # Warning is given if doing tab-to-\t replacement and we have found one in a C# verbatim string literal. warn_level_tabs_found_in_verbatim_string_literals = 2 # unsigned number # Meaning of the settings: # Ignore - do not do any changes # Add - makes sure there is 1 or more space/brace/newline/etc # Force - makes sure there is exactly 1 space/brace/newline/etc, # behaves like Add in some contexts # Remove - removes space/brace/newline/etc # # # - Token(s) can be treated as specific type(s) with the 'set' option: # `set tokenType tokenString [tokenString...]` # # Example: # `set BOOL __AND__ __OR__` # # tokenTypes are defined in src/token_enum.h, use them without the # 'CT_' prefix: 'CT_BOOL' -> 'BOOL' # # # - Token(s) can be treated as type(s) with the 'type' option. # `type tokenString [tokenString...]` # # Example: # `type int c_uint_8 Rectangle` # # This can also be achieved with `set TYPE int c_uint_8 Rectangle` # # # To embed whitespace in tokenStrings use the '\' escape character, or quote # the tokenStrings. These quotes are supported: "'` # # # - Support for the auto detection of languages through the file ending can be # added using the 'file_ext' command. # `file_ext langType langString [langString..]` # # Example: # `file_ext CPP .ch .cxx .cpp.in` # # langTypes are defined in uncrusify_types.h in the lang_flag_e enum, use # them without the 'LANG_' prefix: 'LANG_CPP' -> 'CPP' # # # - Custom macro-based indentation can be set up using 'macro-open', # 'macro-else' and 'macro-close'. # `(macro-open | macro-else | macro-close) tokenString` # # Example: # `macro-open BEGIN_TEMPLATE_MESSAGE_MAP` # `macro-open BEGIN_MESSAGE_MAP` # `macro-close END_MESSAGE_MAP` # ## option(s) with 'not default' value: 81 # libevhtp-1.2.18/evhtp.c000066400000000000000000004257331342660753300147410ustar00rootroot00000000000000/** * @file evhtp.c * * @brief implementation file for libevhtp. */ #include #include #include #include #include #include #include #include #ifndef WIN32 #include #include #include #include #else #define WINVER 0x0501 #include #include #endif #ifndef NO_SYS_UN #include #endif #include #include #include "evhtp/config.h" #include "internal.h" #include "numtoa.h" #include "evhtp/evhtp.h" /** * @brief structure containing a single callback and configuration * * The definition structure which is used within the evhtp_callbacks_t * structure. This holds information about what should execute for either * a single or regex path. * * For example, if you registered a callback to be executed on a request * for "/herp/derp", your defined callback will be executed. * * Optionally you can set callback-specific hooks just like per-connection * hooks using the same rules. * */ struct evhtp_callback { evhtp_callback_type type; /**< the type of callback (regex|path) */ evhtp_callback_cb cb; /**< the actual callback function */ void * cbarg; /**< user-defind arguments passed to the cb */ evhtp_hooks_t * hooks; /**< per-callback hooks */ size_t len; union { char * path; char * glob; #ifndef EVHTP_DISABLE_REGEX regex_t * regex; #endif } val; TAILQ_ENTRY(evhtp_callback) next; }; TAILQ_HEAD(evhtp_callbacks, evhtp_callback); #define SET_BIT(VAR, FLAG) VAR |= FLAG #define UNSET_BIT(VAR, FLAG) VAR &= ~FLAG #define HTP_FLAG_ON(PRE, FLAG) SET_BIT(PRE->flags, FLAG) #define HTP_FLAG_OFF(PRE, FLAG) UNSET_BIT(PRE->flags, FLAG) #define HTP_IS_READING(b) ((bufferevent_get_enabled(b) & \ EV_READ) ? true : false) #define HTP_IS_WRITING(b) ((bufferevent_get_enabled(b) & \ EV_WRITE) ? true : false) #define HTP_LEN_OUTPUT(b) (evbuffer_get_length(bufferevent_get_output(b))) #define HTP_LEN_INPUT(b) (evbuffer_get_length(bufferevent_get_input(b))) #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 #define HOOK_REQUEST_RUN(request, hook_name, ...) do { \ if (HOOK_AVAIL(request, hook_name)) { \ return HOOK_FUNC(request, hook_name) (request, __VA_ARGS__, \ HOOK_ARGS(request, hook_name)); \ } \ if (request->conn && HOOK_AVAIL(request->conn, hook_name)) { \ return HOOK_FUNC(request->conn, hook_name) (request, __VA_ARGS__, \ HOOK_ARGS(request->conn, hook_name)); \ } \ } while (0) #define HOOK_REQUEST_RUN_NARGS(__request, hook_name) do { \ if (HOOK_AVAIL(__request, hook_name)) { \ return HOOK_FUNC(__request, hook_name) (__request, \ HOOK_ARGS(__request, hook_name)); \ } \ if (__request->conn && HOOK_AVAIL(__request->conn, hook_name)) { \ return HOOK_FUNC(__request->conn, hook_name) (request, \ HOOK_ARGS(__request->conn, hook_name)); \ } \ } while (0); #ifndef EVHTP_DISABLE_EVTHR /** * @brief Helper macro to lock htp structure * * @param h htp structure */ #define htp__lock_(h) do { \ if (h->lock) { \ pthread_mutex_lock(h->lock); \ } \ } while (0) /** * @brief Helper macro to unlock htp lock * * @param h htp structure */ #define htp__unlock_(h) do { \ if (h->lock) { \ pthread_mutex_unlock(h->lock); \ } \ } while (0) #else #define htp__lock_(h) do { \ } while (0) #define htp__unlock_(h) do { \ } while (0) #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 /* rc == request->conn. Just little things to make life easier */ #define rc_scratch conn->scratch_buf #define rc_parser conn->parser /* ch_ == conn->hooks->on_... */ #define ch_fini_arg hooks->on_connection_fini_arg #define ch_fini hooks->on_connection_fini /* cr_ == conn->request */ #define cr_status request->status #define cr_flags request->flags #define cr_proto request->proto /* rh_ == request->hooks->on_ */ #define rh_err hooks->on_error #define rh_err_arg hooks->on_error_arg #ifndef EVHTP_DISABLE_MEMFUNCTIONS static void * (*malloc_)(size_t sz) = malloc; static void * (* realloc_)(void * d, size_t sz) = realloc; static void (* free_)(void * d) = free; /** * @brief Wrapper for malloc so that a different malloc can be used * if desired. * * @see evhtp_set_mem_functions * * @param size size_t of memory to be allocated * * @return void * to malloc'd memory or NULL if fail */ static void * htp__malloc_(size_t size) { return malloc_(size); } /** * @brief Wrapper for realloc so that a different realloc can be used * if desired. * * @see evhtp_set_mem_functions * * @param ptr current memory ptr * @param size size_t of memory to be allocated * * @return void * to newly realloc'd memory or NULL if fail */ static void * htp__realloc_(void * ptr, size_t size) { return realloc_(ptr, size); } /** * @brief Wrapper for free so that a different free can be used * if desired. * * @see evhtp_set_mem_functions * * @param ptr pointer to memory to be freed. * */ static void htp__free_(void * ptr) { if (ptr == NULL) { return; } evhtp_safe_free(ptr, free_); } /** * @brief Wrapper for calloc so that a different calloc can be used * if desired. * * @see evhtp_set_mem_functions * * @param nmemb number of members (as a size_t) * @param size size of member blocks (as a size_t) * * @return void * to new memory block */ static void * htp__calloc_(size_t nmemb, size_t size) { if (malloc_ != malloc) { size_t len = nmemb * size; void * p; if ((p = malloc_(len)) == NULL) { return NULL; } memset(p, 0, len); return p; } return calloc(nmemb, size); } /** * @brief implementation of strdup function. * * @param str - null terminated string. * * @return duplicate of string or NULL if fail * */ static char * htp__strdup_(const char * str) { if (malloc_ != malloc) { size_t len; void * p; len = strlen(str); if ((p = malloc_(len + 1)) == NULL) { return NULL; } memcpy(p, str, len + 1); return p; } return strdup(str); } /** * @brief implementation of strndup function. * * @param str - null terminated string. * @param len - size_t length off string * * @return duplicate of string or NULL if fail * */ static char * htp__strndup_(const char * str, size_t len) { if (malloc_ != malloc) { char * p; if ((p = malloc_(len + 1)) != NULL) { memcpy(p, str, len + 1); } else { return NULL; } p[len] = '\0'; return p; } return strndup(str, len); } #else #define htp__malloc_(sz) malloc(sz) #define htp__calloc_(n, sz) calloc(n, sz) #define htp__strdup_(s) strdup(s) #define htp__strndup_(n, sz) strndup(n, sz) #define htp__realloc_(p, sz) realloc(p, sz) #define htp__free_(p) free(p) #endif void evhtp_set_mem_functions(void *(*mallocfn_)(size_t len), void *(*reallocfn_)(void * p, size_t sz), void (*freefn_)(void * p)) { #ifndef EVHTP_DISABLE_MEMFUNCTIONS malloc_ = mallocfn_; realloc_ = reallocfn_; free_ = freefn_; return event_set_mem_functions(malloc_, realloc_, free_); #endif } /** * @brief returns string status code from enum code * * @param code as evhtp_res enum * * @return string corresponding to code, else UNKNOWN */ static const char * status_code_to_str(evhtp_res code) { switch (code) { case EVHTP_RES_200: return "OK"; case EVHTP_RES_300: return "Redirect"; case EVHTP_RES_400: return "Bad Request"; case EVHTP_RES_NOTFOUND: return "Not Found"; case EVHTP_RES_SERVERR: return "Internal Server Error"; case EVHTP_RES_CONTINUE: return "Continue"; case EVHTP_RES_FORBIDDEN: return "Forbidden"; case EVHTP_RES_SWITCH_PROTO: return "Switching Protocols"; case EVHTP_RES_MOVEDPERM: return "Moved Permanently"; case EVHTP_RES_PROCESSING: return "Processing"; case EVHTP_RES_URI_TOOLONG: return "URI Too Long"; case EVHTP_RES_CREATED: return "Created"; case EVHTP_RES_ACCEPTED: return "Accepted"; case EVHTP_RES_NAUTHINFO: return "No Auth Info"; case EVHTP_RES_NOCONTENT: return "No Content"; case EVHTP_RES_RSTCONTENT: return "Reset Content"; case EVHTP_RES_PARTIAL: return "Partial Content"; case EVHTP_RES_MSTATUS: return "Multi-Status"; case EVHTP_RES_IMUSED: return "IM Used"; case EVHTP_RES_FOUND: return "Found"; case EVHTP_RES_SEEOTHER: return "See Other"; case EVHTP_RES_NOTMOD: return "Not Modified"; case EVHTP_RES_USEPROXY: return "Use Proxy"; case EVHTP_RES_SWITCHPROXY: return "Switch Proxy"; case EVHTP_RES_TMPREDIR: return "Temporary Redirect"; case EVHTP_RES_UNAUTH: return "Unauthorized"; case EVHTP_RES_PAYREQ: return "Payment Required"; case EVHTP_RES_METHNALLOWED: return "Not Allowed"; case EVHTP_RES_NACCEPTABLE: return "Not Acceptable"; case EVHTP_RES_PROXYAUTHREQ: return "Proxy Authentication Required"; case EVHTP_RES_TIMEOUT: return "Request Timeout"; case EVHTP_RES_CONFLICT: return "Conflict"; case EVHTP_RES_GONE: return "Gone"; case EVHTP_RES_LENREQ: return "Length Required"; case EVHTP_RES_PRECONDFAIL: return "Precondition Failed"; case EVHTP_RES_ENTOOLARGE: return "Entity Too Large"; case EVHTP_RES_URITOOLARGE: return "Request-URI Too Long"; case EVHTP_RES_UNSUPPORTED: return "Unsupported Media Type"; case EVHTP_RES_RANGENOTSC: return "Requested Range Not Satisfiable"; case EVHTP_RES_EXPECTFAIL: return "Expectation Failed"; case EVHTP_RES_IAMATEAPOT: return "I'm a teapot"; case EVHTP_RES_NOTIMPL: return "Not Implemented"; case EVHTP_RES_BADGATEWAY: return "Bad Gateway"; case EVHTP_RES_SERVUNAVAIL: return "Service Unavailable"; case EVHTP_RES_GWTIMEOUT: return "Gateway Timeout"; case EVHTP_RES_VERNSUPPORT: return "HTTP Version Not Supported"; case EVHTP_RES_BWEXEED: return "Bandwidth Limit Exceeded"; } /* switch */ return "UNKNOWN"; } /* status_code_to_str */ #ifndef EVHTP_DISABLE_SSL static int session_id_context = 1; #ifndef EVHTP_DISABLE_EVTHR static int ssl_num_locks; static evhtp_mutex_t * ssl_locks; static int ssl_locks_initialized = 0; #endif #endif /* * COMPAT FUNCTIONS */ #ifdef NO_STRNLEN /** * @brief Implementation of strnlen function if none exists. * * @param s - null terminated character string * @param maxlen - maximum length of string * * @return length of string * */ static size_t strnlen(const char * s, size_t maxlen) { const char * e; size_t n; for (e = s, n = 0; *e && n < maxlen; e++, n++) { ; } return n; } #endif #ifdef NO_STRNDUP /** * @brief Implementation of strndup if none exists. * * @param s - const char * to null terminated string * @param n - size_t maximum legnth of string * * @return length limited string duplicate or NULL if fail * */ static char * strndup(const char * s, size_t n) { size_t len = strnlen(s, n); char * ret; if (len < n) { return htp__strdup_(s); } if ((ret = htp__malloc_(n + 1)) == NULL) { return NULL; } ret[n] = '\0'; memcpy(ret, s, n); return ret; } #endif /* * PRIVATE FUNCTIONS */ /** * * @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 */ #define htp__is_http_11_(_major, _minor) \ (_major >= 1 && _minor >= 1) /** * @brief helper function to determine if http version is HTTP/1.1 * * @param major the major version number * @param minor the minor version number * * @return 1 if HTTP/1.1, else 0 */ #define htp__is_http_10_(_major, _minor) \ (_major >= 1 && _minor <= 0) /** * @brief returns the HTTP protocol version * * @param major the major version number * @param minor the minor version number * * @return EVHTP_PROTO_10 if HTTP/1.0, EVHTP_PROTO_11 if HTTP/1.1, otherwise * EVHTP_PROTO_INVALID */ static inline evhtp_proto htp__protocol_(const char major, const char minor) { if (htp__is_http_10_(major, minor)) { return EVHTP_PROTO_10; } if (htp__is_http_11_(major, minor)) { return EVHTP_PROTO_11; } return EVHTP_PROTO_INVALID; } /** * @brief runs the user-defined on_path hook for a request * * @param request the request structure * @param path the path structure * * @return EVHTP_RES_OK on success, otherwise something else. */ static inline evhtp_res htp__hook_path_(struct evhtp_request * request, struct evhtp_path * path) { HOOK_REQUEST_RUN(request, on_path, path); return EVHTP_RES_OK; } /** * @brief runs the user-defined on_header hook for a request * * once a full key: value header has been parsed, this will call the hook * * @param request the request strucutre * @param header the header structure * * @return EVHTP_RES_OK on success, otherwise something else. */ static inline evhtp_res htp__hook_header_(struct evhtp_request * request, evhtp_header_t * header) { HOOK_REQUEST_RUN(request, on_header, header); return EVHTP_RES_OK; } /** * @brief runs the user-defined on_Headers hook for a request after all headers * have been parsed. * * @param request the request structure * @param headers the headers tailq structure * * @return EVHTP_RES_OK on success, otherwise something else. */ static inline evhtp_res htp__hook_headers_(struct evhtp_request * request, evhtp_headers_t * headers) { HOOK_REQUEST_RUN(request, on_headers, headers); return EVHTP_RES_OK; } /** * @brief runs the user-defined on_body hook for requests containing a body. * the data is stored in the request->buffer_in so the user may either * leave it, or drain upon being called. * * @param request the request strucutre * @param buf a evbuffer containing body data * * @return EVHTP_RES_OK on success, otherwise something else. */ static inline evhtp_res htp__hook_body_(struct evhtp_request * request, struct evbuffer * buf) { if (request == NULL) { return EVHTP_RES_500; } HOOK_REQUEST_RUN(request, on_read, buf); return EVHTP_RES_OK; } /** * @brief runs the user-defined hook called just prior to a request been * free()'d * * @param request therequest structure * * @return EVHTP_RES_OK on success, otherwise treated as an error */ static inline evhtp_res htp__hook_request_fini_(struct evhtp_request * request) { if (request == NULL) { return EVHTP_RES_500; } HOOK_REQUEST_RUN_NARGS(request, on_request_fini); return EVHTP_RES_OK; } /** * @brief Runs the user defined request hook * * @param request * @param len * @return */ static inline evhtp_res htp__hook_chunk_new_(struct evhtp_request * request, uint64_t len) { HOOK_REQUEST_RUN(request, on_new_chunk, len); return EVHTP_RES_OK; } /** * @brief Runs the user defined on_chunk_fini hook * * @param request * @return */ static inline evhtp_res htp__hook_chunk_fini_(struct evhtp_request * request) { HOOK_REQUEST_RUN_NARGS(request, on_chunk_fini); return EVHTP_RES_OK; } /** * @brief Runs the user defined on chunk_finis hook * * @param request * @return */ static inline evhtp_res htp__hook_chunks_fini_(struct evhtp_request * request) { HOOK_REQUEST_RUN_NARGS(request, on_chunks_fini); return EVHTP_RES_OK; } /** * @brief Runs the user defined on_headers_start hook * * @param request * @return */ static inline evhtp_res htp__hook_headers_start_(struct evhtp_request * request) { HOOK_REQUEST_RUN_NARGS(request, on_headers_start); return EVHTP_RES_OK; } /** * @brief runs the user-definedhook called just prior to a connection being * closed * * @param connection the connection structure * * @return EVHTP_RES_OK on success, but pretty much ignored in any case. */ static inline evhtp_res htp__hook_connection_fini_(struct evhtp_connection * connection) { if (evhtp_unlikely(connection == NULL)) { return 500; } if (connection->hooks != NULL && connection->ch_fini != NULL) { return (connection->ch_fini)(connection, connection->ch_fini_arg); } 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 htp__hook_error_(struct evhtp_request * request, evhtp_error_flags errtype) { if (request && request->hooks && request->rh_err) { (*request->rh_err)(request, errtype, request->rh_err_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 htp__hook_connection_error_(struct evhtp_connection * connection, evhtp_error_flags errtype) { if (connection == NULL) { return EVHTP_RES_FATAL; } if (connection->request != NULL) { htp__hook_error_(connection->request, errtype); } return EVHTP_RES_OK; } /** * @brief Runs the user defined hostname processing hook * * @param r * @param hostname * @return */ static inline evhtp_res htp__hook_hostname_(struct evhtp_request * r, const char * hostname) { HOOK_REQUEST_RUN(r, on_hostname, hostname); return EVHTP_RES_OK; } /** * @brief Runs the user defined on_write hook * * @param connection * @return */ static inline evhtp_res htp__hook_connection_write_(struct evhtp_connection * connection) { if (connection->hooks && connection->hooks->on_write) { return (connection->hooks->on_write)(connection, connection->hooks->on_write_arg); } return EVHTP_RES_OK; } /** * @brief glob/wildcard type pattern matching. * * Note: This code was derived from redis's (v2.6) stringmatchlen() function. * * @param pattern * @param string * * @return */ static int htp__glob_match_(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 (htp__glob_match_(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; } /* htp__glob_match_ */ /** * @brief Locates a given callback offsets performs a regex pattern match * * @param [IN] cbs ptr to evhtp_callbacks_t structure * @param [IN] path * @param [OUT] start_offset * @param [OUT] end_offset * @return */ static evhtp_callback_t * htp__callback_find_(evhtp_callbacks_t * cbs, const char * path, unsigned int * start_offset, unsigned int * end_offset) { size_t path_len; evhtp_callback_t * callback; #ifndef EVHTP_DISABLE_REGEX regmatch_t pmatch[28]; #endif if (evhtp_unlikely(cbs == NULL)) { return NULL; } path_len = strlen(path); TAILQ_FOREACH(callback, cbs, next) { switch (callback->type) { case evhtp_callback_type_hash: if (strncmp(path, callback->val.path, callback->len) == 0) { *start_offset = 0; *end_offset = path_len; return callback; } break; #ifndef EVHTP_DISABLE_REGEX case evhtp_callback_type_regex: if (regexec(callback->val.regex, path, callback->val.regex->re_nsub + 1, pmatch, 0) == 0) { *start_offset = pmatch[callback->val.regex->re_nsub].rm_so; *end_offset = pmatch[callback->val.regex->re_nsub].rm_eo; return callback; } break; #endif case evhtp_callback_type_glob: { size_t glob_len = strlen(callback->val.glob); if (htp__glob_match_(callback->val.glob, glob_len, path, path_len) == 1) { *start_offset = 0; *end_offset = path_len; return callback; } } default: break; } /* switch */ } return NULL; } /* htp__callback_find_ */ /** * @brief Correctly frees the evhtp_path_t ptr that is passed in. * @param path */ static void htp__path_free_(struct evhtp_path * path) { if (evhtp_unlikely(path == NULL)) { return; } evhtp_safe_free(path->full, htp__free_); evhtp_safe_free(path->path, htp__free_); evhtp_safe_free(path->file, htp__free_); evhtp_safe_free(path->match_start, htp__free_); evhtp_safe_free(path->match_end, htp__free_); evhtp_safe_free(path, htp__free_); } /** * @brief parses the path and file from an input buffer * * @details in order to properly create a structure that can match * both a path and a file, this will parse a string into * what it considers a path, and a file. * * @details if for example the input was "/a/b/c", the parser will * consider "/a/b/" as the path, and "c" as the file. * * @param the unallocated destination buffer. * @param data raw input data (assumes a /path/[file] structure) * @param len length of the input data * * @return 0 on success, -1 on error. */ static int htp__path_new_(evhtp_path_t ** out, const char * data, size_t len) { struct evhtp_path * req_path = NULL; const char * data_end = (const char *)(data + len); char * path = NULL; char * file = NULL; req_path = htp__calloc_(1, sizeof(*req_path)); #ifndef NDEBUG if (req_path == NULL) { return -1; } #endif *out = NULL; if (evhtp_unlikely(len == 0)) { /* * odd situation here, no preceding "/", so just assume the path is "/" */ path = htp__strdup_("/"); if (evhtp_unlikely(path == NULL)) { goto error; } } else if (*data != '/') { /* request like GET stupid HTTP/1.0, treat stupid as the file, and * assume the path is "/" */ path = htp__strdup_("/"); if (evhtp_unlikely(path == NULL)) { goto error; } file = htp__strndup_(data, len); if (evhtp_unlikely(file == NULL)) { goto error; } } else { if (data[len - 1] != '/') { /* * the last character in data is assumed to be a file, not the end of path * loop through the input data backwards until we find a "/" */ size_t i; for (i = (len - 1); i != 0; i--) { if (data[i] == '/') { /* * we have found a "/" representing the start of the file, * and the end of the path */ size_t path_len; size_t file_len; path_len = (size_t)(&data[i] - data) + 1; file_len = (size_t)(data_end - &data[i + 1]); /* check for overflow */ if ((const char *)(data + path_len) > data_end) { goto error; } /* check for overflow */ if ((const char *)(&data[i + 1] + file_len) > data_end) { goto error; } path = htp__strndup_(data, path_len); if (evhtp_unlikely(path == NULL)) { goto error; } file = htp__strndup_(&data[i + 1], file_len); if (evhtp_unlikely(file == NULL)) { goto error; } break; } } if (i == 0 && data[i] == '/' && !file && !path) { /* drops here if the request is something like GET /foo */ path = htp__strdup_("/"); if (evhtp_unlikely(path == NULL)) { goto error; } if (len > 1) { file = htp__strndup_((const char *)(data + 1), len); if (evhtp_unlikely(file == NULL)) { goto error; } } } } else { /* the last character is a "/", thus the request is just a path */ path = htp__strndup_(data, len); if (evhtp_unlikely(path == NULL)) { goto error; } } } if (len != 0) { req_path->full = htp__strndup_(data, len); } else { req_path->full = htp__strdup_("/"); } if (evhtp_unlikely(req_path->full == NULL)) { goto error; } req_path->path = path; req_path->file = file; *out = req_path; return 0; error: evhtp_safe_free(path, htp__free_); evhtp_safe_free(file, htp__free_); evhtp_safe_free(req_path, htp__path_free_); return -1; } /* htp__path_new_ */ /** * @brief create an authority structure * * @return 0 on success, -1 on error */ static int htp__authority_new_(evhtp_authority_t ** out) { struct evhtp_authority * authority; if (evhtp_unlikely(out == NULL)) { return -1; } *out = htp__calloc_(1, sizeof(*authority)); return (*out != NULL) ? 0 : -1; } /** * @brief frees an authority structure * * @param authority evhtp_authority_t */ static void htp__authority_free_(evhtp_authority_t * authority) { if (authority == NULL) { return; } evhtp_safe_free(authority->username, htp__free_); evhtp_safe_free(authority->password, htp__free_); evhtp_safe_free(authority->hostname, htp__free_); evhtp_safe_free(authority, htp__free_); } /** * @brief frees an overlay URI structure * * @param uri evhtp_uri_t */ static void htp__uri_free_(evhtp_uri_t * uri) { if (evhtp_unlikely(uri == NULL)) { return; } evhtp_safe_free(uri->query, evhtp_query_free); evhtp_safe_free(uri->path, htp__path_free_); evhtp_safe_free(uri->authority, htp__authority_free_); evhtp_safe_free(uri->fragment, htp__free_); evhtp_safe_free(uri->query_raw, htp__free_); evhtp_safe_free(uri, htp__free_); } /** * @brief create an overlay URI structure * * @return 0 on success, -1 on error. */ static int htp__uri_new_(evhtp_uri_t ** out) { struct evhtp_uri * uri; *out = NULL; if ((uri = htp__calloc_(1, sizeof(*uri))) == NULL) { return -1; } uri->authority = NULL; if (htp__authority_new_(&uri->authority) == -1) { evhtp_safe_free(uri, htp__uri_free_); return -1; } *out = uri; return 0; } /** * @brief frees all data in an evhtp_request_t along with calling finished hooks * * @param request the request structure */ static void htp__request_free_(evhtp_request_t * request) { if (request == NULL) { return; } htp__hook_request_fini_(request); evhtp_safe_free(request->uri, htp__uri_free_); evhtp_safe_free(request->headers_in, evhtp_kvs_free); evhtp_safe_free(request->headers_out, evhtp_kvs_free); if (request->conn && request->conn->request == request) { request->conn->request = NULL; } if (request->buffer_in != NULL) { evhtp_safe_free(request->buffer_in, evbuffer_free); } if (request->buffer_out != NULL) { evhtp_safe_free(request->buffer_out, evbuffer_free); } evhtp_safe_free(request->hooks, htp__free_); evhtp_safe_free(request, htp__free_); } /** * @brief Creates a new evhtp_request_t * * @param c * * @return evhtp_request_t structure on success, otherwise NULL */ static evhtp_request_t * htp__request_new_(evhtp_connection_t * c) { struct evhtp_request * req; uint8_t error; if (evhtp_unlikely(!(req = htp__calloc_(sizeof(*req), 1)))) { return NULL; } error = 1; req->conn = c; req->htp = c ? c->htp : NULL; req->status = EVHTP_RES_OK; do { if (!(req->buffer_in = evbuffer_new())) { break; } if (!(req->buffer_out = evbuffer_new())) { break; } if (!(req->headers_in = htp__malloc_(sizeof(*req->headers_in)))) { break; } if (!(req->headers_out = htp__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_safe_free(req, htp__request_free_); return req; } /* htp__request_new_ */ /** * @brief Starts the parser for the connection associated with the parser struct * * @param p * @return 0 on success, -1 on fail */ static int htp__request_parse_start_(htparser * p) { evhtp_connection_t * c; if (p == NULL) { return 0; } if (!(c = htparser_get_userdata(p))) { return 0; } if (evhtp_unlikely(c->type == evhtp_type_client)) { return 0; } if (c->flags & EVHTP_CONN_FLAG_PAUSED) { return -1; } if (c->request) { if (c->request->flags & EVHTP_REQ_FLAG_FINISHED) { evhtp_safe_free(c->request, htp__request_free_); } else { return -1; } } if (((c->request = htp__request_new_(c))) == NULL) { return -1; } return 0; } /** * @brief parses http request arguments * * @see htparser_get_userdata * * @param p * @param data * @param len * @return 0 on success, -1 on failure (sets connection cr_status as well) */ static int htp__request_parse_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 * we return a 0 to the parser to continue. */ return 0; } /* 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; uri->fragment = htp__malloc_(fraglen + 1); evhtp_alloc_assert(uri->fragment); 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 (evhtp_unlikely(!uri->query)) { c->cr_status = EVHTP_RES_ERROR; return -1; } uri->query_raw = htp__malloc_(len + 1); evhtp_alloc_assert(uri->query_raw); memcpy(uri->query_raw, data, len); uri->query_raw[len] = '\0'; return 0; } /* htp__request_parse_args_ */ static int htp__request_parse_headers_start_(htparser * p) { evhtp_connection_t * c = htparser_get_userdata(p); if ((c->cr_status = htp__hook_headers_start_(c->request)) != EVHTP_RES_OK) { return -1; } return 0; } static int htp__request_parse_header_key_(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); char * key_s; evhtp_header_t * hdr; key_s = htp__malloc_(len + 1); evhtp_alloc_assert(key_s); if (key_s == NULL) { c->cr_status = EVHTP_RES_FATAL; return -1; } key_s[len] = '\0'; memcpy(key_s, data, len); if ((hdr = evhtp_header_key_add(c->request->headers_in, key_s, 0)) == NULL) { htp__free_(key_s); c->cr_status = EVHTP_RES_FATAL; return -1; } hdr->k_heaped = 1; return 0; } static int htp__request_parse_header_val_(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); char * val_s; evhtp_header_t * header; val_s = htp__malloc_(len + 1); evhtp_alloc_assert(val_s); val_s[len] = '\0'; memcpy(val_s, data, len); if ((header = evhtp_header_val_add(c->request->headers_in, val_s, 0)) == NULL) { evhtp_safe_free(val_s, htp__free_); c->cr_status = EVHTP_RES_FATAL; return -1; } header->v_heaped = 1; if ((c->cr_status = htp__hook_header_(c->request, header)) != EVHTP_RES_OK) { return -1; } return 0; } static inline evhtp_t * htp__request_find_vhost_(evhtp_t * evhtp, const char * name) { evhtp_t * evhtp_vhost; evhtp_alias_t * evhtp_alias; TAILQ_FOREACH(evhtp_vhost, &evhtp->vhosts, next_vhost) { if (evhtp_unlikely(evhtp_vhost->server_name == NULL)) { continue; } if (htp__glob_match_(evhtp_vhost->server_name, strlen(evhtp_vhost->server_name), name, strlen(name)) == 1) { return evhtp_vhost; } TAILQ_FOREACH(evhtp_alias, &evhtp_vhost->aliases, next) { if (evhtp_alias->alias == NULL) { continue; } if (htp__glob_match_(evhtp_alias->alias, strlen(evhtp_alias->alias), name, strlen(name)) == 1) { return evhtp_vhost; } } } return NULL; } static inline int htp__request_set_callbacks_(evhtp_request_t * request) { evhtp_t * evhtp; evhtp_connection_t * conn; evhtp_uri_t * uri; evhtp_path_t * path; evhtp_hooks_t * hooks; evhtp_callback_t * callback; evhtp_callback_cb cb; void * cbarg; if (request == NULL) { return -1; } if ((evhtp = request->htp) == NULL) { return -1; } if ((conn = request->conn) == NULL) { return -1; } if ((uri = request->uri) == NULL) { return -1; } if ((path = uri->path) == NULL) { return -1; } hooks = NULL; callback = NULL; cb = NULL; cbarg = NULL; if ((callback = htp__callback_find_(evhtp->callbacks, path->full, &path->matched_soff, &path->matched_eoff))) { /* matched a callback using both path and file (/a/b/c/d) */ cb = callback->cb; cbarg = callback->cbarg; hooks = callback->hooks; } else if ((callback = htp__callback_find_(evhtp->callbacks, path->path, &path->matched_soff, &path->matched_eoff))) { /* matched a callback using *just* the path (/a/b/c/) */ cb = callback->cb; cbarg = callback->cbarg; hooks = callback->hooks; } else { /* no callbacks found for either case, use defaults */ cb = evhtp->defaults.cb; cbarg = evhtp->defaults.cbarg; path->matched_soff = 0; path->matched_eoff = (unsigned int)strlen(path->full); } if (path->match_start == NULL) { path->match_start = htp__calloc_(strlen(path->full) + 1, 1); evhtp_alloc_assert(path->match_start); } if (path->match_end == NULL) { path->match_end = htp__calloc_(strlen(path->full) + 1, 1); evhtp_alloc_assert(path->match_end); } if (path->matched_soff != UINT_MAX /*ONIG_REGION_NOTPOS*/) { if (path->matched_eoff - path->matched_soff) { memcpy(path->match_start, (void *)(path->full + path->matched_soff), path->matched_eoff - path->matched_soff); } else { memcpy(path->match_start, (void *)(path->full + path->matched_soff), strlen((const char *)(path->full + path->matched_soff))); } memcpy(path->match_end, (void *)(path->full + path->matched_eoff), strlen(path->full) - path->matched_eoff); } if (hooks != NULL) { if (request->hooks == NULL) { request->hooks = htp__malloc_(sizeof(evhtp_hooks_t)); evhtp_alloc_assert(request->hooks); } memcpy(request->hooks, hooks, sizeof(evhtp_hooks_t)); } request->cb = cb; request->cbarg = cbarg; return 0; } /* htp__request_set_callbacks_ */ static int htp__request_parse_hostname_(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); evhtp_t * evhtp; evhtp_t * evhtp_vhost; #ifndef EVHTP_DISABLE_SSL if ((c->flags & EVHTP_CONN_FLAG_VHOST_VIA_SNI) && c->ssl != NULL) { /* use the SNI set hostname instead of the header hostname */ const char * host; host = SSL_get_servername(c->ssl, TLSEXT_NAMETYPE_host_name); if ((c->cr_status = htp__hook_hostname_(c->request, host)) != EVHTP_RES_OK) { return -1; } return 0; } #endif evhtp = c->htp; /* since this is called after htp__request_parse_path_(), which already * setup callbacks for the URI, we must now attempt to find callbacks which * are specific to this host. */ htp__lock_(evhtp); { if ((evhtp_vhost = htp__request_find_vhost_(evhtp, data))) { htp__lock_(evhtp_vhost); { /* if we found a match for the host, we must set the htp * variables for both the connection and the request. */ c->htp = evhtp_vhost; c->request->htp = evhtp_vhost; htp__request_set_callbacks_(c->request); } htp__unlock_(evhtp_vhost); } } htp__unlock_(evhtp); if ((c->cr_status = htp__hook_hostname_(c->request, data)) != EVHTP_RES_OK) { return -1; } return 0; } /* htp__request_parse_hostname_ */ static int htp__require_uri_(evhtp_connection_t * c) { if (c != NULL && c->request != NULL) { if (c->request->uri == NULL) { return htp__uri_new_(&c->request->uri); } return 0; } return -1; } static int htp__request_parse_host_(htparser * p, const char * data, size_t len) { evhtp_connection_t * c; evhtp_authority_t * authority; if (evhtp_unlikely(p == NULL)) { return -1; } c = htparser_get_userdata(p); /* all null checks are done in require_uri_, * no need to check twice */ if (htp__require_uri_(c) == -1) { return -1; } authority = c->request->uri->authority; authority->hostname = htp__malloc_(len + 1); evhtp_alloc_assert(authority->hostname); if (authority->hostname == NULL) { c->cr_status = EVHTP_RES_FATAL; return -1; } memcpy(authority->hostname, data, len); authority->hostname[len] = '\0'; return 0; } static int htp__request_parse_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 (htp__require_uri_(c) == -1) { return -1; } authority = c->request->uri->authority; port = strtoul(data, &endptr, 10); if (endptr - data != len || port > 65535) { c->cr_status = EVHTP_RES_FATAL; return -1; } authority->port = port; return 0; } static int htp__request_parse_path_(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); evhtp_path_t * path; if (evhtp_unlikely(p == NULL || c == NULL)) { return -1; } if (htp__require_uri_(c) == -1) { return -1; } if (htp__path_new_(&path, data, len) == -1) { c->cr_status = EVHTP_RES_FATAL; return -1; } c->request->uri->path = path; c->request->uri->scheme = htparser_get_scheme(p); c->request->method = htparser_get_method(p); htp__lock_(c->htp); { htp__request_set_callbacks_(c->request); } htp__unlock_(c->htp); if ((c->cr_status = htp__hook_path_(c->request, path)) != EVHTP_RES_OK) { return -1; } return 0; } /* htp__request_parse_path_ */ static int htp__request_parse_headers_(htparser * p) { evhtp_connection_t * c; if ((c = htparser_get_userdata(p)) == NULL) { return -1; } /* XXX proto should be set with htparsers on_hdrs_begin hook */ if (htparser_should_keep_alive(p) == 1) { HTP_FLAG_ON(c->request, EVHTP_REQ_FLAG_KEEPALIVE); } c->cr_proto = htp__protocol_(htparser_get_major(p), htparser_get_minor(p)); c->cr_status = htp__hook_headers_(c->request, c->request->headers_in); if (c->cr_status != EVHTP_RES_OK) { return -1; } if (c->type == evhtp_type_server && c->htp->flags & EVHTP_FLAG_ENABLE_100_CONT) { /* only send a 100 continue response if it hasn't been disabled via * evhtp_disable_100_continue. */ if (!evhtp_header_find(c->request->headers_in, "Expect")) { return 0; } evbuffer_add_printf(bufferevent_get_output(c->bev), "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; } static int htp__request_parse_body_(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); struct evbuffer * buf; int res = 0; if (c->max_body_size > 0 && c->body_bytes_read + len >= c->max_body_size) { HTP_FLAG_ON(c, EVHTP_CONN_FLAG_ERROR); c->cr_status = EVHTP_RES_DATA_TOO_LONG; return -1; } if ((buf = c->scratch_buf) == NULL) { return -1; } evbuffer_add(buf, data, len); if ((c->cr_status = htp__hook_body_(c->request, buf)) != EVHTP_RES_OK) { res = -1; } if (evbuffer_get_length(buf)) { evbuffer_add_buffer(c->request->buffer_in, buf); } evbuffer_drain(buf, -1); c->body_bytes_read += len; return res; } static int htp__request_parse_chunk_new_(htparser * p) { evhtp_connection_t * c = htparser_get_userdata(p); if (c == NULL) { return -1; } if ((c->cr_status = htp__hook_chunk_new_(c->request, htparser_get_content_length(p))) != EVHTP_RES_OK) { return -1; } return 0; } static int htp__request_parse_chunk_fini_(htparser * p) { evhtp_connection_t * c = htparser_get_userdata(p); if (c == NULL) { return -1; } if ((c->cr_status = htp__hook_chunk_fini_(c->request)) != EVHTP_RES_OK) { return -1; } return 0; } static int htp__request_parse_chunks_fini_(htparser * p) { evhtp_connection_t * c = htparser_get_userdata(p); if (c == NULL) { return -1; } if ((c->cr_status = htp__hook_chunks_fini_(c->request)) != EVHTP_RES_OK) { return -1; } return 0; } /** * @brief determines if the request body contains the query arguments. * if the query is NULL and the content length of the body has never * been drained, and the content-type is x-www-form-urlencoded, the * function returns 1 * * @param req * * @return 1 if evhtp can use the body as the query arguments, 0 otherwise. */ static int htp__should_parse_query_body_(evhtp_request_t * req) { const char * content_type; if (req == NULL) { return 0; } if (req->uri == NULL || req->uri->query != NULL) { return 0; } if (evhtp_request_content_len(req) == 0) { return 0; } if (evhtp_request_content_len(req) != evbuffer_get_length(req->buffer_in)) { return 0; } content_type = evhtp_kv_find(req->headers_in, "content-type"); if (content_type == NULL) { return 0; } if (strncasecmp(content_type, "application/x-www-form-urlencoded", 33)) { return 0; } return 1; } static int htp__request_parse_fini_(htparser * p) { evhtp_connection_t * c = htparser_get_userdata(p); if (c == NULL) { return -1; } if (c->flags & EVHTP_CONN_FLAG_PAUSED) { return -1; } /* check to see if we should use the body of the request as the query * arguments. * * htp__should_parse_query_body_ does all the proper null checks. */ if (htp__should_parse_query_body_(c->request) == 1) { const char * body; size_t body_len; evhtp_uri_t * uri; struct evbuffer * buf_in; uri = c->request->uri; buf_in = c->request->buffer_in; body_len = evbuffer_get_length(buf_in); body = (const char *)evbuffer_pullup(buf_in, body_len); uri->query_raw = htp__calloc_(body_len + 1, 1); if (evhtp_unlikely(uri->query_raw == NULL)) { c->cr_status = EVHTP_RES_FATAL; return -1; } memcpy(uri->query_raw, body, body_len); uri->query = evhtp_parse_query(body, body_len); } /* * XXX c->request should never be NULL, but we have found some path of * execution where this actually happens. We will check for now, but the bug * path needs to be tracked down. * */ if (c->request && c->request->cb) { (c->request->cb)(c->request, c->request->cbarg); } if (c->flags & EVHTP_CONN_FLAG_PAUSED) { return -1; } return 0; } /* htp__request_parse_fini_ */ static size_t htp__evbuffer_add_iovec_(struct evbuffer * buf, struct evbuffer_iovec * vec, int n_vec) { #if LIBEVENT_VERSION_NUMBER < 0x02010000 int n; size_t res; size_t to_alloc; res = to_alloc = 0; for (n = 0; n < n_vec; n++) { to_alloc += vec[n].iov_len; } evbuffer_expand(buf, to_alloc); for (n = 0; n < n_vec; n++) { evbuffer_add(buf, vec[n].iov_base, vec[n].iov_len); res += vec[n].iov_len; } return res; #else return evbuffer_add_iovec(buf, vec, n_vec); #endif } static int htp__create_headers_(evhtp_header_t * header, void * arg) { struct evbuffer * buf = arg; struct evbuffer_iovec iov[4] = { { header->key, header->klen }, { ": ", 2 }, { header->val, header->vlen }, { "\r\n", 2 } }; htp__evbuffer_add_iovec_(buf, iov, 4); return 0; } static struct evbuffer * htp__create_reply_(evhtp_request_t * request, evhtp_res code) { struct evbuffer * 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]; evhtp_assert(request && request->headers_out && request->buffer_out && request->conn && request->rc_parser); request->status = code; content_type = evhtp_header_find(request->headers_out, "Content-Type"); out_len = evbuffer_get_length(request->buffer_out); if ((buf = request->rc_scratch) == NULL) { request->rc_scratch = evbuffer_new(); evhtp_alloc_assert(request->rc_scratch); } evbuffer_drain(buf, -1); if (htparser_get_multipart(request->rc_parser) == 1) { goto check_proto; } if (out_len && !(request->flags & EVHTP_REQ_FLAG_CHUNKED)) { /* add extra headers (like content-length/type) if not already present */ if (!evhtp_header_find(request->headers_out, "Content-Length")) { /* 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", out_buf, 0, 1)); } } check_proto: /* add the proper keep-alive type headers based on http version */ switch (request->proto) { case EVHTP_PROTO_11: if (!(request->flags & EVHTP_REQ_FLAG_KEEPALIVE)) { /* protocol is HTTP/1.1 but client wanted to close */ evhtp_headers_add_header(request->headers_out, evhtp_header_new("Connection", "close", 0, 0)); } if (!evhtp_header_find(request->headers_out, "Content-Length") && /* cannot have both chunked and content-length */ !(request->flags & EVHTP_REQ_FLAG_CHUNKED)) { evhtp_headers_add_header(request->headers_out, evhtp_header_new("Content-Length", "0", 0, 0)); } break; case EVHTP_PROTO_10: if (request->flags & EVHTP_REQ_FLAG_KEEPALIVE) { /* protocol is HTTP/1.0 and clients wants to keep established */ evhtp_headers_add_header(request->headers_out, evhtp_header_new("Connection", "keep-alive", 0, 0)); } break; default: /* this sometimes happens when a response is made but paused before * the method has been parsed */ htparser_set_major(request->rc_parser, 1); htparser_set_minor(request->rc_parser, 0); break; } /* switch */ if (!content_type) { evhtp_headers_add_header(request->headers_out, evhtp_header_new("Content-Type", "text/plain", 0, 0)); } /* attempt to add the status line into a temporary buffer and then use * evbuffer_add(). Using plain old snprintf() will be faster than * evbuffer_add_printf(). If the snprintf() fails, which it rarely should, * we fallback to using evbuffer_add_printf(). */ major = evhtp_modp_uchartoa(htparser_get_major(request->rc_parser)); minor = evhtp_modp_uchartoa(htparser_get_minor(request->rc_parser)); evhtp_modp_u32toa((uint32_t)code, out_buf); /* create the initial reply status via scatter-gather io (note: this used to * be a formatted write which led to some spurrious performance problems. * This now uses iovec/scatter/gather to create the status reply portion * of the header. */ { const char * status_str = status_code_to_str(code); struct evbuffer_iovec iov[9] = { { "HTTP/1", 5 }, /* data == "HTTP/" */ { (void *)&major, 1 }, /* data == "HTTP/X */ { ".", 1 }, /* data == "HTTP/X." */ { (void *)&minor, 1 }, /* data == "HTTP/X.X" */ { " ", 1 }, /* data == "HTTP/X.X " */ { out_buf, strlen(out_buf) }, /* data = "HTTP/X.X YYY" */ { " ", 1 }, /* data = "HTTP/X.X YYY " */ { (void *)status_str, strlen(status_str) }, /* data = "HTTP/X.X YYY ZZZ" */ { "\r\n", 2 }, /* data = "HTTP/X.X YYY ZZZ\r\n" */ }; htp__evbuffer_add_iovec_(buf, iov, 9); } evhtp_headers_for_each(request->headers_out, htp__create_headers_, buf); evbuffer_add(buf, "\r\n", 2); if (evbuffer_get_length(request->buffer_out)) { evbuffer_add_buffer(buf, request->buffer_out); } return buf; } /* htp__create_reply_ */ /** * @brief callback definitions for request processing from libhtparse */ static htparse_hooks request_psets = { .on_msg_begin = htp__request_parse_start_, .method = NULL, .scheme = NULL, .host = htp__request_parse_host_, .port = htp__request_parse_port_, .path = htp__request_parse_path_, .args = htp__request_parse_args_, .uri = NULL, .on_hdrs_begin = htp__request_parse_headers_start_, .hdr_key = htp__request_parse_header_key_, .hdr_val = htp__request_parse_header_val_, .hostname = htp__request_parse_hostname_, .on_hdrs_complete = htp__request_parse_headers_, .on_new_chunk = htp__request_parse_chunk_new_, .on_chunk_complete = htp__request_parse_chunk_fini_, .on_chunks_complete = htp__request_parse_chunks_fini_, .body = htp__request_parse_body_, .on_msg_complete = htp__request_parse_fini_ }; static void htp__connection_readcb_(struct bufferevent * bev, void * arg) { evhtp_connection_t * c = arg; void * buf; size_t nread; size_t avail; if (evhtp_unlikely(bev == NULL)) { return; } avail = HTP_LEN_INPUT(bev); if (evhtp_unlikely(avail == 0)) { return; } if (c->flags & EVHTP_CONN_FLAG_PAUSED) { log_debug("connection is paused, returning"); return; } if (c->request) { c->cr_status = EVHTP_RES_OK; } buf = evbuffer_pullup(bufferevent_get_input(bev), avail); evhtp_assert(buf != NULL); evhtp_assert(c->parser != NULL); nread = htparser_run(c->parser, &request_psets, (const char *)buf, avail); log_debug("nread = %zu", nread); if (!(c->flags & EVHTP_CONN_FLAG_OWNER)) { /* * someone has taken the ownership of this connection, we still need to * drain the input buffer that had been read up to this point. */ log_debug("EVHTP_CONN_FLAG_OWNER set, removing contexts"); evbuffer_drain(bufferevent_get_input(bev), nread); evhtp_safe_free(c, evhtp_connection_free); return; } if (c->request) { switch (c->cr_status) { case EVHTP_RES_DATA_TOO_LONG: htp__hook_connection_error_(c, -1); evhtp_safe_free(c, evhtp_connection_free); return; default: break; } } evbuffer_drain(bufferevent_get_input(bev), nread); if (c->request && c->cr_status == EVHTP_RES_PAUSE) { log_debug("Pausing connection"); evhtp_request_pause(c->request); } else if (htparser_get_error(c->parser) != htparse_error_none) { log_debug("error %d, freeing connection", htparser_get_error(c->parser)); evhtp_safe_free(c, evhtp_connection_free); } else if (nread < avail) { /* we still have more data to read (piped request probably) */ log_debug("Reading more data via resumption"); evhtp_connection_resume(c); } } /* htp__connection_readcb_ */ static void htp__connection_writecb_(struct bufferevent * bev, void * arg) { evhtp_connection_t * conn; uint64_t keepalive_max; const char * errstr; evhtp_assert(bev != NULL); log_debug("writecb"); if (evhtp_unlikely(arg == NULL)) { log_error("No data associated with the bufferevent %p", bev); evhtp_safe_free(bev, bufferevent_free); return; } errstr = NULL; conn = (evhtp_connection_t *)arg; do { if (evhtp_unlikely(conn->request == NULL)) { errstr = "no request associated with connection"; break; } if (evhtp_unlikely(conn->parser == NULL)) { errstr = "no parser registered with connection"; break; } if (evhtp_likely(conn->type == evhtp_type_server)) { if (evhtp_unlikely(conn->htp == NULL)) { errstr = "no context associated with the server-connection"; break; } keepalive_max = conn->htp->max_keepalive_requests; } else { keepalive_max = 0; } } while (0); if (evhtp_unlikely(errstr != NULL)) { log_error("shutting down connection: %s", errstr); evhtp_safe_free(conn, evhtp_connection_free); return; } /* connection is in a paused state, no further processing yet */ if (conn->flags & EVHTP_CONN_FLAG_PAUSED) { log_debug("is paused"); return; } /* run user-hook for on_write callback before further analysis */ htp__hook_connection_write_(conn); if (conn->flags & EVHTP_CONN_FLAG_WAITING) { log_debug("Disabling WAIT flag"); HTP_FLAG_OFF(conn, EVHTP_CONN_FLAG_WAITING); if (HTP_IS_READING(bev) == false) { log_debug("enabling EV_READ"); bufferevent_enable(bev, EV_READ); } if (HTP_LEN_INPUT(bev)) { log_debug("have input data, will travel"); htp__connection_readcb_(bev, arg); return; } } /* if the connection is not finished, OR there is data ready to output * (can only happen if a user-defined connection_write hook added data * manually, since this is called only when all data has been flushed) * just return and wait. */ if (!(conn->cr_flags & EVHTP_REQ_FLAG_FINISHED) || HTP_LEN_OUTPUT(bev)) { log_debug("not finished"); 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 * keepalive bit to 0, thus closing the connection. */ if (keepalive_max > 0) { if (++conn->num_requests >= keepalive_max) { HTP_FLAG_OFF(conn->request, EVHTP_REQ_FLAG_KEEPALIVE); } } if (conn->cr_flags & EVHTP_REQ_FLAG_KEEPALIVE) { htp_type type; log_debug("keep-alive on"); /* free up the current request, set it to NULL, making * way for the next request. */ evhtp_safe_free(conn->request, htp__request_free_); /* since the request is keep-alive, assure that the connection * is aware of the same. */ HTP_FLAG_ON(conn, EVHTP_CONN_FLAG_KEEPALIVE); conn->body_bytes_read = 0; if (conn->type == evhtp_type_server) { if (conn->htp->parent != NULL && !(conn->flags & EVHTP_CONN_FLAG_VHOST_VIA_SNI)) { /* this request was served by a virtual host evhtp_t structure * which was *NOT* found via SSL SNI lookup. In this case we want to * reset our connections evhtp_t structure back to the original so * that subsequent requests can have a different 'Host' header. */ conn->htp = conn->htp->parent; } } switch (conn->type) { case evhtp_type_client: type = htp_type_response; break; case evhtp_type_server: type = htp_type_request; break; default: log_error("Unknown connection type"); evhtp_safe_free(conn, evhtp_connection_free); return; } htparser_init(conn->parser, type); htparser_set_userdata(conn->parser, conn); return; } else { log_debug("goodbye connection"); evhtp_safe_free(conn, evhtp_connection_free); return; } return; } /* htp__connection_writecb_ */ static void htp__connection_eventcb_(struct bufferevent * bev, short events, void * arg) { evhtp_connection_t * c = arg; log_debug("%p %p eventcb %s%s%s%s", arg, (void *)bev, events & BEV_EVENT_CONNECTED ? "connected" : "", events & BEV_EVENT_ERROR ? "error" : "", events & BEV_EVENT_TIMEOUT ? "timeout" : "", events & BEV_EVENT_EOF ? "eof" : ""); if (c->hooks && c->hooks->on_event) { (c->hooks->on_event)(c, events, c->hooks->on_event_arg); } if ((events & BEV_EVENT_CONNECTED)) { log_debug("CONNECTED"); if (evhtp_likely(c->type == evhtp_type_client)) { HTP_FLAG_ON(c, EVHTP_CONN_FLAG_CONNECTED); bufferevent_setcb(bev, htp__connection_readcb_, htp__connection_writecb_, htp__connection_eventcb_, c); } return; } #ifndef EVHTP_DISABLE_SSL if (c->ssl && !(events & BEV_EVENT_EOF)) { #ifdef EVHTP_DEBUG unsigned long sslerr; while ((sslerr = bufferevent_get_openssl_error(bev))) { log_error("SSL ERROR %lu:%i:%s:%i:%s:%i:%s", sslerr, ERR_GET_REASON(sslerr), ERR_reason_error_string(sslerr), ERR_GET_LIB(sslerr), ERR_lib_error_string(sslerr), ERR_GET_FUNC(sslerr), ERR_func_error_string(sslerr)); } #endif /* XXX need to do better error handling for SSL specific errors */ HTP_FLAG_ON(c, EVHTP_CONN_FLAG_ERROR); if (c->request) { HTP_FLAG_ON(c->request, EVHTP_REQ_FLAG_ERROR); } } #endif if (events == (BEV_EVENT_EOF | BEV_EVENT_READING)) { log_debug("EOF | READING"); if (errno == EAGAIN) { /* libevent will sometimes recv again when it's not actually ready, * this results in a 0 return value, and errno will be set to EAGAIN * (try again). This does not mean there is a hard socket error, but * simply needs to be read again. * * but libevent will disable the read side of the bufferevent * anyway, so we must re-enable it. */ log_debug("errno EAGAIN"); if (HTP_IS_READING(bev) == false) { bufferevent_enable(bev, EV_READ); } errno = 0; return; } } /* set the error mask */ HTP_FLAG_ON(c, EVHTP_CONN_FLAG_ERROR); /* unset connected flag */ HTP_FLAG_OFF(c, EVHTP_CONN_FLAG_CONNECTED); htp__hook_connection_error_(c, events); if (c->flags & EVHTP_CONN_FLAG_PAUSED) { /* we are currently paused, so we don't want to free just yet, let's * wait till the next loop. */ HTP_FLAG_ON(c, EVHTP_CONN_FLAG_FREE_CONN); } else { evhtp_safe_free(c, evhtp_connection_free); } } /* htp__connection_eventcb_ */ static void htp__connection_resumecb_(int fd, short events, void * arg) { evhtp_connection_t * c = arg; log_debug("resumecb"); /* unset the pause flag */ HTP_FLAG_OFF(c, EVHTP_CONN_FLAG_PAUSED); if (c->request) { log_debug("cr status = OK %d", c->cr_status); c->cr_status = EVHTP_RES_OK; } if (c->flags & EVHTP_CONN_FLAG_FREE_CONN) { log_debug("flags == FREE_CONN"); evhtp_safe_free(c, evhtp_connection_free); return; } /* 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 (HTP_LEN_OUTPUT(c->bev)) { log_debug("SET WAITING"); HTP_FLAG_ON(c, EVHTP_CONN_FLAG_WAITING); if (HTP_IS_WRITING(c->bev) == false) { log_debug("ENABLING EV_WRITE"); bufferevent_enable(c->bev, EV_WRITE); } } else { log_debug("SET READING"); if (HTP_IS_READING(c->bev) == false) { log_debug("ENABLING EV_READ"); bufferevent_enable(c->bev, EV_READ | EV_WRITE); } if (HTP_LEN_INPUT(c->bev)) { log_debug("calling readcb directly"); htp__connection_readcb_(c->bev, c); } } } /* htp__connection_resumecb_ */ static int htp__run_pre_accept_(evhtp_t * htp, evhtp_connection_t * conn) { void * args; evhtp_res res; if (evhtp_likely(htp->defaults.pre_accept == NULL)) { return 0; } args = htp->defaults.pre_accept_cbarg; res = htp->defaults.pre_accept(conn, args); if (res != EVHTP_RES_OK) { return -1; } return 0; } static int htp__connection_accept_(struct event_base * evbase, evhtp_connection_t * connection) { struct timeval * c_recv_timeo; struct timeval * c_send_timeo; if (htp__run_pre_accept_(connection->htp, connection) < 0) { evutil_closesocket(connection->sock); return -1; } #ifndef EVHTP_DISABLE_SSL if (connection->htp->ssl_ctx != NULL) { connection->ssl = SSL_new(connection->htp->ssl_ctx); connection->bev = bufferevent_openssl_socket_new(evbase, connection->sock, connection->ssl, BUFFEREVENT_SSL_ACCEPTING, connection->htp->bev_flags); SSL_set_app_data(connection->ssl, connection); goto end; } #endif connection->bev = bufferevent_socket_new(evbase, connection->sock, connection->htp->bev_flags); log_debug("enter sock=%d\n", connection->sock); #ifndef EVHTP_DISABLE_SSL end: #endif if (connection->recv_timeo.tv_sec || connection->recv_timeo.tv_usec) { c_recv_timeo = &connection->recv_timeo; } else if (connection->htp->recv_timeo.tv_sec || connection->htp->recv_timeo.tv_usec) { c_recv_timeo = &connection->htp->recv_timeo; } else { c_recv_timeo = NULL; } if (connection->send_timeo.tv_sec || connection->send_timeo.tv_usec) { c_send_timeo = &connection->send_timeo; } else if (connection->htp->send_timeo.tv_sec || connection->htp->send_timeo.tv_usec) { c_send_timeo = &connection->htp->send_timeo; } else { c_send_timeo = NULL; } evhtp_connection_set_timeouts(connection, c_recv_timeo, c_send_timeo); connection->resume_ev = event_new(evbase, -1, EV_READ | EV_PERSIST, htp__connection_resumecb_, connection); event_add(connection->resume_ev, NULL); bufferevent_setcb(connection->bev, htp__connection_readcb_, htp__connection_writecb_, htp__connection_eventcb_, connection); bufferevent_enable(connection->bev, EV_READ); return 0; } /* htp__connection_accept_ */ static void htp__default_request_cb_(evhtp_request_t * request, void * arg) { evhtp_headers_add_header(request->headers_out, evhtp_header_new("Content-Length", "0", 0, 0)); evhtp_send_reply(request, EVHTP_RES_NOTFOUND); } static evhtp_connection_t * htp__connection_new_(evhtp_t * htp, evutil_socket_t sock, evhtp_type type) { evhtp_connection_t * connection; htp_type ptype; switch (type) { case evhtp_type_client: ptype = htp_type_response; break; case evhtp_type_server: ptype = htp_type_request; break; default: return NULL; } connection = htp__calloc_(sizeof(*connection), 1); if (evhtp_unlikely(connection == NULL)) { return NULL; } connection->scratch_buf = evbuffer_new(); if (evhtp_unlikely(connection->scratch_buf == NULL)) { evhtp_safe_free(connection->scratch_buf, htp__free_); return NULL; } if (htp != NULL) { connection->max_body_size = htp->max_body_size; } connection->flags = EVHTP_CONN_FLAG_OWNER; connection->sock = sock; connection->htp = htp; connection->type = type; connection->parser = htparser_new(); if (evhtp_unlikely(connection->parser == NULL)) { evhtp_safe_free(connection, evhtp_connection_free); return NULL; } htparser_init(connection->parser, ptype); htparser_set_userdata(connection->parser, connection); return connection; } /* htp__connection_new_ */ #ifdef LIBEVENT_HAS_SHUTDOWN #ifndef EVHTP_DISABLE_SSL static void htp__shutdown_eventcb_(struct bufferevent * bev, short events, void * arg) { } #endif #endif static int htp__run_post_accept_(evhtp_t * htp, evhtp_connection_t * connection) { void * args; evhtp_res res; if (evhtp_likely(htp->defaults.post_accept == NULL)) { return 0; } args = htp->defaults.post_accept_cbarg; res = htp->defaults.post_accept(connection, args); if (res != EVHTP_RES_OK) { return -1; } return 0; } #ifndef EVHTP_DISABLE_EVTHR static void htp__run_in_thread_(evthr_t * thr, void * arg, void * shared) { evhtp_t * htp = shared; evhtp_connection_t * connection = arg; connection->evbase = evthr_get_base(thr); connection->thread = thr; if (htp__connection_accept_(connection->evbase, connection) < 0) { evhtp_safe_free(connection, evhtp_connection_free); return; } if (htp__run_post_accept_(htp, connection) < 0) { evhtp_safe_free(connection, evhtp_connection_free); return; } } #endif static void htp__accept_cb_(struct evconnlistener * serv, int fd, struct sockaddr * s, int sl, void * arg) { evhtp_t * htp = arg; evhtp_connection_t * connection; evhtp_assert(htp && serv && serv && s); connection = htp__connection_new_(htp, fd, evhtp_type_server); if (evhtp_unlikely(connection == NULL)) { return; } log_debug("fd = %d, conn = %p", fd, connection); connection->saddr = htp__malloc_(sl); if (evhtp_unlikely(connection->saddr == NULL)) { /* should probably start doing error callbacks */ evhtp_safe_free(connection, evhtp_connection_free); return; } memcpy(connection->saddr, s, sl); #ifndef EVHTP_DISABLE_EVTHR if (htp->thr_pool != NULL) { if (evthr_pool_defer(htp->thr_pool, htp__run_in_thread_, connection) != EVTHR_RES_OK) { evutil_closesocket(connection->sock); evhtp_safe_free(connection, evhtp_connection_free); return; } return; } #endif connection->evbase = htp->evbase; if (htp__connection_accept_(htp->evbase, connection) == -1) { evhtp_safe_free(connection, evhtp_connection_free); return; } if (htp__run_post_accept_(htp, connection) == -1) { evhtp_safe_free(connection, evhtp_connection_free); return; } } /* htp__accept_cb_ */ #ifndef EVHTP_DISABLE_SSL #ifndef EVHTP_DISABLE_EVTHR #ifndef WIN32 #define _HTP_tid (unsigned long)pthread_self() #else #define _HTP_tid pthread_self().p #endif #if OPENSSL_VERSION_NUMBER < 0x10000000L static unsigned long htp__ssl_get_thread_id_(void) { return _HTP_tid; } #else static void htp__ssl_get_thread_id_(CRYPTO_THREADID * id) { CRYPTO_THREADID_set_numeric(id, _HTP_tid); } #endif static void htp__ssl_thread_lock_(int mode, int type, const char * file, int line) { if (type < ssl_num_locks) { if (mode & CRYPTO_LOCK) { pthread_mutex_lock(&(ssl_locks[type])); } else { pthread_mutex_unlock(&(ssl_locks[type])); } } } #endif static void htp__ssl_delete_scache_ent_(evhtp_ssl_ctx_t * ctx, evhtp_ssl_sess_t * sess) { evhtp_t * htp; evhtp_ssl_cfg_t * cfg; evhtp_ssl_data_t * sid; unsigned int slen; htp = (evhtp_t *)SSL_CTX_get_app_data(ctx); cfg = htp->ssl_cfg; sid = (evhtp_ssl_data_t *)SSL_SESSION_get_id(sess, &slen); if (cfg->scache_del) { (cfg->scache_del)(htp, sid, slen); } } static int htp__ssl_add_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_sess_t * sess) { evhtp_connection_t * connection; evhtp_ssl_cfg_t * cfg; evhtp_ssl_data_t * sid; unsigned int slen; connection = (evhtp_connection_t *)SSL_get_app_data(ssl); if (connection->htp == NULL) { return 0; /* We cannot get the ssl_cfg */ } cfg = connection->htp->ssl_cfg; sid = (evhtp_ssl_data_t *)SSL_SESSION_get_id(sess, &slen); SSL_set_timeout(sess, cfg->scache_timeout); if (cfg->scache_add) { return (cfg->scache_add)(connection, sid, slen, sess); } return 0; } static evhtp_ssl_sess_t * htp__ssl_get_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_data_t * sid, int sid_len, int * copy) { evhtp_connection_t * connection; evhtp_ssl_cfg_t * cfg; evhtp_ssl_sess_t * sess; connection = (evhtp_connection_t * )SSL_get_app_data(ssl); if (connection->htp == NULL) { return NULL; /* We have no way of getting ssl_cfg */ } cfg = connection->htp->ssl_cfg; sess = NULL; if (cfg->scache_get) { sess = (cfg->scache_get)(connection, sid, sid_len); } *copy = 0; return sess; } static int htp__ssl_servername_(evhtp_ssl_t * ssl, int * unused, void * arg) { const char * sname; evhtp_connection_t * connection; evhtp_t * evhtp; evhtp_t * evhtp_vhost; if (evhtp_unlikely(ssl == NULL)) { return SSL_TLSEXT_ERR_NOACK; } if (!(sname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) { return SSL_TLSEXT_ERR_NOACK; } if (!(connection = SSL_get_app_data(ssl))) { return SSL_TLSEXT_ERR_NOACK; } if (!(evhtp = connection->htp)) { return SSL_TLSEXT_ERR_NOACK; } if ((evhtp_vhost = htp__request_find_vhost_(evhtp, sname))) { SSL_CTX * ctx = SSL_get_SSL_CTX(ssl); connection->htp = evhtp_vhost; HTP_FLAG_ON(connection, EVHTP_CONN_FLAG_VHOST_VIA_SNI); SSL_set_SSL_CTX(ssl, evhtp_vhost->ssl_ctx); SSL_set_options(ssl, SSL_CTX_get_options(ctx)); if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) || (SSL_num_renegotiations(ssl) == 0)) { SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ctx), SSL_CTX_get_verify_callback(ctx)); } return SSL_TLSEXT_ERR_OK; } return SSL_TLSEXT_ERR_NOACK; } /* htp__ssl_servername_ */ #endif /* * PUBLIC FUNCTIONS */ htp_method evhtp_request_get_method(evhtp_request_t * r) { evhtp_assert(r != NULL); evhtp_assert(r->conn != NULL); evhtp_assert(r->conn->parser != NULL); return htparser_get_method(r->conn->parser); } void evhtp_connection_pause(evhtp_connection_t * c) { evhtp_assert(c != NULL); if (c->flags & EVHTP_CONN_FLAG_PAUSED) { log_debug("connection is already paused"); return; } log_debug("setting PAUSED flag"); HTP_FLAG_ON(c, EVHTP_CONN_FLAG_PAUSED); if (HTP_IS_READING(c->bev) == true) { log_debug("disabling EV_READ"); bufferevent_disable(c->bev, EV_READ); } return; } void evhtp_connection_resume(evhtp_connection_t * c) { evhtp_assert(c != NULL); if (!(c->flags & EVHTP_CONN_FLAG_PAUSED)) { log_error("ODDITY, resuming when not paused?!?"); return; } HTP_FLAG_OFF(c, EVHTP_CONN_FLAG_PAUSED); log_debug("resume"); event_active(c->resume_ev, EV_WRITE, 1); return; } void evhtp_request_pause(evhtp_request_t * request) { evhtp_assert(request != NULL); request->status = EVHTP_RES_PAUSE; evhtp_connection_pause(request->conn); } void evhtp_request_resume(evhtp_request_t * request) { evhtp_assert(request != NULL); evhtp_connection_resume(request->conn); } evhtp_header_t * evhtp_header_key_add(evhtp_headers_t * headers, const char * key, char key_alloc) { evhtp_header_t * header; if (!(header = evhtp_header_new(key, NULL, key_alloc, 0))) { return NULL; } evhtp_headers_add_header(headers, header); return header; } evhtp_header_t * evhtp_header_val_add(evhtp_headers_t * headers, const char * val, char val_alloc) { evhtp_header_t * header; if (evhtp_unlikely(headers == NULL || val == NULL)) { return NULL; } if (!(header = TAILQ_LAST(headers, evhtp_kvs))) { return NULL; } if (header->val != NULL) { return NULL; } header->vlen = strlen(val); if (val_alloc == 1) { header->val = htp__malloc_(header->vlen + 1); evhtp_alloc_assert(header->val); header->val[header->vlen] = '\0'; memcpy(header->val, val, header->vlen); } else { header->val = (char *)val; } header->v_heaped = val_alloc; return header; } evhtp_kvs_t * evhtp_kvs_new(void) { evhtp_kvs_t * kvs; kvs = htp__malloc_(sizeof(*kvs)); if (evhtp_unlikely(kvs == NULL)) { return NULL; } TAILQ_INIT(kvs); return kvs; } evhtp_kv_t * evhtp_kv_new(const char * key, const char * val, char key_alloc, char val_alloc) { evhtp_kv_t * kv; kv = htp__malloc_(sizeof(*kv)); if (evhtp_unlikely(kv == NULL)) { return NULL; } kv->k_heaped = key_alloc; kv->v_heaped = val_alloc; kv->klen = 0; kv->vlen = 0; kv->key = NULL; kv->val = NULL; if (key != NULL) { kv->klen = strlen(key); if (key_alloc == 1) { char * s; if (!(s = htp__malloc_(kv->klen + 1))) { evhtp_safe_free(kv, htp__free_); return NULL; } memcpy(s, key, kv->klen); s[kv->klen] = '\0'; kv->key = s; } else { kv->key = (char *)key; } } if (val != NULL) { kv->vlen = strlen(val); if (val_alloc == 1) { char * s = htp__malloc_(kv->vlen + 1); if (evhtp_unlikely(s == NULL)) { evhtp_safe_free(kv, evhtp_kv_free); return NULL; } s[kv->vlen] = '\0'; memcpy(s, val, kv->vlen); kv->val = s; } else { kv->val = (char *)val; } } return kv; } /* evhtp_kv_new */ void evhtp_kv_free(evhtp_kv_t * kv) { if (evhtp_unlikely(kv == NULL)) { return; } if (kv->k_heaped == 1) { evhtp_safe_free(kv->key, htp__free_); } if (kv->v_heaped == 1) { evhtp_safe_free(kv->val, htp__free_); } evhtp_safe_free(kv, htp__free_); } void evhtp_kv_rm_and_free(evhtp_kvs_t * kvs, evhtp_kv_t * kv) { if (evhtp_unlikely(kvs == NULL || kv == NULL)) { return; } TAILQ_REMOVE(kvs, kv, next); evhtp_safe_free(kv, evhtp_kv_free); } void evhtp_kvs_free(evhtp_kvs_t * kvs) { evhtp_kv_t * kv; evhtp_kv_t * save; if (evhtp_unlikely(kvs == NULL)) { return; } kv = NULL; save = NULL; for (kv = TAILQ_FIRST(kvs); kv != NULL; kv = save) { save = TAILQ_NEXT(kv, next); TAILQ_REMOVE(kvs, kv, next); evhtp_safe_free(kv, evhtp_kv_free); } evhtp_safe_free(kvs, htp__free_); } int evhtp_kvs_for_each(evhtp_kvs_t * kvs, evhtp_kvs_iterator cb, void * arg) { evhtp_kv_t * kv; if (kvs == NULL || cb == NULL) { return -1; } TAILQ_FOREACH(kv, kvs, next) { int res; if ((res = cb(kv, arg))) { return res; } } return 0; } const char * evhtp_kv_find(evhtp_kvs_t * kvs, const char * key) { evhtp_kv_t * kv; if (evhtp_unlikely(kvs == NULL || key == NULL)) { return NULL; } TAILQ_FOREACH(kv, kvs, next) { if (strcasecmp(kv->key, key) == 0) { return kv->val; } } return NULL; } const char * evhtp_header_find(evhtp_headers_t * headers, const char * key) { return evhtp_kv_find(headers, key); } void evhtp_headers_add_header(evhtp_headers_t * headers, evhtp_header_t * header) { return evhtp_kvs_add_kv(headers, header); } evhtp_header_t * evhtp_header_new(const char * key, const char * val, char kalloc, char valloc) { return evhtp_kv_new(key, val, kalloc, valloc); } evhtp_kv_t * evhtp_kvs_find_kv(evhtp_kvs_t * kvs, const char * key) { evhtp_kv_t * kv; if (evhtp_unlikely(kvs == NULL || key == NULL)) { return NULL; } TAILQ_FOREACH(kv, kvs, next) { if (strcasecmp(kv->key, key) == 0) { return kv; } } return NULL; } void evhtp_kvs_add_kv(evhtp_kvs_t * kvs, evhtp_kv_t * kv) { if (evhtp_unlikely(kvs == NULL || kv == NULL)) { return; } TAILQ_INSERT_TAIL(kvs, kv, next); } void evhtp_kvs_add_kvs(evhtp_kvs_t * dst, evhtp_kvs_t * src) { evhtp_kv_t * kv; if (dst == NULL || src == NULL) { return; } TAILQ_FOREACH(kv, src, next) { evhtp_kvs_add_kv(dst, evhtp_kv_new(kv->key, kv->val, kv->k_heaped, kv->v_heaped)); } } typedef enum { s_query_start = 0, s_query_separator, s_query_key, s_query_val, s_query_key_hex_1, s_query_key_hex_2, s_query_val_hex_1, s_query_val_hex_2, s_query_done } query_parser_state; static inline int evhtp_is_hex_query_char(unsigned char ch) { switch (ch) { case 'a': case 'A': case 'b': case 'B': case 'c': case 'C': case 'd': case 'D': case 'e': case 'E': case 'f': case 'F': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return 1; default: return 0; } /* switch */ } enum unscape_state { unscape_state_start = 0, unscape_state_hex1, unscape_state_hex2 }; int evhtp_unescape_string(unsigned char ** out, unsigned char * str, size_t str_len) { unsigned char * optr; unsigned char * sptr; unsigned char d; unsigned char ch; unsigned char c; size_t i; enum unscape_state state; state = unscape_state_start; optr = *out; sptr = str; d = 0; for (i = 0; i < str_len; i++) { ch = *sptr++; switch (state) { case unscape_state_start: if (ch == '%') { state = unscape_state_hex1; break; } *optr++ = ch; break; case unscape_state_hex1: if (ch >= '0' && ch <= '9') { d = (unsigned char)(ch - '0'); state = unscape_state_hex2; break; } c = (unsigned char)(ch | 0x20); if (c >= 'a' && c <= 'f') { d = (unsigned char)(c - 'a' + 10); state = unscape_state_hex2; break; } state = unscape_state_start; *optr++ = ch; break; case unscape_state_hex2: state = unscape_state_start; if (ch >= '0' && ch <= '9') { ch = (unsigned char)((d << 4) + ch - '0'); *optr++ = ch; break; } c = (unsigned char)(ch | 0x20); if (c >= 'a' && c <= 'f') { ch = (unsigned char)((d << 4) + c - 'a' + 10); *optr++ = ch; break; } break; } /* switch */ } *out = optr; return 0; } /* evhtp_unescape_string */ evhtp_query_t * evhtp_parse_query_wflags(const char * query, const size_t len, const int flags) { evhtp_query_t * query_args; 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; key_buf = htp__malloc_(len + 1); if (evhtp_unlikely(key_buf == NULL)) { evhtp_safe_free(query_args, evhtp_query_free); return NULL; } val_buf = htp__malloc_(len + 1); if (evhtp_unlikely(val_buf == NULL)) { evhtp_safe_free(query_args, evhtp_query_free); return NULL; } #endif for (i = 0; i < len; i++) { ch = query[i]; if (key_idx >= len || val_idx >= len) { goto error; } switch (state) { case s_query_start: key_idx = 0; val_idx = 0; key_buf[0] = '\0'; val_buf[0] = '\0'; state = s_query_key; /* Fall through. */ case s_query_key: switch (ch) { case '=': state = s_query_val; break; case '%': key_buf[key_idx++] = ch; 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)) { /* not hex, so we treat as a normal key */ if ((key_idx + 2) >= len) { /* we need to insert \%, but not enough space */ goto error; } key_buf[key_idx - 1] = '%'; key_buf[key_idx++] = ch; key_buf[key_idx] = '\0'; state = s_query_key; break; } key_buf[key_idx++] = ch; key_buf[key_idx] = '\0'; state = s_query_key_hex_2; break; case s_query_key_hex_2: if (!evhtp_is_hex_query_char(ch)) { goto error; } key_buf[key_idx++] = ch; key_buf[key_idx] = '\0'; state = s_query_key; break; 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)); key_idx = 0; val_idx = 0; key_buf[0] = '\0'; val_buf[0] = '\0'; state = s_query_key; break; case '%': val_buf[val_idx++] = ch; val_buf[val_idx] = '\0'; if (!(flags & EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX)) { state = s_query_val_hex_1; } break; default: val_buf[val_idx++] = ch; val_buf[val_idx] = '\0'; break; } /* switch */ break; case s_query_val_hex_1: if (!evhtp_is_hex_query_char(ch)) { /* not really a hex val */ if ((val_idx + 2) >= len) { /* we need to insert \%, but not enough space */ goto error; } if (val_idx == 0) { goto error; } val_buf[val_idx - 1] = '%'; val_buf[val_idx++] = ch; val_buf[val_idx] = '\0'; state = s_query_val; break; } val_buf[val_idx++] = ch; val_buf[val_idx] = '\0'; state = s_query_val_hex_2; break; case s_query_val_hex_2: if (!evhtp_is_hex_query_char(ch)) { goto error; } val_buf[val_idx++] = ch; val_buf[val_idx] = '\0'; state = s_query_val; break; default: /* bad state */ goto error; } /* switch */ } 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); } #ifndef EVHTP_HAS_C99 evhtp_safe_free(key_buf, htp__free_); evhtp_safe_free(val_buf, htp__free_); #endif return query_args; error: #ifndef EVHTP_HAS_C99 evhtp_safe_free(key_buf, htp__free_); evhtp_safe_free(val_buf, htp__free_); #endif evhtp_safe_free(query_args, evhtp_query_free); 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; struct evbuffer * reply_buf; c = evhtp_request_get_connection(request); if (!(reply_buf = htp__create_reply_(request, code))) { evhtp_safe_free(c, evhtp_connection_free); return; } bufferevent_write_buffer(c->bev, reply_buf); evbuffer_drain(reply_buf, -1); } void evhtp_send_reply_body(evhtp_request_t * request, struct evbuffer * buf) { evhtp_connection_t * c; c = request->conn; bufferevent_write_buffer(c->bev, buf); } void evhtp_send_reply_end(evhtp_request_t * request) { HTP_FLAG_ON(request, EVHTP_REQ_FLAG_FINISHED); } void evhtp_send_reply(evhtp_request_t * request, evhtp_res code) { evhtp_connection_t * c; struct evbuffer * reply_buf; struct bufferevent * bev; c = request->conn; log_debug("set finished flag"); HTP_FLAG_ON(request, EVHTP_REQ_FLAG_FINISHED); if (!(reply_buf = htp__create_reply_(request, code))) { evhtp_safe_free(request->conn, evhtp_connection_free); return; } bev = c->bev; log_debug("writing to bev %p", bev); bufferevent_write_buffer(bev, reply_buf); evbuffer_drain(reply_buf, -1); } int evhtp_response_needs_body(const evhtp_res code, const htp_method method) { return code != EVHTP_RES_NOCONTENT && code != EVHTP_RES_NOTMOD && (code < 100 || code >= 200) && method != htp_method_HEAD; } void evhtp_send_reply_chunk_start(evhtp_request_t * request, evhtp_res code) { evhtp_header_t * content_len; if (evhtp_response_needs_body(code, request->method)) { content_len = evhtp_headers_find_header(request->headers_out, "Content-Length"); switch (request->proto) { case EVHTP_PROTO_11: /* * prefer HTTP/1.1 chunked encoding to closing the connection; * note RFC 2616 section 4.4 forbids it with Content-Length: * and it's not necessary then anyway. */ evhtp_kv_rm_and_free(request->headers_out, content_len); HTP_FLAG_ON(request, EVHTP_REQ_FLAG_CHUNKED); break; case EVHTP_PROTO_10: /* * HTTP/1.0 can be chunked as long as the Content-Length header * is set to 0 */ evhtp_kv_rm_and_free(request->headers_out, content_len); HTP_FLAG_ON(request, EVHTP_REQ_FLAG_CHUNKED); break; default: HTP_FLAG_OFF(request, EVHTP_REQ_FLAG_CHUNKED); break; } /* switch */ } else { HTP_FLAG_OFF(request, EVHTP_REQ_FLAG_CHUNKED); } if (request->flags & EVHTP_REQ_FLAG_CHUNKED) { evhtp_headers_add_header(request->headers_out, evhtp_header_new("Transfer-Encoding", "chunked", 0, 0)); /* * if data already exists on the output buffer, we automagically convert * it to the first chunk. */ if (evbuffer_get_length(request->buffer_out) > 0) { char lstr[128]; int sres; sres = snprintf(lstr, sizeof(lstr), "%x\r\n", (unsigned)evbuffer_get_length(request->buffer_out)); if (sres >= sizeof(lstr) || sres < 0) { /* overflow condition, shouldn't ever get here, but lets * terminate the connection asap */ goto end; } evbuffer_prepend(request->buffer_out, lstr, strlen(lstr)); evbuffer_add(request->buffer_out, "\r\n", 2); } } end: evhtp_send_reply_start(request, code); } /* evhtp_send_reply_chunk_start */ void evhtp_send_reply_chunk(evhtp_request_t * request, struct evbuffer * buf) { struct evbuffer * output; if (evbuffer_get_length(buf) == 0) { return; } output = bufferevent_get_output(request->conn->bev); if (request->flags & EVHTP_REQ_FLAG_CHUNKED) { evbuffer_add_printf(output, "%x\r\n", (unsigned)evbuffer_get_length(buf)); } evhtp_send_reply_body(request, buf); if (request->flags & EVHTP_REQ_FLAG_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->flags & EVHTP_REQ_FLAG_CHUNKED) { evbuffer_add(bufferevent_get_output(evhtp_request_get_bev(request)), "0\r\n\r\n", 5); } evhtp_send_reply_end(request); } void evhtp_unbind_socket(evhtp_t * htp) { if (htp == NULL || htp->server == NULL) { return; } evhtp_safe_free(htp->server, evconnlistener_free); } static int htp__serv_setsockopts_(evhtp_t * htp, evutil_socket_t sock) { int on = 1; if (htp == NULL || sock == -1) { log_error("htp = %p && sock = %d", htp, sock); return -1; } if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)) == -1) { return -1; } if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) == -1) { return -1; } #if defined(SO_REUSEPORT) if (htp->flags & EVHTP_FLAG_ENABLE_REUSEPORT) { if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)) == -1) { if (errno != EOPNOTSUPP) { log_error("SO_REUSEPORT error"); return -1; } log_warn("SO_REUSEPORT NOT SUPPORTED"); } } #endif #if defined(TCP_NODELAY) if (htp->flags & EVHTP_FLAG_ENABLE_NODELAY) { if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on)) == -1) { if (errno != EOPNOTSUPP) { log_error("TCP_NODELAY error"); return -1; } log_warn("NODELAY NOT SUPPORTED"); } } #endif #if defined(TCP_DEFER_ACCEPT) if (htp->flags & EVHTP_FLAG_ENABLE_DEFER_ACCEPT) { if (setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, (void *)&on, sizeof(on)) == -1) { if (errno != EOPNOTSUPP) { log_error("DEFER_ACCEPT error"); return -1; } log_warn("DEFER_ACCEPT NOT SUPPORTED"); } } #endif return 0; } /* htp__setsockopts_ */ int evhtp_accept_socket(evhtp_t * htp, evutil_socket_t sock, int backlog) { int err = 1; if (htp == NULL || sock == -1) { log_error("htp = %p && sock = %d", htp, sock); return -1; } do { htp->server = evconnlistener_new(htp->evbase, htp__accept_cb_, htp, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, backlog, sock); if (htp->server == NULL) { break; } #ifndef EVHTP_DISABLE_SSL if (htp->ssl_ctx != NULL) { /* if ssl is enabled and we have virtual hosts, set our servername * callback. We do this here because we want to make sure that this gets * set after all potential virtualhosts have been set, not just after * ssl_init. */ if (TAILQ_FIRST(&htp->vhosts) != NULL) { SSL_CTX_set_tlsext_servername_callback(htp->ssl_ctx, htp__ssl_servername_); } } #endif err = 0; } while (0); if (err == 1) { if (htp->server != NULL) { evhtp_safe_free(htp->server, evconnlistener_free); } return -1; } return 0; } /* evhtp_accept_socket */ int evhtp_bind_sockaddr(evhtp_t * htp, struct sockaddr * sa, size_t sin_len, int backlog) { evutil_socket_t fd = -1; int on = 1; int error = 1; if (htp == NULL) { log_error("NULL param passed"); return -1; } /* XXX: API's should not set signals */ #ifndef WIN32 signal(SIGPIPE, SIG_IGN); #endif do { if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) { log_error("couldn't create socket"); return -1; } evutil_make_socket_closeonexec(fd); evutil_make_socket_nonblocking(fd); if (htp__serv_setsockopts_(htp, fd) == -1) { break; } if (sa->sa_family == AF_INET6) { if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) { break; } } if (bind(fd, sa, sin_len) == -1) { break; } error = 0; } while (0); if (error == 1) { if (fd != -1) { evutil_closesocket(fd); } return -1; } if (evhtp_accept_socket(htp, fd, backlog) == -1) { /* accept_socket() does not close the descriptor * on error, but this function does. */ evutil_closesocket(fd); return -1; } return 0; } /* evhtp_bind_sockaddr */ int evhtp_bind_socket(evhtp_t * htp, const char * baddr, uint16_t port, int backlog) { #ifndef NO_SYS_UN struct sockaddr_un sockun = { 0 }; #endif struct sockaddr * sa; struct sockaddr_in6 sin6 = { 0 }; struct sockaddr_in sin = { 0 }; size_t sin_len; if (!strncmp(baddr, "ipv6:", 5)) { baddr += 5; sin_len = sizeof(struct sockaddr_in6); sin6.sin6_port = htons(port); sin6.sin6_family = AF_INET6; evutil_inet_pton(AF_INET6, baddr, &sin6.sin6_addr); sa = (struct sockaddr *)&sin6; } else if (!strncmp(baddr, "unix:", 5)) { #ifndef NO_SYS_UN baddr += 5; if (strlen(baddr) >= sizeof(sockun.sun_path)) { return -1; } sin_len = sizeof(struct sockaddr_un); sockun.sun_family = AF_UNIX; strncpy(sockun.sun_path, baddr, strlen(baddr)); sa = (struct sockaddr *)&sockun; #else return -1; #endif } else { if (!strncmp(baddr, "ipv4:", 5)) { baddr += 5; } sin_len = sizeof(struct sockaddr_in); sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = inet_addr(baddr); sa = (struct sockaddr *)&sin; } return evhtp_bind_sockaddr(htp, sa, sin_len, backlog); } /* evhtp_bind_socket */ void evhtp_callbacks_free(evhtp_callbacks_t * callbacks) { evhtp_callback_t * callback; evhtp_callback_t * tmp; if (callbacks == NULL) { return; } TAILQ_FOREACH_SAFE(callback, callbacks, next, tmp) { TAILQ_REMOVE(callbacks, callback, next); evhtp_safe_free(callback, evhtp_callback_free); } evhtp_safe_free(callbacks, htp__free_); } evhtp_callback_t * evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_cb cb, void * arg) { evhtp_callback_t * hcb; hcb = htp__calloc_(sizeof(*hcb), 1); if (evhtp_unlikely(hcb == NULL)) { evhtp_safe_free(hcb, htp__free_); return NULL; } hcb->type = type; hcb->cb = cb; hcb->cbarg = arg; hcb->len = strlen(path); switch (type) { case evhtp_callback_type_hash: hcb->val.path = htp__strdup_(path); if (evhtp_unlikely(hcb->val.path == NULL)) { evhtp_safe_free(hcb, evhtp_callback_free); return NULL; } break; #ifndef EVHTP_DISABLE_REGEX case evhtp_callback_type_regex: hcb->val.regex = htp__malloc_(sizeof(regex_t)); if (evhtp_unlikely(hcb->val.regex == NULL)) { evhtp_safe_free(hcb, evhtp_callback_free); return NULL; } if (regcomp(hcb->val.regex, (char *)path, REG_EXTENDED) != 0) { evhtp_safe_free(hcb->val.regex, htp__free_); evhtp_safe_free(hcb, htp__free_); return NULL; } break; #endif case evhtp_callback_type_glob: hcb->val.glob = htp__strdup_(path); if (evhtp_unlikely(hcb->val.glob == NULL)) { evhtp_safe_free(hcb, evhtp_callback_free); return NULL; } break; default: evhtp_safe_free(hcb, htp__free_); return NULL; } /* switch */ return hcb; } /* evhtp_callback_new */ void evhtp_callback_free(evhtp_callback_t * callback) { if (callback == NULL) { return; } switch (callback->type) { case evhtp_callback_type_hash: evhtp_safe_free(callback->val.path, htp__free_); break; case evhtp_callback_type_glob: evhtp_safe_free(callback->val.glob, htp__free_); break; #ifndef EVHTP_DISABLE_REGEX case evhtp_callback_type_regex: evhtp_safe_free(callback->val.regex, htp__free_); break; #endif } if (callback->hooks) { evhtp_safe_free(callback->hooks, htp__free_); } evhtp_safe_free(callback, htp__free_); return; } int evhtp_callbacks_add_callback(evhtp_callbacks_t * cbs, evhtp_callback_t * cb) { TAILQ_INSERT_TAIL(cbs, cb, next); return 0; } static int htp__set_hook_(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void * arg) { if (*hooks == NULL) { if (!(*hooks = htp__calloc_(sizeof(evhtp_hooks_t), 1))) { return -1; } } switch (type) { case evhtp_hook_on_headers_start: (*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; break; case evhtp_hook_on_headers: (*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; break; case evhtp_hook_on_read: (*hooks)->on_read = (evhtp_hook_read_cb)cb; (*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; break; case evhtp_hook_on_connection_fini: (*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; break; case evhtp_hook_on_new_chunk: (*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; break; case evhtp_hook_on_chunks_complete: (*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; break; case evhtp_hook_on_write: (*hooks)->on_write = (evhtp_hook_write_cb)cb; (*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 */ return 0; } /* htp__set_hook_ */ static int htp__unset_hook_(evhtp_hooks_t ** hooks, evhtp_hook_type type) { return htp__set_hook_(hooks, type, NULL, NULL); } int evhtp_callback_unset_hook(evhtp_callback_t * callback, evhtp_hook_type type) { return htp__unset_hook_(&callback->hooks, type); } int evhtp_request_unset_hook(evhtp_request_t * req, evhtp_hook_type type) { return htp__unset_hook_(&req->hooks, type); } int evhtp_connection_unset_hook(evhtp_connection_t * conn, evhtp_hook_type type) { return htp__unset_hook_(&conn->hooks, type); } int evhtp_callback_set_hook(evhtp_callback_t * callback, evhtp_hook_type type, evhtp_hook cb, void * arg) { return htp__set_hook_(&callback->hooks, type, cb, arg); } int evhtp_request_set_hook(evhtp_request_t * req, evhtp_hook_type type, evhtp_hook cb, void * arg) { return htp__set_hook_(&req->hooks, type, cb, arg); } int evhtp_connection_set_hook(evhtp_connection_t * conn, evhtp_hook_type type, evhtp_hook cb, void * arg) { return htp__set_hook_(&conn->hooks, type, cb, arg); } int evhtp_unset_all_hooks(evhtp_hooks_t ** hooks) { int i; struct { enum evhtp_hook_type type; } hooklist_[] = { { evhtp_hook_on_header }, { evhtp_hook_on_headers }, { evhtp_hook_on_path }, { evhtp_hook_on_read }, { evhtp_hook_on_request_fini }, { evhtp_hook_on_connection_fini }, { evhtp_hook_on_new_chunk }, { evhtp_hook_on_chunk_complete }, { evhtp_hook_on_chunks_complete }, { evhtp_hook_on_headers_start }, { evhtp_hook_on_error }, { evhtp_hook_on_hostname }, { evhtp_hook_on_write }, { evhtp_hook_on_event }, { evhtp_hook_on_conn_error }, { evhtp_hook__max } }; if (hooks == NULL) { return -1; } for (i = 0; hooklist_[i].type != evhtp_hook__max; i++) { if (htp__unset_hook_(hooks, hooklist_[i].type) == -1) { return -1; } } return 0; } evhtp_hooks_t * evhtp_connection_get_hooks(evhtp_connection_t * c) { if (evhtp_unlikely(c == NULL)) { return NULL; } return c->hooks; } /** * @brief returns request hooks * * @param r * @return */ evhtp_hooks_t * evhtp_request_get_hooks(evhtp_request_t * r) { if (evhtp_unlikely(r == NULL)) { return NULL; } return r->hooks; } /** * @brief returns callback hooks * * @param cb * @return */ evhtp_hooks_t * evhtp_callback_get_hooks(evhtp_callback_t * cb) { return cb->hooks; } evhtp_callback_t * evhtp_set_cb(evhtp_t * htp, const char * path, evhtp_callback_cb cb, void * arg) { evhtp_callback_t * hcb; htp__lock_(htp); if (htp->callbacks == NULL) { if (!(htp->callbacks = htp__calloc_(sizeof(evhtp_callbacks_t), 1))) { htp__unlock_(htp); return NULL; } TAILQ_INIT(htp->callbacks); } if (!(hcb = evhtp_callback_new(path, evhtp_callback_type_hash, cb, arg))) { htp__unlock_(htp); return NULL; } if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) { evhtp_safe_free(hcb, evhtp_callback_free); htp__unlock_(htp); return NULL; } htp__unlock_(htp); return hcb; } evhtp_callback_t * evhtp_get_cb(evhtp_t * htp, const char * path) { evhtp_callback_t * callback; evhtp_assert(htp != NULL); if (evhtp_unlikely(htp->callbacks == NULL)) { return NULL; } TAILQ_FOREACH(callback, htp->callbacks, next) { if (strcmp(callback->val.path, path) == 0) { return callback; } } return NULL; } #ifndef EVHTP_DISABLE_EVTHR static void htp__thread_init_(evthr_t * thr, void * arg) { evhtp_t * htp = (evhtp_t *)arg; if (htp->thread_init_cb) { htp->thread_init_cb(htp, thr, htp->thread_cbarg); } } static void htp__thread_exit_(evthr_t * thr, void * arg) { evhtp_t * htp = (evhtp_t *)arg; if (htp->thread_exit_cb) { htp->thread_exit_cb(htp, thr, htp->thread_cbarg); } } static int htp__use_threads_(evhtp_t * htp, evhtp_thread_init_cb init_cb, evhtp_thread_exit_cb exit_cb, int nthreads, void * arg) { if (htp == NULL) { return -1; } htp->thread_cbarg = arg; htp->thread_init_cb = init_cb; htp->thread_exit_cb = exit_cb; #ifndef EVHTP_DISABLE_SSL evhtp_ssl_use_threads(); #endif if (!(htp->thr_pool = evthr_pool_wexit_new(nthreads, htp__thread_init_, htp__thread_exit_, htp))) { return -1; } evthr_pool_start(htp->thr_pool); return 0; } int evhtp_use_threads(evhtp_t * htp, evhtp_thread_init_cb init_cb, int nthreads, void * arg) { return htp__use_threads_(htp, init_cb, NULL, nthreads, arg); } int evhtp_use_threads_wexit(evhtp_t * htp, evhtp_thread_init_cb init_cb, evhtp_thread_exit_cb exit_cb, int nthreads, void * arg) { return htp__use_threads_(htp, init_cb, exit_cb, nthreads, arg); } #endif #ifndef EVHTP_DISABLE_EVTHR int evhtp_use_callback_locks(evhtp_t * htp) { if (htp == NULL) { return -1; } if (!(htp->lock = htp__malloc_(sizeof(pthread_mutex_t)))) { return -1; } return pthread_mutex_init(htp->lock, NULL); } #endif #ifndef EVHTP_DISABLE_REGEX evhtp_callback_t * evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg) { evhtp_callback_t * hcb; htp__lock_(htp); if (htp->callbacks == NULL) { if (!(htp->callbacks = htp__calloc_(sizeof(evhtp_callbacks_t), 1))) { htp__unlock_(htp); return NULL; } TAILQ_INIT(htp->callbacks); } if (!(hcb = evhtp_callback_new(pattern, evhtp_callback_type_regex, cb, arg))) { htp__unlock_(htp); return NULL; } if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) { evhtp_safe_free(hcb, evhtp_callback_free); htp__unlock_(htp); return NULL; } htp__unlock_(htp); return hcb; } #endif evhtp_callback_t * evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg) { evhtp_callback_t * hcb; htp__lock_(htp); if (htp->callbacks == NULL) { if (!(htp->callbacks = htp__calloc_(sizeof(evhtp_callbacks_t), 1))) { htp__unlock_(htp); return NULL; } TAILQ_INIT(htp->callbacks); } if (!(hcb = evhtp_callback_new(pattern, evhtp_callback_type_glob, cb, arg))) { htp__unlock_(htp); return NULL; } if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) { evhtp_safe_free(hcb, evhtp_callback_free); htp__unlock_(htp); return NULL; } htp__unlock_(htp); return hcb; } void evhtp_set_gencb(evhtp_t * htp, evhtp_callback_cb cb, void * arg) { htp->defaults.cb = cb; htp->defaults.cbarg = arg; } void evhtp_set_pre_accept_cb(evhtp_t * htp, evhtp_pre_accept_cb cb, void * arg) { htp->defaults.pre_accept = cb; htp->defaults.pre_accept_cbarg = arg; } void evhtp_set_post_accept_cb(evhtp_t * htp, evhtp_post_accept_cb cb, void * arg) { htp->defaults.post_accept = cb; htp->defaults.post_accept_cbarg = arg; } #ifndef EVHTP_DISABLE_SSL #ifndef EVHTP_DISABLE_EVTHR int evhtp_ssl_use_threads(void) { int i; if (ssl_locks_initialized == 1) { return 0; } ssl_locks_initialized = 1; ssl_num_locks = CRYPTO_num_locks(); if ((ssl_locks = htp__calloc_(ssl_num_locks, sizeof(evhtp_mutex_t))) == NULL) { return -1; } for (i = 0; i < ssl_num_locks; i++) { pthread_mutex_init(&(ssl_locks[i]), NULL); } #if OPENSSL_VERSION_NUMBER < 0x10000000L CRYPTO_set_id_callback(htp__ssl_get_thread_id_); #else CRYPTO_THREADID_set_callback(htp__ssl_get_thread_id_); #endif CRYPTO_set_locking_callback(htp__ssl_thread_lock_); return 0; } #endif int evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) { long cache_mode; unsigned char c; if (cfg == NULL || htp == NULL || cfg->pemfile == NULL) { return -1; } #if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_library_init(); ERR_load_crypto_strings(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); #else /* unnecessary in OpenSSL 1.1.0 */ /* * if (OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT, NULL) == 0) { * log_error("OPENSSL_init_ssl"); * return -1; * } * * if (OPENSSL_init_crypto( * OPENSSL_INIT_ADD_ALL_CIPHERS | * OPENSSL_INIT_ADD_ALL_DIGESTS | * OPENSSL_INIT_LOAD_CONFIG, NULL) == 0) { * log_error("OPENSSL_init_crypto"); * return -1; * } */ #endif if (RAND_poll() != 1) { log_error("RAND_poll"); return -1; } if (RAND_bytes(&c, 1) != 1) { log_error("RAND_bytes"); return -1; } #if OPENSSL_VERSION_NUMBER < 0x10000000L STACK_OF(SSL_COMP) * comp_methods = SSL_COMP_get_compression_methods(); sk_SSL_COMP_zero(comp_methods); #endif htp->ssl_cfg = cfg; #if OPENSSL_VERSION_NUMBER < 0x10100000L htp->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); #else htp->ssl_ctx = SSL_CTX_new(TLS_server_method()); #endif evhtp_alloc_assert(htp->ssl_ctx); #if OPENSSL_VERSION_NUMBER >= 0x10000000L SSL_CTX_set_options(htp->ssl_ctx, SSL_MODE_RELEASE_BUFFERS | SSL_OP_NO_COMPRESSION); SSL_CTX_set_timeout(htp->ssl_ctx, cfg->ssl_ctx_timeout); #endif SSL_CTX_set_options(htp->ssl_ctx, cfg->ssl_opts); #ifndef OPENSSL_NO_ECDH if (cfg->named_curve != NULL) { EC_KEY * ecdh = NULL; int nid = 0; nid = OBJ_sn2nid(cfg->named_curve); if (nid == 0) { log_error("ECDH initialization failed: unknown curve %s", cfg->named_curve); } ecdh = EC_KEY_new_by_curve_name(nid); if (ecdh == NULL) { log_error("ECDH initialization failed for curve %s", cfg->named_curve); } SSL_CTX_set_tmp_ecdh(htp->ssl_ctx, ecdh); EC_KEY_free(ecdh); } #endif /* OPENSSL_NO_ECDH */ #ifndef OPENSSL_NO_DH if (cfg->dhparams != NULL) { FILE * fh; DH * dh; fh = fopen(cfg->dhparams, "r"); if (fh != NULL) { dh = PEM_read_DHparams(fh, NULL, NULL, NULL); if (dh != NULL) { SSL_CTX_set_tmp_dh(htp->ssl_ctx, dh); DH_free(dh); } else { log_error("DH initialization failed: unable to parse file %s", cfg->dhparams); } fclose(fh); } else { log_error("DH initialization failed: unable to open file %s", cfg->dhparams); } } #endif /* OPENSSL_NO_DH */ if (cfg->ciphers != NULL) { if (SSL_CTX_set_cipher_list(htp->ssl_ctx, cfg->ciphers) == 0) { log_error("set_cipher_list"); return -1; } } SSL_CTX_load_verify_locations(htp->ssl_ctx, cfg->cafile, cfg->capath); X509_STORE_set_flags(SSL_CTX_get_cert_store(htp->ssl_ctx), cfg->store_flags); SSL_CTX_set_verify(htp->ssl_ctx, cfg->verify_peer, cfg->x509_verify_cb); if (cfg->x509_chk_issued_cb != NULL) { #if OPENSSL_VERSION_NUMBER < 0x10100000L htp->ssl_ctx->cert_store->check_issued = cfg->x509_chk_issued_cb; #else X509_STORE_set_check_issued(SSL_CTX_get_cert_store(htp->ssl_ctx), cfg->x509_chk_issued_cb); #endif } if (cfg->verify_depth) { SSL_CTX_set_verify_depth(htp->ssl_ctx, cfg->verify_depth); } switch (cfg->scache_type) { case evhtp_ssl_scache_type_disabled: cache_mode = SSL_SESS_CACHE_OFF; break; default: cache_mode = SSL_SESS_CACHE_SERVER; break; } /* switch */ SSL_CTX_use_certificate_chain_file(htp->ssl_ctx, cfg->pemfile); char * const key = cfg->privfile ? cfg->privfile : cfg->pemfile; if (cfg->decrypt_cb != NULL) { EVP_PKEY * pkey = cfg->decrypt_cb(key); if (pkey == NULL) { return -1; } SSL_CTX_use_PrivateKey(htp->ssl_ctx, pkey); /*cleanup */ EVP_PKEY_free(pkey); } else { SSL_CTX_use_PrivateKey_file(htp->ssl_ctx, key, SSL_FILETYPE_PEM); } SSL_CTX_set_session_id_context(htp->ssl_ctx, (void *)&session_id_context, sizeof(session_id_context)); SSL_CTX_set_app_data(htp->ssl_ctx, htp); SSL_CTX_set_session_cache_mode(htp->ssl_ctx, cache_mode); if (cache_mode != SSL_SESS_CACHE_OFF) { SSL_CTX_sess_set_cache_size(htp->ssl_ctx, cfg->scache_size ? cfg->scache_size : 1024); if (cfg->scache_type == evhtp_ssl_scache_type_builtin || cfg->scache_type == evhtp_ssl_scache_type_user) { SSL_CTX_sess_set_new_cb(htp->ssl_ctx, htp__ssl_add_scache_ent_); SSL_CTX_sess_set_get_cb(htp->ssl_ctx, htp__ssl_get_scache_ent_); SSL_CTX_sess_set_remove_cb(htp->ssl_ctx, htp__ssl_delete_scache_ent_); if (cfg->scache_init) { cfg->args = (cfg->scache_init)(htp); } } } return 0; } /* evhtp_use_ssl */ #endif struct bufferevent * evhtp_connection_get_bev(evhtp_connection_t * connection) { return connection->bev; } struct bufferevent * evhtp_connection_take_ownership(evhtp_connection_t * connection) { struct bufferevent * bev = evhtp_connection_get_bev(connection); if (connection->hooks) { evhtp_unset_all_hooks(&connection->hooks); } if (connection->request && connection->request->hooks) { evhtp_unset_all_hooks(&connection->request->hooks); } evhtp_connection_set_bev(connection, NULL); /* relinquish ownership of this connection, unset * the ownership flag. */ HTP_FLAG_OFF(connection, EVHTP_CONN_FLAG_OWNER); bufferevent_disable(bev, EV_READ); bufferevent_setcb(bev, NULL, NULL, NULL, NULL); return bev; } struct bufferevent * evhtp_request_get_bev(evhtp_request_t * request) { return evhtp_connection_get_bev(request->conn); } struct bufferevent * evhtp_request_take_ownership(evhtp_request_t * request) { return evhtp_connection_take_ownership(request->conn); } void evhtp_connection_set_bev(evhtp_connection_t * conn, struct bufferevent * bev) { conn->bev = bev; } void evhtp_request_set_bev(evhtp_request_t * request, struct bufferevent * bev) { evhtp_connection_set_bev(request->conn, bev); } void evhtp_request_set_keepalive(evhtp_request_t * request, int val) { if (val) { HTP_FLAG_ON(request, EVHTP_REQ_FLAG_KEEPALIVE); } } evhtp_connection_t * evhtp_request_get_connection(evhtp_request_t * request) { return request->conn; } evhtp_proto evhtp_request_get_proto(evhtp_request_t * request) { return request->proto; } evhtp_res evhtp_request_get_status_code(evhtp_request_t * request) { return request->status; } const char * evhtp_request_get_status_code_str(evhtp_request_t * request) { return status_code_to_str(request->status); } inline void evhtp_connection_set_timeouts(evhtp_connection_t * c, const struct timeval * rtimeo, const struct timeval * wtimeo) { if (evhtp_unlikely(c == NULL)) { return; } bufferevent_set_timeouts(c->bev, rtimeo, wtimeo); } void evhtp_connection_set_max_body_size(evhtp_connection_t * c, uint64_t len) { if (len == 0) { c->max_body_size = c->htp->max_body_size; } else { c->max_body_size = len; } } void evhtp_request_set_max_body_size(evhtp_request_t * req, uint64_t len) { evhtp_connection_set_max_body_size(req->conn, len); } void evhtp_connection_free(evhtp_connection_t * connection) { if (evhtp_unlikely(connection == NULL)) { return; } htp__hook_connection_fini_(connection); evhtp_safe_free(connection->request, htp__request_free_); evhtp_safe_free(connection->parser, htp__free_); evhtp_safe_free(connection->hooks, htp__free_); evhtp_safe_free(connection->saddr, htp__free_); evhtp_safe_free(connection->scratch_buf, evbuffer_free); if (connection->resume_ev) { evhtp_safe_free(connection->resume_ev, event_free); } if (connection->bev) { #ifdef LIBEVENT_HAS_SHUTDOWN bufferevent_shutdown(connection->bev, htp__shutdown_eventcb_); #else #ifndef EVHTP_DISABLE_SSL if (connection->ssl != NULL) { SSL_set_shutdown(connection->ssl, SSL_RECEIVED_SHUTDOWN); SSL_shutdown(connection->ssl); } #endif evhtp_safe_free(connection->bev, bufferevent_free); #endif } evhtp_safe_free(connection, htp__free_); } /* evhtp_connection_free */ void evhtp_request_free(evhtp_request_t * request) { if (request == NULL) { return; } evhtp_safe_free(request, htp__request_free_); } void evhtp_set_timeouts(evhtp_t * htp, const struct timeval * r_timeo, const struct timeval * w_timeo) { if (r_timeo != NULL) { htp->recv_timeo = *r_timeo; } if (w_timeo != NULL) { htp->send_timeo = *w_timeo; } } void evhtp_set_max_keepalive_requests(evhtp_t * htp, uint64_t num) { htp->max_keepalive_requests = num; } void evhtp_set_bev_flags(evhtp_t * htp, int flags) { htp->bev_flags = flags; } void evhtp_set_max_body_size(evhtp_t * htp, uint64_t len) { htp->max_body_size = len; } void evhtp_disable_100_continue(evhtp_t * htp) { HTP_FLAG_OFF(htp, EVHTP_FLAG_ENABLE_100_CONT); } void evhtp_set_parser_flags(evhtp_t * htp, int flags) { htp->parser_flags = flags; } #define HTP_FLAG_FNGEN(NAME, TYPE) void \ evhtp ## NAME ## _enable_flag(TYPE v, int flag) { \ HTP_FLAG_ON(v, flag); \ } \ \ void \ evhtp ## NAME ## _disable_flag(TYPE v, int flag) { \ HTP_FLAG_OFF(v, flag); \ } \ \ int \ evhtp ## NAME ## _get_flags(TYPE v) { \ if (v) { \ return v->flags; \ } \ return -1; \ } HTP_FLAG_FNGEN(, evhtp_t *); HTP_FLAG_FNGEN(_connection, evhtp_connection_t *); HTP_FLAG_FNGEN(_request, evhtp_request_t *); int evhtp_add_alias(evhtp_t * evhtp, const char * name) { evhtp_alias_t * alias; if (evhtp_unlikely(evhtp == NULL || name == NULL)) { return -1; } if (!(alias = htp__calloc_(sizeof(evhtp_alias_t), 1))) { return -1; } log_debug("Adding %s to aliases", name); alias->alias = htp__strdup_(name); if (evhtp_unlikely(alias->alias == NULL)) { evhtp_safe_free(alias, htp__free_); return -1; } TAILQ_INSERT_TAIL(&evhtp->aliases, alias, next); return 0; } int evhtp_add_aliases(evhtp_t * htp, const char * name, ...) { va_list argp; if (evhtp_add_alias(htp, name) == -1) { return -1; } va_start(argp, name); { const char * p; while ((p = va_arg(argp, const char *)) != NULL) { if (evhtp_add_alias(htp, p) == -1) { log_error("Unable to add %s alias", p); va_end(argp); return -1; } } } va_end(argp); return 0; } int evhtp_add_vhost(evhtp_t * evhtp, const char * name, evhtp_t * vhost) { if (evhtp == NULL || name == NULL || vhost == NULL) { return -1; } if (TAILQ_FIRST(&vhost->vhosts) != NULL) { /* vhosts cannot have secondary vhosts defined */ return -1; } vhost->server_name = htp__strdup_(name); if (evhtp_unlikely(vhost->server_name == NULL)) { return -1; } /* set the parent of this vhost so when the request has been completely * serviced, the vhost can be reset to the original evhtp structure. * * This allows for a keep-alive connection to make multiple requests with * different Host: values. */ vhost->parent = evhtp; /* inherit various flags from the parent evhtp structure */ vhost->bev_flags = evhtp->bev_flags; vhost->max_body_size = evhtp->max_body_size; vhost->max_keepalive_requests = evhtp->max_keepalive_requests; vhost->recv_timeo = evhtp->recv_timeo; vhost->send_timeo = evhtp->send_timeo; TAILQ_INSERT_TAIL(&evhtp->vhosts, vhost, next_vhost); return 0; } /** * @brief Allocates new evhtp_t structure * * @param [OUT] out - double ptr to evhtp_t structure. * @param [IN] evbase - event_base structure * @param [IN] arg - anonymous argument * * @return 0 on success, -1 on failure */ static int evhtp__new_(evhtp_t ** out, struct event_base * evbase, void * arg) { evhtp_t * htp; if (evhtp_unlikely(evbase == NULL)) { return -1; } *out = NULL; if ((htp = htp__calloc_(1, sizeof(*htp))) == NULL) { return -1; } htp->arg = arg; htp->evbase = evbase; htp->flags = EVHTP_FLAG_DEFAULTS; htp->bev_flags = BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS; /* default to lenient argument parsing */ htp->parser_flags = EVHTP_PARSE_QUERY_FLAG_DEFAULT; TAILQ_INIT(&htp->vhosts); TAILQ_INIT(&htp->aliases); /* note that we pass the htp context to the callback, * not the user supplied arguments. That is stored * within the context itself. */ evhtp_set_gencb(htp, htp__default_request_cb_, (void *)htp); *out = htp; return 0; } evhtp_t * evhtp_new(struct event_base * evbase, void * arg) { evhtp_t * htp; if (evhtp__new_(&htp, evbase, arg) == -1) { return NULL; } return htp; } void evhtp_free(evhtp_t * evhtp) { evhtp_alias_t * evhtp_alias, * tmp; if (evhtp == NULL) { return; } #ifndef EVHTP_DISABLE_EVTHR if (evhtp->thr_pool) { evthr_pool_stop(evhtp->thr_pool); evthr_pool_free(evhtp->thr_pool); } #endif #ifndef EVHTP_DISABLE_SSL if (evhtp->ssl_ctx) { evhtp_safe_free(evhtp->ssl_ctx, SSL_CTX_free); } #endif if (evhtp->server_name) { evhtp_safe_free(evhtp->server_name, htp__free_); } if (evhtp->callbacks) { evhtp_safe_free(evhtp->callbacks, evhtp_callbacks_free); } TAILQ_FOREACH_SAFE(evhtp_alias, &evhtp->aliases, next, tmp) { if (evhtp_alias->alias != NULL) { evhtp_safe_free(evhtp_alias->alias, htp__free_); } TAILQ_REMOVE(&evhtp->aliases, evhtp_alias, next); evhtp_safe_free(evhtp_alias, htp__free_); } evhtp_safe_free(evhtp, htp__free_); } /* evhtp_free */ /***************************************************************** * client request functions * *****************************************************************/ evhtp_connection_t * evhtp_connection_new(struct event_base * evbase, const char * addr, uint16_t port) { return evhtp_connection_new_dns(evbase, NULL, addr, port); } evhtp_connection_t * evhtp_connection_new_dns(struct event_base * evbase, struct evdns_base * dns_base, const char * addr, uint16_t port) { evhtp_connection_t * conn; int err; log_debug("Enter"); evhtp_assert(evbase != NULL); if (!(conn = htp__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_safe_free(conn, evhtp_connection_free); return NULL; } bufferevent_enable(conn->bev, EV_READ); bufferevent_setcb(conn->bev, NULL, NULL, htp__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_safe_free(conn, evhtp_connection_free); 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 #define ssl_sk_new_ bufferevent_openssl_socket_new #define ssl_sk_connect_ bufferevent_socket_connect evhtp_connection_t * evhtp_connection_ssl_new(struct event_base * evbase, const char * addr, uint16_t port, evhtp_ssl_ctx_t * ctx) { evhtp_connection_t * conn; struct sockaddr_in sin; const char * errstr; if (evbase == NULL) { return NULL; } if (!(conn = htp__connection_new_(NULL, -1, evhtp_type_client))) { return NULL; } conn->evbase = evbase; errstr = NULL; do { if ((conn->ssl = SSL_new(ctx)) == NULL) { errstr = "unable to allocate SSL context"; break; } if ((conn->bev = ssl_sk_new_(evbase, -1, conn->ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE)) == NULL) { errstr = "unable to allocate bev context"; break; } if (bufferevent_enable(conn->bev, EV_READ) == -1) { errstr = "unable to enable reading"; break; } bufferevent_setcb(conn->bev, NULL, NULL, htp__connection_eventcb_, conn); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(addr); sin.sin_port = htons(port); if (ssl_sk_connect_(conn->bev, (struct sockaddr *)&sin, sizeof(sin)) == -1) { errstr = "sk_connect_ failure"; break; } } while (0); if (errstr != NULL) { log_error("%s", errstr); evhtp_safe_free(conn, evhtp_connection_free); return NULL; } return conn; } /* evhtp_connection_ssl_new */ #endif evhtp_request_t * evhtp_request_new(evhtp_callback_cb cb, void * arg) { evhtp_request_t * r; r = htp__request_new_(NULL); evhtp_alloc_assert(r); r->cb = cb; r->cbarg = arg; r->proto = EVHTP_PROTO_11; return r; } int evhtp_make_request(evhtp_connection_t * c, evhtp_request_t * r, htp_method meth, const char * uri) { struct evbuffer * obuf; char * proto; obuf = bufferevent_get_output(c->bev); r->conn = c; r->method = meth; c->request = r; switch (r->proto) { case EVHTP_PROTO_10: proto = "1.0"; break; case EVHTP_PROTO_11: default: proto = "1.1"; break; } evbuffer_add_printf(obuf, "%s %s HTTP/%s\r\n", htparser_get_methodstr_m(meth), uri, proto); if (evbuffer_get_length(r->buffer_out)) { if (evhtp_header_find(r->headers_out, "content-length") == NULL) { char out_buf[64] = { 0 }; size_t out_len = evbuffer_get_length(r->buffer_out); evhtp_modp_sizetoa(out_len, out_buf); evhtp_headers_add_header(r->headers_out, evhtp_header_new("Content-Length", out_buf, 0, 1)); } } evhtp_headers_for_each(r->headers_out, htp__create_headers_, obuf); evbuffer_add_reference(obuf, "\r\n", 2, NULL, NULL); if (evbuffer_get_length(r->buffer_out)) { evbuffer_add_buffer(obuf, r->buffer_out); } return 0; } /* evhtp_make_request */ unsigned int evhtp_request_status(evhtp_request_t * r) { return htparser_get_status(r->conn->parser); } libevhtp-1.2.18/evhtp.pc.in000066400000000000000000000004451342660753300155130ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ libdir=@LIB_INSTALL_DIR@ includedir=@INCLUDE_INSTALL_DIR@/evhtp 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} libevhtp-1.2.18/examples/000077500000000000000000000000001342660753300152475ustar00rootroot00000000000000libevhtp-1.2.18/examples/CMakeLists.txt000066400000000000000000000054751342660753300200220ustar00rootroot00000000000000add_custom_target(examples) add_executable(test_extensive EXCLUDE_FROM_ALL test.c) add_executable(test_basic EXCLUDE_FROM_ALL test_basic.c) add_executable(test_vhost EXCLUDE_FROM_ALL test_vhost.c) add_executable(test_client EXCLUDE_FROM_ALL test_client.c) add_executable(test_query EXCLUDE_FROM_ALL test_query.c) add_executable(test_perf EXCLUDE_FROM_ALL test_perf.c) add_executable(example_vhost EXCLUDE_FROM_ALL example_vhost.c) add_executable(example_pause EXCLUDE_FROM_ALL example_pause.c) add_executable(example_chunked EXCLUDE_FROM_ALL example_chunked.c) add_executable(example_request_fini EXCLUDE_FROM_ALL example_request_fini.c) add_executable(example_basic EXCLUDE_FROM_ALL example_basic.c) if(NOT EVHTP_DISABLE_EVTHR) #add_executable(example_locality EXCLUDE_FROM_ALL example_locality.c) #target_link_libraries(example_locality evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) add_executable(test_proxy EXCLUDE_FROM_ALL test_proxy.c) target_link_libraries(test_proxy evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) add_dependencies(examples test_proxy) endif() target_link_libraries(test_extensive 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}) target_link_libraries(test_perf evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(example_vhost evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(example_pause evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(example_chunked evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(example_request_fini evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(example_basic evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) if(NOT EVHTP_DISABLE_SSL) file(COPY https/etc/ca.cnf https/etc/client1.cnf https/etc/client2.cnf https/etc/server.cnf DESTINATION https/etc/) configure_file(https/bin/generate.sh.in https/bin/generate.sh @ONLY) add_executable(example_https_server EXCLUDE_FROM_ALL https/example_https_server.c) target_link_libraries(example_https_server evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) add_executable(example_https_client EXCLUDE_FROM_ALL https/example_https_client.c) target_link_libraries(example_https_client evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) add_dependencies(examples example_https_server example_https_client) endif() add_dependencies(examples example_request_fini example_chunked example_pause example_vhost test_extensive test_basic test_vhost test_client test_query test_perf example_basic) libevhtp-1.2.18/examples/eutils.h000066400000000000000000000020411342660753300167220ustar00rootroot00000000000000#pragma once #include #include #include static void * mm__dup_(const void * src, size_t size) { void * mem = malloc(size); return mem ? memcpy(mem, src, size) : NULL; } #define mm__alloc_(type, ...) \ (type *)mm__dup_((type[]) {__VA_ARGS__ }, sizeof(type)) #define bind__sock_port0_(HTP) ({ \ struct sockaddr_in sin; \ socklen_t len = sizeof(struct sockaddr); \ uint16_t port = 0; \ \ evhtp_bind_socket(HTP, "127.0.0.1", 9999, 128); \ \ if (getsockname( \ evconnlistener_get_fd(HTP->server), \ (struct sockaddr *)&sin, &len) == 0) { \ port = ntohs(sin.sin_port); \ } \ port; \ }) libevhtp-1.2.18/examples/example_basic.c000066400000000000000000000021131342660753300202040ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "./eutils.h" #include "internal.h" #include "evhtp/evhtp.h" #include "evhtp/log.h" static void process_request_(evhtp_request_t * req, void * arg) { (void)arg; evhtp_log_request_f(arg, req, stderr); evhtp_send_reply(req, EVHTP_RES_OK); } int main(int argc, char ** argv) { (void)argc; (void)argv; struct event_base * evbase; struct evhtp * htp; void * log; evbase = event_base_new(); htp = evhtp_new(evbase, NULL); log = evhtp_log_new("$rhost $host '$ua' [$ts] '$meth $path HTTP/$proto' $status"); evhtp_set_cb(htp, "/", process_request_, log); evhtp_enable_flag(htp, EVHTP_FLAG_ENABLE_ALL); #ifndef EVHTP_DISABLE_EVTHR /* create 1 listener, 4 acceptors */ evhtp_use_threads_wexit(htp, NULL, NULL, 4, NULL); #endif log_info("Basic server, run: curl http://127.0.0.1:%d/", bind__sock_port0_(htp)); event_base_loop(evbase, 0); return 0; } libevhtp-1.2.18/examples/example_chunked.c000066400000000000000000000110501342660753300205440ustar00rootroot00000000000000#include #include #include #include #include #include "./eutils.h" #include "internal.h" #include "evhtp/evhtp.h" struct reply_ { evhtp_request_t * request; FILE * file_desc; struct evbuffer * buffer; }; /* This function is called each time the client has been sent * all outstanding data. We use this to send the next part of * the file in a chunk at 128 byte increments. * * When there is no more data to be read from the file, this * will send the final chunked reply and free our struct reply_. */ static evhtp_res http__send_chunk_(evhtp_connection_t * conn, void * arg) { struct reply_ * reply = (struct reply_ *)arg; char buf[128]; size_t bytes_read; /* try to read 128 bytes from the file pointer */ bytes_read = fread(buf, 1, sizeof(buf), reply->file_desc); log_info("Sending %zu bytes", bytes_read); if (bytes_read > 0) { /* add our data we read from the file into our reply buffer */ evbuffer_add(reply->buffer, buf, bytes_read); /* send the reply buffer as a http chunked message */ evhtp_send_reply_chunk(reply->request, reply->buffer); /* we can now drain our reply buffer as to not be a resource * hog. */ evbuffer_drain(reply->buffer, bytes_read); } /* check if we have read everything from the file */ if (feof(reply->file_desc)) { log_info("Sending last chunk"); /* now that we have read everything from the file, we must * first unset our on_write hook, then inform evhtp to send * this message as the final chunk. */ evhtp_connection_unset_hook(conn, evhtp_hook_on_write); evhtp_send_reply_chunk_end(reply->request); /* we can now free up our little reply_ structure */ { fclose(reply->file_desc); evhtp_safe_free(reply->buffer, evbuffer_free); evhtp_safe_free(reply, free); } } return EVHTP_RES_OK; } static evhtp_res http__conn_fini_(struct evhtp_connection * c, void * arg) { log_info("hi"); return EVHTP_RES_OK; } /* This function is called when a request has been fully received. * * This function assumes the `arg` value is the filename that was * passed via `evhtp_set_gencb` in `main`. * * 1. open the file * 2. create a `struct reply_` * 3. create an evbuffer that we will write into. * 4. set a hook to call the function `http__send_chunk_` each * time all data has been sent from the previous write call. * 5. start the chunked stream via `evhtp_send_reply_chunk_start` */ static void http__callback_(evhtp_request_t * req, void * arg) { const char * filename = arg; FILE * file_desc; struct reply_ * reply; evhtp_assert(arg != NULL); /* open up the file as passed to us via evhtp_set_gencb */ file_desc = fopen(filename, "r"); evhtp_assert(file_desc != NULL); /* create our little internal reply structure which will * be used by `http__send_chunk_` */ reply = mm__alloc_(struct reply_, { req, file_desc, evbuffer_new() }); /* here we set a connection hook of the type `evhtp_hook_on_write` * * this will execute the function `http__send_chunk_` each time * all data has been written to the client. */ evhtp_connection_set_hook(req->conn, evhtp_hook_on_write, http__send_chunk_, reply); /* set a hook to be called when the client disconnects */ evhtp_connection_set_hook(req->conn, evhtp_hook_on_connection_fini, http__conn_fini_, NULL); /* we do not have to start sending data from the file from here - * this function will write data to the client, thus when finished, * will call our `http__send_chunk_` callback. */ evhtp_send_reply_chunk_start(req, EVHTP_RES_OK); } int main(int argc, char ** argv) { evhtp_t * htp; struct event_base * evbase; if (argc < 2) { printf("Usage: %s \n", argv[0]); exit(EXIT_FAILURE); } evbase = event_base_new(); evhtp_alloc_assert(evbase); htp = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp); /* here we set our default request response callback, the argument * that is passed will be the filename we want to stream to the * client in chunked form. */ evhtp_set_gencb(htp, http__callback_, strdup(argv[1])); log_info("curl http://127.0.0.1:%d/", bind__sock_port0_(htp)); event_base_loop(evbase, 0); return 0; } libevhtp-1.2.18/examples/example_locality.c000066400000000000000000000061671342660753300207600ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CPU__COUNT sysconf(_SC_NPROCESSORS_ONLN) static int _init = 0; static int _threads = 0; struct cc__config { char * host; uint16_t port; }; static void dummy_eventcb_(int sock, short which, void * args) { (void)sock; (void)which; (void)args; } static void process_request_(evhtp_request_t * req, void * arg) { (void)arg; evbuffer_add_reference(req->buffer_out, "test_default_cb\n", 16, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_OK); } static void attach_cbpf_(int fd) { struct sock_filter code[] = { { BPF_LD | BPF_W | BPF_ABS, 0, 0, SKF_AD_OFF + SKF_AD_CPU }, /* A = raw_smp_processor_id() */ { BPF_RET | BPF_A, 0, 0, 0 }, /* return A */ }; struct sock_fprog p = { .len = 2, .filter = code, }; if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &p, sizeof(p)) == -1) { fprintf(stderr, "failed to set SO_ATTACH_REUSEPORT_CBPF\n"); } } static void htp_worker_init_(evthr_t * thread, void * args) { struct event_base * evbase; struct cc__config * config; struct evhtp * htp; int core; cpu_set_t cpu_set; if (!(config = (struct cc__config *)args)) { } if (!(evbase = evthr_get_base(thread))) { evthr_stop(thread); return; } if (!(htp = evhtp_new(evbase, thread))) { evthr_stop(thread); return; } evhtp_set_cb(htp, "/", process_request_, thread); evhtp_enable_flag(htp, EVHTP_FLAG_ENABLE_ALL); /*evhtp_use_threads_wexit(htp, NULL, NULL, CPU__COUNT / 2, NULL); */ core = _threads++ % CPU__COUNT; printf("me = %d\n", getppid()); CPU_ZERO(&cpu_set); CPU_SET(core, &cpu_set); pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpu_set); if (pthread_getaffinity_np( pthread_self(), sizeof(cpu_set_t), &cpu_set) == 0) { int i; for (i = 0; i < CPU__COUNT; i++) { if (CPU_ISSET(i, &cpu_set)) { printf("CPU %d\n", i); } } } evhtp_bind_socket(htp, "127.0.0.1", 8089, 1024); attach_cbpf_(evconnlistener_get_fd(htp->server)); } /* htp_worker_init_ */ int main(int argc, char ** argv) { (void)argc; (void)argv; struct event_base * evbase; struct event * dummy_ev; evthr_pool_t * workers; evbase = event_base_new(); dummy_ev = event_new(evbase, -1, EV_READ | EV_PERSIST, dummy_eventcb_, NULL); event_add(dummy_ev, NULL); if (!(workers = evthr_pool_wexit_new(CPU__COUNT, htp_worker_init_, NULL, NULL))) { exit(EXIT_FAILURE); } evthr_pool_start(workers); event_base_loop(evbase, 0); return 0; } libevhtp-1.2.18/examples/example_pause.c000066400000000000000000000064221342660753300202470ustar00rootroot00000000000000/* * Quick example of how to pause a request, and in this case, simply * set a timer to emit the response 10 seconds later. * * This is a good way to long running tasks before responding (thus * not blocking any other processing). */ #include #include #include #include #include #include "internal.h" #include "evhtp/evhtp.h" #include "./eutils.h" struct paused_request_ { struct event * _timeoutev; struct timeval _timeout; evhtp_request_t * _request; }; /* once 10 seconds has passed, this function is called, it will * resume the request, and send the final response back to the * client. */ static void http_resume__callback_(int sock, short events, void * arg) { struct paused_request_ * preq; evhtp_request_t * req; evhtp_assert(arg != NULL); preq = (struct paused_request_ *)arg; req = preq->_request; evhtp_assert(req != NULL); evhtp_safe_free(preq->_timeoutev, event_free); evhtp_safe_free(preq, free); /* inform the evhtp API to resume this connection request */ evhtp_request_resume(req); /* add the current time to our output buffer to the client */ evbuffer_add_printf(req->buffer_out, "time end %ld\n", time(NULL)); /* finally send the response to the client, YAY! */ evhtp_send_reply(req, EVHTP_RES_OK); } /* this is our default callback, it is the one who sets up the evtimer * that triggers the response after 10 seconds. */ static void http_pause__callback_(evhtp_request_t * req, void * arg) { struct timeval * tv = (struct timeval *)arg; struct paused_request_ * preq; /* allocate a little structure that holds our evtimer and the * pending request, se the timeout to 10 seconds. */ preq = malloc(sizeof(*preq)); evhtp_alloc_assert(preq); preq->_request = req; preq->_timeout.tv_sec = tv->tv_sec; preq->_timeout.tv_usec = tv->tv_usec; /* when 10 seconds is up, the function http_resume__callback_ will * be called, this function will actually send the response. */ preq->_timeoutev = evtimer_new(req->htp->evbase, http_resume__callback_, preq); evhtp_alloc_assert(preq->_timeoutev); /* just for debugging, add the time the request was first seen */ evbuffer_add_printf(req->buffer_out, "time start %ld\n", time(NULL)); /* add the timer to the event loop */ evtimer_add(preq->_timeoutev, &preq->_timeout); /* notify the evhtp API to "pause" this request (meaning it will no * longer do any work on this connection until it is "resumed". */ evhtp_request_pause(req); } int main(int argc, char ** argv) { evhtp_t * htp; struct event_base * evbase; struct timeval timeo = { 10, 0 }; evbase = event_base_new(); evhtp_alloc_assert(evbase); htp = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp); /* we just set the default callback for any requests to * the function that pauses the session, sets a timer, * and 10 seconds later, sends the response. */ evhtp_set_gencb(htp, http_pause__callback_, &timeo); log_info("response delayed for 10s: " "curl http://127.0.0.1:%d/", bind__sock_port0_(htp)); return event_base_loop(evbase, 0); } libevhtp-1.2.18/examples/example_request_fini.c000066400000000000000000000034411342660753300216250ustar00rootroot00000000000000/* Example of how to use a hook_request_fini callback. * any hook defined as `evhtp_hook_on_request_fini` will invoke * a user-defined function just prior to being free()'d. * * Here is just a quick example. */ #include #include #include "./eutils.h" #include "internal.h" #include "evhtp/evhtp.h" static evhtp_res request__callback_fini_(evhtp_request_t * req, void * arg) { log_info("req=%p, statusCode=%d statusCodeString=%s for path=%s", req, evhtp_request_get_status_code(req), evhtp_request_get_status_code_str(req), req->uri->path->full); return EVHTP_RES_OK; } static void request__callback_(evhtp_request_t * req, void * arg) { evbuffer_add_printf(req->buffer_out, "Hello, world\n"); evhtp_send_reply(req, EVHTP_RES_200); } int main(int argc, char ** argv) { struct event_base * evbase; evhtp_callback_t * req_callback; evhtp_t * htp; evbase = event_base_new(); evhtp_alloc_assert(evbase); htp = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp); req_callback = evhtp_set_cb(htp, "/", request__callback_, NULL); evhtp_alloc_assert(req_callback); /* Here we are going to make a on_request_fini for a specific * callback, in this case "/" (which will match /anything/really). */ evhtp_callback_set_hook(req_callback, evhtp_hook_on_request_fini, request__callback_fini_, NULL); srand(time(NULL)); #define GENCHAR() ((char)('a' + rand() % 26)) log_info("Simple usage of using request_fini hooks, run: " "curl http://127.0.0.1:%d/%c/%c/%c", bind__sock_port0_(htp), GENCHAR(), GENCHAR(), GENCHAR()); return event_base_loop(evbase, 0); } libevhtp-1.2.18/examples/example_vhost.c000066400000000000000000000104441342660753300202740ustar00rootroot00000000000000#include #include #include #include #include #include "internal.h" #include "evhtp/evhtp.h" #include "./eutils.h" #define make_response(cb) do { \ evbuffer_add_printf(req->buffer_out, \ "%s = host:%s, arg:%s\n", cb, \ evhtp_header_find(req->headers_in, "Host"), \ (char *)arg); \ } while (0) static void vhost_1__callback_(evhtp_request_t * req, void * arg) { /* these should be our callbacks for our evhtp.io hosts */ make_response("vhost_1__callback_"); evhtp_send_reply(req, EVHTP_RES_OK); } static void vhost_2__callback_(evhtp_request_t * req, void * arg) { /* these should be our callbacks for our google hosts */ make_response("vhost_2__callback_"); evhtp_send_reply(req, EVHTP_RES_OK); } int main(int argc, char ** argv) { struct event_base * evbase; evhtp_t * htp; evhtp_t * htp_vhost_1; evhtp_t * htp_vhost_2; evbase = event_base_new(); evhtp_alloc_assert(evbase); /* allocate our main evhtp structure which vhosts are * nested under */ htp = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp); /* create a evhtp structure for vhost_1 specific hostnames, * this will match hostnames for 'evhtp.io' * */ htp_vhost_1 = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp_vhost_1); /* running a get on /vhost for htp_vhost_1 will be different * from htp_vhost_2 due to the hostname we set below. */ evhtp_set_cb(htp_vhost_1, "/vhost", vhost_1__callback_, "evhtp.io domains"); /* create a evhtp structure for vhost_2 specific hostnames, * this will match hostnames for 'google.com' */ htp_vhost_2 = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp_vhost_2); /* running a get on /vhost for http_vhost_2 will be different * from the http_vhost_1 due to the hostname we set below. */ evhtp_set_cb(htp_vhost_2, "/vhost", vhost_2__callback_, "google.com domains"); /* if Host: evhtp.io is present, the callbacks fro htp_vhost_1 are * used. We do this by adding the vhost_1 evhtp to the main htp ctx. */ evhtp_add_vhost(htp, "evhtp.io", htp_vhost_1); /* now lets set some virtual host aliases to evhtp.io */ evhtp_add_aliases(htp_vhost_1, "www.evhtp.io", "web.evhtp.io", NULL); /* If Host: google.com is present, the callbacks for htp_vhost_2 are * used instead. This must be attached to the main htp context. */ evhtp_add_vhost(htp, "google.com", htp_vhost_2); /* now add some virtual host aliases for google.com */ evhtp_add_aliases(htp_vhost_2, "www.google.com", "web.google.com", "inbox.google.com", NULL); /* we can also append a single alias to vhost_2 like this */ evhtp_add_alias(htp_vhost_2, "gmail.google.com"); { uint16_t port = bind__sock_port0_(htp); log_info("[[ try the following commands and you should see 'evhtp.io domains' ]]"); log_info("====================================================================="); log_info("curl -H'Host: evhtp.io' http://127.0.0.1:%d/vhost", port); log_info("curl -H'Host: www.evhtp.io' http://127.0.0.1:%d/vhost", port); log_info("curl -H'Host: web.evhtp.io' http://127.0.0.1:%d/vhost", port); log_info("========================================================================"); log_info("[[ try the following commands and you should see 'google.com domains' ]]"); log_info("========================================================================"); log_info("curl -H'Host: google.com' http://127.0.0.1:%d/vhost", port); log_info("curl -H'Host: www.google.com' http://127.0.0.1:%d/vhost", port); log_info("curl -H'Host: web.google.com' http://127.0.0.1:%d/vhost", port); log_info("curl -H'Host: inbox.google.com' http://127.0.0.1:%d/vhost", port); log_info("curl -H'Host: gmail.google.com' http://127.0.0.1:%d/vhost", port); } return event_base_loop(evbase, 0); } /* main */ libevhtp-1.2.18/examples/https/000077500000000000000000000000001342660753300164115ustar00rootroot00000000000000libevhtp-1.2.18/examples/https/README.md000066400000000000000000000070141342660753300176720ustar00rootroot00000000000000After running `make examples`, if SSL is enabled, you can quickly test HTTPS, with optional client-based certificate authentication using the following process within the build directory: ``` # do all the stupid ssl generation ./examples/https/bin/generate.sh # Test without client auth # Run the server ./examples/example_https_server \ -cert examples/https/server-crt.pem \ -key examples/https/server-key.pem \ -verify-client off # Make a request curl -vk https://localhost:4443/ # Test WITH client auth ./examples/example_https_server \ -cert examples/https/server-crt.pem \ -key examples/https/server-key.pem \ -ca examples/https/ca-crt.pem \ -verify-client on \ -verify-depth 2 # Make a request with the client key curl -kv \ --key examples/https/client1-key.pem \ --cert examples/https/client1-crt.pem \ https://localhost:4443/ ``` The output (with client-certs) should look like: ``` < HTTP/1.1 200 OK < X-SSL-Subject: /C=US/ST=MA/L=Boston/O=Critical Stack/OU=evhtp/CN=client1/emailAddress=nate@cl0d.com < X-SSL-Issuer: /C=US/ST=MA/L=Boston/O=Critical Stack/OU=evhtp/CN=ca/emailAddress=nate@cl0d.com < X-SSL-Notbefore: Dec 7 16:10:34 2017 GMT < X-SSL-Notafter: Sep 1 16:10:34 2020 GMT < X-SSL-Serial: 57459A54BD08848C6D1546C2733EAD8A03553670 < X-SSL-Cipher: ECDHE-RSA-AES256-GCM-SHA384 < X-SSL-Sha1: 7A:68:47:CD:79:18:FF:DA:65:BC:67:6B:C2:5D:F3:66:9A:4A:64:7A < X-SSL-Certificate: -----BEGIN CERTIFICATE----- < MIIFkDCCA3igAwIBAgIUV0WaVL0IhIxtFUbCcz6tigNVNnAwDQYJKoZIhvcNAQEL < BQAwfzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1BMQ8wDQYDVQQHDAZCb3N0b24x < FzAVBgNVBAoMDkNyaXRpY2FsIFN0YWNrMQ4wDAYDVQQLDAVldmh0cDELMAkGA1UE < AwwCY2ExHDAaBgkqhkiG9w0BCQEWDW5hdGVAY2wwZC5jb20wHhcNMTcxMjA3MTYx < MDM0WhcNMjAwOTAxMTYxMDM0WjCBhDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1B < MQ8wDQYDVQQHDAZCb3N0b24xFzAVBgNVBAoMDkNyaXRpY2FsIFN0YWNrMQ4wDAYD < VQQLDAVldmh0cDEQMA4GA1UEAwwHY2xpZW50MTEcMBoGCSqGSIb3DQEJARYNbmF0 < ZUBjbDBkLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALVoTyUm < 62PqJ9RHkNewV+0Dn6AvTVYXQRIejORB75e1OklAp3LGw+Nlc1iP5/MjKzTtMpxk < kLDTDDhiX1mC2j9BDYOC6gpWEVosyU+fXaQvCxWKy4BASPUk7toLwgHxv855TTjV < 2pe6VtAsImCT6sUGrKDnywAFvsBriXnzbTllm4gl7oPi8TrVZhk475JjEKgGKzsS < wtpbxNUqiTXe5lQ/jU6oCCMWG1VWgPLLTIZElhp/TPqLO976DutPXuCBy56NoPMy < DRm7YarhmG1vQNFdeJmC8/xdnKCbQpVhR9sF13tNIc+9QlTKNwzn375MK9e+xjXO < nc8RuqRLeJcMrs5bx5Mtd28F+yNA0riGGtp72vse95bG0PoVUhAqzog4iceHtt6M < 4jyyBhICHYKhkrAYoy3lQdfckiiu5GjZ4rPAq+PP4XPgcXYeqxph6OA+IJwUcWjK < KjfQHvKJbIfo1ILQ4wzGxJ2KAE7F8CBrgokhYmshOMpDx6C4RPwifbtN2GcJN3Vc < kaKGwE72PExFcCLKXwJvXTz+4P87JywCCtYXbUXlgn6rMe4JSRn1NZF00nkeV5bD < AwCCoqSR5Qg9VUyhZKMF3zyQjHKS07SRDyl2vKLFUxnIu+6y4FxvnQqQOdDoz0BG < Uf/s2KNRWK3w3i7hDz0mAQpXyeqmGilT4NZhAgMBAAEwDQYJKoZIhvcNAQELBQAD < ggIBAIypf+0b+5xDRZ4IkcnlbUemZ0xt14UIw4N1Dr2kqp94gu4Z1nkLvYpLg61W < sy3vJLDKc1kSPZG5sPEj/W9zophaSQzL8P/yLHQ3psk2+ie/XDmpmvMsvsVExgut < lOExwMVfp+dIJ1cVfK5i8oMIE0IBbtdAIE/tzV+zzHpvAdA9KDcydW4oF+FLRLmQ < +qfKnK1BkxWqQayNmsbVN63ao8i/4OKD5VtKGPC5RdsEURIDc579lFKACpUnQGaJ < EKX/dKNiqoJSqOEDSsCN6jSJ7uTr5do+7xydqOcTQ+gI3FQsC1NjseqRRU0Q4HVL < 95crEmqxlOxOjcrQK6U36HyKfn7EJ1B6/SJM8U9abOKBRUQjgLlrC6GaA/rToHmX < mlkqw2nKTnZhvIGmi0UjwtOD8rQnGahnq+jwoQV6Ag2YbSfeygvajJvdLBjEBYm+ < 5F6nQgv3JR9iJoS9AxcCEURH7jIAfdbYT6RBT3VARZyPcPtLSMFLLcXh0Z/Egifi < f+xTIL7mCgdW2Jp5s8cNjhrWk6dJVaXwwA6MNSfDeWeu7uHRm3Ir0Jwoe5I2pENm < mKueI6EhIKc6tdQWS6t+ZM0IJsVvhh4s0FqeUFYCP7RxG+P4u5wZxHdjbfUUJ8zA < xHMrDvO8p6dwRUDAPkOqCPpdGmBky/ukBXNi2u0OOJ+wUgoA < -----END CERTIFICATE----- < Content-Length: 0 < Content-Type: text/plain < ``` libevhtp-1.2.18/examples/https/bin/000077500000000000000000000000001342660753300171615ustar00rootroot00000000000000libevhtp-1.2.18/examples/https/bin/generate.sh.in000077500000000000000000000037571342660753300217330ustar00rootroot00000000000000#!/usr/bin/env bash CONFIG_DIR="@PROJECT_BINARY_DIR@/examples/https" # Create new CA openssl req -new -x509 -days 9999 \ -config "$CONFIG_DIR/etc/ca.cnf" \ -keyout "$CONFIG_DIR/ca-key.pem" \ -out "$CONFIG_DIR/ca-crt.pem" # Generate private key for server openssl genrsa -out "$CONFIG_DIR/server-key.pem" 4096 # Generate cert signing request openssl req -new \ -config "$CONFIG_DIR/etc/server.cnf" \ -key "$CONFIG_DIR/server-key.pem" \ -out "$CONFIG_DIR/server-csr.pem" # Sign the request openssl x509 -req \ -extfile "$CONFIG_DIR/etc/server.cnf" \ -days 999 \ -passin "pass:password" \ -in "$CONFIG_DIR/server-csr.pem" \ -CA "$CONFIG_DIR/ca-crt.pem" \ -CAkey "$CONFIG_DIR/ca-key.pem" \ -CAcreateserial \ -out "$CONFIG_DIR/server-crt.pem" # Generate a few client certs openssl genrsa -out "$CONFIG_DIR/client1-key.pem" 4096 openssl genrsa -out "$CONFIG_DIR/client2-key.pem" 4096 # create two cert sign requests openssl req -new -config "$CONFIG_DIR/etc/client1.cnf" -key $CONFIG_DIR/client1-key.pem -out $CONFIG_DIR/client1-csr.pem openssl req -new -config $CONFIG_DIR/etc/client2.cnf -key $CONFIG_DIR/client2-key.pem -out $CONFIG_DIR/client2-csr.pem # sign the above client certs openssl x509 -req \ -extfile $CONFIG_DIR/etc/client1.cnf \ -days 999 \ -passin "pass:password" \ -in $CONFIG_DIR/client1-csr.pem \ -CA $CONFIG_DIR/ca-crt.pem \ -CAkey $CONFIG_DIR/ca-key.pem \ -CAcreateserial \ -out $CONFIG_DIR/client1-crt.pem openssl x509 -req \ -extfile $CONFIG_DIR/etc/client2.cnf \ -days 999 \ -passin "pass:password" \ -in $CONFIG_DIR/client2-csr.pem \ -CA $CONFIG_DIR/ca-crt.pem \ -CAkey $CONFIG_DIR/ca-key.pem \ -CAcreateserial \ -out $CONFIG_DIR/client2-crt.pem libevhtp-1.2.18/examples/https/etc/000077500000000000000000000000001342660753300171645ustar00rootroot00000000000000libevhtp-1.2.18/examples/https/etc/ca.cnf000066400000000000000000000013231342660753300202360ustar00rootroot00000000000000[ ca ] default_ca = CA_default [ CA_default ] serial = ca-serial crl = ca-crl.pem database = ca-database.txt name_opt = CA_default cert_opt = CA_default default_crl_days = 9999 default_md = md5 [ req ] default_bits = 4096 days = 9999 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no output_password = password [ req_distinguished_name ] C = US ST = MA L = Boston O = Critical Stack OU = evhtp CN = ca emailAddress = nate@cl0d.com [ req_attributes ] challengePassword = test libevhtp-1.2.18/examples/https/etc/client1.cnf000066400000000000000000000012421342660753300212120ustar00rootroot00000000000000[ req ] default_bits = 4096 days = 9999 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no x509_extensions = v3_ca [ req_distinguished_name ] C = US ST = MA L = Boston O = Critical Stack OU = evhtp CN = client1 emailAddress = nate@cl0d.com [ req_attributes ] challengePassword = password [ v3_ca ] authorityInfoAccess = @issuer_info [ issuer_info ] OCSP;URI.0 = http://ocsp.example.com/ caIssuers;URI.0 = http://example.com/ca.cert libevhtp-1.2.18/examples/https/etc/client2.cnf000066400000000000000000000012421342660753300212130ustar00rootroot00000000000000[ req ] default_bits = 4096 days = 9999 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no x509_extensions = v3_ca [ req_distinguished_name ] C = US ST = MA L = Boston O = Critical Stack OU = evhtp CN = client2 emailAddress = nate@cl0d.com [ req_attributes ] challengePassword = password [ v3_ca ] authorityInfoAccess = @issuer_info [ issuer_info ] OCSP;URI.0 = http://ocsp.example.com/ caIssuers;URI.0 = http://example.com/ca.cert libevhtp-1.2.18/examples/https/etc/server.cnf000066400000000000000000000012441342660753300211630ustar00rootroot00000000000000[ req ] default_bits = 4096 days = 9999 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no x509_extensions = v3_ca [ req_distinguished_name ] C = US ST = MA L = Boston O = Critical Stack OU = evhtp CN = localhost emailAddress = nate@cl0d.com [ req_attributes ] challengePassword = password [ v3_ca ] authorityInfoAccess = @issuer_info [ issuer_info ] OCSP;URI.0 = http://ocsp.example.com/ caIssuers;URI.0 = http://example.com/ca.cert libevhtp-1.2.18/examples/https/example_https_client.c000066400000000000000000000107301342660753300227710ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "internal.h" #include "evhtp/evhtp.h" #ifndef EVHTP_DISABLE_SSL #include "evhtp/sslutils.h" static int print_header_(evhtp_header_t * header, void * arg) { fprintf(stderr, "%s: %s\n", header->key, header->val); return 0; } static void https_resp_(evhtp_request_t * req, void * arg) { evhtp_headers_for_each(req->headers_in, print_header_, NULL); if (evbuffer_get_length(req->buffer_in)) { fprintf(stderr, "got: %.*s\n", (int)evbuffer_get_length(req->buffer_in), evbuffer_pullup(req->buffer_in, -1)); } /* since we only made one request, we break the event loop */ event_base_loopbreak((struct event_base *)arg); } enum { OPTARG_CERT = 1000, OPTARG_KEY, OPTARG_ADDR, OPTARG_PORT, OPTARG_SNI }; #endif int main(int argc, char ** argv) { #ifndef EVHTP_DISABLE_SSL struct event_base * evbase; evhtp_connection_t * conn; evhtp_request_t * req; evhtp_ssl_ctx_t * ctx; char * addr = NULL; uint16_t port = 4443; char * key = NULL; char * crt = NULL; int opt = 0; int long_index = 0; int res; static struct option long_options[] = { { "cert", required_argument, 0, OPTARG_CERT }, { "key", required_argument, 0, OPTARG_KEY }, { "addr", required_argument, 0, OPTARG_ADDR }, { "port", required_argument, 0, OPTARG_PORT }, { "help", no_argument, 0, 'h' }, { NULL, 0, 0, 0 } }; while ((opt = getopt_long_only(argc, argv, "", long_options, &long_index)) != -1) { switch (opt) { case 'h': printf("Usage: %s\n" " -key \n" " -cert \n" " -addr \n" " -port \n", argv[0]); return 0; case OPTARG_CERT: crt = strdup(optarg); break; case OPTARG_KEY: key = strdup(optarg); break; case OPTARG_ADDR: addr = strdup(optarg); break; case OPTARG_PORT: port = atoi(optarg); break; } /* switch */ } evbase = event_base_new(); evhtp_alloc_assert(evbase); #if OPENSSL_VERSION_NUMBER < 0x10100000L ctx = SSL_CTX_new(SSLv23_client_method()); #else ctx = SSL_CTX_new(TLS_client_method()); #endif evhtp_assert(ctx != NULL); if (key) { /* client private key file defined, so use it */ res = SSL_CTX_use_PrivateKey_file( ctx, key, SSL_FILETYPE_PEM); if (res == 0) { ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } } if (crt) { /* client cert key file defined, use it */ res = SSL_CTX_use_certificate_file( ctx, crt, SSL_FILETYPE_PEM); if (res == 0) { ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } } /* create a new connection to the server */ conn = evhtp_connection_ssl_new(evbase, addr ? : "127.0.0.1", port, ctx); evhtp_assert(conn != NULL); /* when the request has been completed, call https_resp_ */ req = evhtp_request_new(https_resp_, evbase); evhtp_assert(req != NULL); /* make a request context, 'GET / HTTP/1.1' */ res = evhtp_make_request(conn, req, htp_method_GET, "/"); evhtp_assert(res == 0); /* the loop will make the request and call https_resp_ * when complete. */ event_base_loop(evbase, 0); /* free up all the resources */ { SSL_CTX_free(ctx); evhtp_safe_free(req, evhtp_request_free); evhtp_safe_free(conn, evhtp_connection_free); event_base_free(evbase); free(crt); free(key); free(addr); } return 0; #else log_error("Not compiled with SSL support, go away"); return EXIT_FAILURE; #endif } /* main */ libevhtp-1.2.18/examples/https/example_https_server.c000066400000000000000000000220071342660753300230210ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "internal.h" #include "evhtp/evhtp.h" #ifndef EVHTP_DISABLE_SSL #include "evhtp/sslutils.h" static void http__callback_(evhtp_request_t * req, void * arg) { evhtp_connection_t * conn; evhtp_assert(req != NULL); conn = evhtp_request_get_connection(req); evhtp_assert(conn != NULL); htp_sslutil_add_xheaders( req->headers_out, conn->ssl, HTP_SSLUTILS_XHDR_ALL); return evhtp_send_reply(req, EVHTP_RES_OK); } static int ssl__x509_verify_(int ok, X509_STORE_CTX * store) { char buf[256]; X509 * err_cert; int err; int depth; SSL * ssl; evhtp_connection_t * connection; evhtp_ssl_cfg_t * ssl_cfg; err_cert = X509_STORE_CTX_get_current_cert(store); err = X509_STORE_CTX_get_error(store); depth = X509_STORE_CTX_get_error_depth(store); ssl = X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx()); X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256); connection = SSL_get_app_data(ssl); ssl_cfg = connection->htp->ssl_cfg; if (depth > ssl_cfg->verify_depth) { ok = 0; err = X509_V_ERR_CERT_CHAIN_TOO_LONG; X509_STORE_CTX_set_error(store, err); } if (!ok) { log_error("SSL: verify error:num=%d:%s:depth=%d:%s", err, X509_verify_cert_error_string(err), depth, buf); } return ok; } enum { OPTARG_CERT = 1000, OPTARG_KEY, OPTARG_CA, OPTARG_CAPATH, OPTARG_CIPHERS, OPTARG_VERIFY_PEER, OPTARG_VERIFY_DEPTH, OPTARG_ENABLE_CACHE, OPTARG_CACHE_TIMEOUT, OPTARG_CACHE_SIZE, OPTARG_CTX_TIMEOUT, OPTARG_ENABLE_PROTOCOL, OPTARG_DISABLE_PROTOCOL }; static const char * help = "Usage %s [opts] :\n" " -cert : Server PEM-encoded X.509 Certificate file\n" " -key : Server PEM-encoded Private Key file\n" " -ca : File of PEM-encoded Server CA Certificates\n" " -capath : Directory of PEM-encoded CA Certificates for Client Auth\n" " -ciphers : Accepted SSL Ciphers\n" " -verify-client (on | off | optional)\n" " Enables verification of client certificates. \n" " on : the client has to present a valid cert \n" " off : no client cert is required at all \n" " optional : the client may present a valid cert \n" " -verify-depth : Maximum depth of CA Certificates in Client Certificate verification\n" " -enable-protocol

: Enable one of the following protocols: SSLv2, SSLv3, TLSv1, or ALL\n" " -disable-protocol

: Disable one of the following protocols: SSLv2, SSLv3, TLSv1, or ALL\n" " -ctx-timeout : SSL Session Timeout (SSL >= 1.0)\n"; evhtp_ssl_cfg_t * parse__ssl_opts_(int argc, char ** argv) { int opt = 0; int long_index = 0; int ssl_verify_mode = 0; struct stat f_stat; evhtp_ssl_cfg_t * ssl_config = calloc(1, sizeof(evhtp_ssl_cfg_t)); ssl_config->ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1; static struct option long_options[] = { { "cert", required_argument, 0, OPTARG_CERT }, { "key", required_argument, 0, OPTARG_KEY }, { "ca", required_argument, 0, OPTARG_CA }, { "capath", required_argument, 0, OPTARG_CAPATH }, { "ciphers", required_argument, 0, OPTARG_CIPHERS }, { "verify-client", required_argument, 0, OPTARG_VERIFY_PEER }, { "verify-depth", required_argument, 0, OPTARG_VERIFY_DEPTH }, { "enable-cache", no_argument, 0, OPTARG_ENABLE_CACHE }, { "cache-timeout", required_argument, 0, OPTARG_CACHE_TIMEOUT }, { "cache-size", required_argument, 0, OPTARG_CACHE_SIZE }, { "enable-protocol", required_argument, 0, OPTARG_ENABLE_PROTOCOL }, { "disable-protocol", required_argument, 0, OPTARG_DISABLE_PROTOCOL }, { "ctx-timeout", required_argument, 0, OPTARG_CTX_TIMEOUT }, { "help", no_argument, 0, 'h' }, { NULL, 0, 0, 0 } }; while ((opt = getopt_long_only(argc, argv, "", long_options, &long_index)) != -1) { switch (opt) { case 'h': printf(help, argv[0]); exit(EXIT_FAILURE); case OPTARG_CERT: ssl_config->pemfile = strdup(optarg); break; case OPTARG_KEY: ssl_config->privfile = strdup(optarg); break; case OPTARG_CA: ssl_config->cafile = strdup(optarg); break; case OPTARG_CAPATH: ssl_config->capath = strdup(optarg); break; case OPTARG_CIPHERS: ssl_config->ciphers = strdup(optarg); break; case OPTARG_VERIFY_DEPTH: ssl_config->verify_depth = atoi(optarg); break; case OPTARG_VERIFY_PEER: ssl_verify_mode = htp_sslutil_verify2opts(optarg); break; case OPTARG_ENABLE_CACHE: ssl_config->scache_type = evhtp_ssl_scache_type_internal; break; case OPTARG_CACHE_TIMEOUT: ssl_config->scache_timeout = atoi(optarg); break; case OPTARG_CACHE_SIZE: ssl_config->scache_size = atoi(optarg); break; case OPTARG_CTX_TIMEOUT: ssl_config->ssl_ctx_timeout = atoi(optarg); break; case OPTARG_ENABLE_PROTOCOL: if (!strcasecmp(optarg, "SSLv2")) { ssl_config->ssl_opts &= ~SSL_OP_NO_SSLv2; } else if (!strcasecmp(optarg, "SSLv3")) { ssl_config->ssl_opts &= ~SSL_OP_NO_SSLv3; } else if (!strcasecmp(optarg, "TLSv1")) { ssl_config->ssl_opts &= ~SSL_OP_NO_TLSv1; } else if (!strcasecmp(optarg, "ALL")) { ssl_config->ssl_opts = 0; } break; case OPTARG_DISABLE_PROTOCOL: if (!strcasecmp(optarg, "SSLv2")) { ssl_config->ssl_opts |= SSL_OP_NO_SSLv2; } else if (!strcasecmp(optarg, "SSLv3")) { ssl_config->ssl_opts |= SSL_OP_NO_SSLv3; } else if (!strcasecmp(optarg, "TLSv1")) { ssl_config->ssl_opts |= SSL_OP_NO_TLSv1; } else if (!strcasecmp(optarg, "ALL")) { ssl_config->ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1; } break; default: break; } /* switch */ } if (ssl_verify_mode != 0) { ssl_config->verify_peer = ssl_verify_mode; ssl_config->x509_verify_cb = ssl__x509_verify_; } if (ssl_config->pemfile) { if (stat(ssl_config->pemfile, &f_stat) != 0) { log_error("Cannot load SSL cert '%s' (%s)", ssl_config->pemfile, strerror(errno)); exit(EXIT_FAILURE); } } if (ssl_config->privfile) { if (stat(ssl_config->privfile, &f_stat) != 0) { log_error("Cannot load SSL key '%s' (%s)", ssl_config->privfile, strerror(errno)); exit(EXIT_FAILURE); } } if (ssl_config->cafile) { if (stat(ssl_config->cafile, &f_stat) != 0) { log_error("Cannot find SSL CA File '%s' (%s)", ssl_config->cafile, strerror(errno)); exit(EXIT_FAILURE); } } if (ssl_config->capath) { if (stat(ssl_config->capath, &f_stat) != 0) { log_error("Cannot find SSL CA PATH '%s' (%s)", ssl_config->capath, strerror(errno)); exit(EXIT_FAILURE); } } return ssl_config; } /* parse__ssl_opts_ */ #endif int main(int argc, char ** argv) { #ifndef EVHTP_DISABLE_SSL evhtp_t * htp; struct event_base * evbase; evbase = event_base_new(); evhtp_alloc_assert(evbase); htp = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp); evhtp_ssl_init(htp, parse__ssl_opts_(argc, argv)); evhtp_set_gencb(htp, http__callback_, NULL); evhtp_bind_socket(htp, "127.0.0.1", 4443, 128); log_info("curl https://127.0.0.1:4443/"); event_base_loop(evbase, 0); return 0; #else log_error("Not compiled with SSL support, go away"); return EXIT_FAILURE; #endif } libevhtp-1.2.18/examples/test.c000066400000000000000000000462741342660753300164070ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "internal.h" #include "evhtp/evhtp.h" #ifndef EVHTP_DISABLE_EVTHR int use_threads = 0; int num_threads = 0; #endif char * bind_addr = "0.0.0.0"; uint16_t bind_port = 8081; char * ext_body = NULL; char * ssl_pem = NULL; char * ssl_ca = NULL; char * ssl_capath = NULL; size_t bw_limit = 0; uint64_t max_keepalives = 0; int backlog = 1024; struct pauser { event_t * timer_ev; evhtp_request_t * request; struct timeval * tv; }; /* pause testing */ static void resume_request_timer(evutil_socket_t sock, short which, void * arg) { struct pauser * pause = (struct pauser *)arg; printf("resume_request_timer(%p) timer_ev = %p\n", pause->request->conn, pause->timer_ev); fflush(stdout); evhtp_request_resume(pause->request); } static evhtp_res pause_cb(evhtp_request_t * request, evhtp_header_t * header, void * arg) { struct pauser * pause = (struct pauser *)arg; int s = rand() % 1000000; printf("pause_cb(%p) pause == %p, timer_ev = %p\n", request->conn, pause, pause->timer_ev); printf("pause_cb(%p) k=%s, v=%s timer_ev = %p\n", request->conn, header->key, header->val, pause->timer_ev); printf("pause_cb(%p) setting to %ld usec sleep timer_ev = %p\n", request->conn, (long int)s, pause->timer_ev); pause->tv->tv_sec = 0; pause->tv->tv_usec = s; if (evtimer_pending(pause->timer_ev, NULL)) { evtimer_del(pause->timer_ev); } evtimer_add(pause->timer_ev, pause->tv); return EVHTP_RES_PAUSE; } static evhtp_res pause_connection_fini(evhtp_connection_t * connection, void * arg) { printf("pause_connection_fini(%p)\n", connection); return EVHTP_RES_OK; } static evhtp_res pause_request_fini(evhtp_request_t * request, void * arg) { struct pauser * pause = (struct pauser *)arg; printf("pause_request_fini() req=%p, c=%p status_code=%d::%s\n", request, evhtp_request_get_connection(request), evhtp_request_get_status_code(request), evhtp_request_get_status_code_str(request)); evhtp_safe_free(pause->timer_ev, event_free); evhtp_safe_free(pause->tv, free); evhtp_safe_free(pause, free); return EVHTP_RES_OK; } static evhtp_res pause_init_cb(evhtp_request_t * req, evhtp_path_t * path, void * arg) { evbase_t * evbase = req->conn->evbase; struct pauser * pause = calloc(sizeof(struct pauser), 1); pause->tv = calloc(sizeof(struct timeval), 1); pause->timer_ev = evtimer_new(evbase, resume_request_timer, pause); pause->request = req; evhtp_request_set_hook(req, evhtp_hook_on_header, pause_cb, pause); evhtp_request_set_hook(req, evhtp_hook_on_request_fini, pause_request_fini, pause); evhtp_connection_set_hook(req->conn, evhtp_hook_on_connection_fini, pause_connection_fini, NULL); return EVHTP_RES_OK; } static void test_pause_cb(evhtp_request_t * request, void * arg) { printf("test_pause_cb(%p)\n", request->conn); evhtp_send_reply(request, EVHTP_RES_OK); } static void _owned_readcb(evbev_t * bev, void * arg) { /* echo the input back to the client */ bufferevent_write_buffer(bev, bufferevent_get_input(bev)); } static void _owned_eventcb(evbev_t * bev, short events, void * arg) { evhtp_safe_free(bev, bufferevent_free); } static void test_ownership(evhtp_request_t * request, void * arg) { evhtp_connection_t * conn = evhtp_request_get_connection(request); evbev_t * bev = evhtp_connection_take_ownership(conn); bufferevent_enable(bev, EV_READ); bufferevent_setcb(bev, _owned_readcb, NULL, _owned_eventcb, NULL); } #ifndef EVHTP_DISABLE_REGEX static void test_regex(evhtp_request_t * req, void * arg) { evbuffer_add_printf(req->buffer_out, "rest_regex start = '%s', end = '%s\n", req->uri->path->match_start, req->uri->path->match_end); evhtp_send_reply(req, EVHTP_RES_OK); } static void dynamic_cb(evhtp_request_t * r, void * arg) { const char * name = arg; evbuffer_add_printf(r->buffer_out, "dynamic_cb = %s\n", name); evhtp_send_reply(r, EVHTP_RES_OK); } static void create_callback(evhtp_request_t * r, void * arg) { char * uri; char * nuri; size_t urilen; uri = r->uri->path->match_start; urilen = strlen(uri); if (urilen == 0) { return evhtp_send_reply(r, EVHTP_RES_BADREQ); } nuri = calloc(urilen + 2, 1); snprintf(nuri, urilen + 2, "/%s", uri); evhtp_set_cb(r->htp, nuri, dynamic_cb, nuri); 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, "test_foo_cb\n", 12, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_OK); } static void test_500_cb(evhtp_request_t * req, void * arg ) { evbuffer_add_reference(req->buffer_out, "test_500_cb\n", 12, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_SERVERR); } static void test_max_body(evhtp_request_t * req, void * arg) { evbuffer_add_reference(req->buffer_out, "test_max_body\n", 14, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_OK); } const char * chunk_strings[] = { "I give you the light of Eärendil,\n", "our most beloved star.\n", "May it be a light for you in dark places,\n", "when all other lights go out.\n", NULL }; static void test_chunking(evhtp_request_t * req, void * arg) { const char * chunk_str; evbuf_t * buf; int i = 0; buf = evbuffer_new(); evhtp_send_reply_chunk_start(req, EVHTP_RES_OK); while ((chunk_str = chunk_strings[i++]) != NULL) { evbuffer_add(buf, chunk_str, strlen(chunk_str)); evhtp_send_reply_chunk(req, buf); evbuffer_drain(buf, -1); } evhtp_send_reply_chunk_end(req); evhtp_safe_free(buf, evbuffer_free); } static void test_bar_cb(evhtp_request_t * req, void * arg) { evhtp_send_reply(req, EVHTP_RES_OK); } static void test_glob_cb(evhtp_request_t * req, void * arg) { evbuffer_add(req->buffer_out, "test_glob_cb\n", 13); evhtp_send_reply(req, EVHTP_RES_OK); } static void test_default_cb(evhtp_request_t * req, void * arg) { evbuffer_add_reference(req->buffer_out, "test_default_cb\n", 16, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_OK); } static evhtp_res print_kv(evhtp_request_t * req, evhtp_header_t * hdr, void * arg) { evbuffer_add_printf(req->buffer_out, "print_kv() key = '%s', val = '%s'\n", hdr->key, hdr->val); return EVHTP_RES_OK; } static int output_header(evhtp_header_t * header, void * arg) { evbuf_t * buf = arg; evbuffer_add_printf(buf, "print_kvs() key = '%s', val = '%s'\n", header->key, header->val); return 0; } static evhtp_res print_kvs(evhtp_request_t * req, evhtp_headers_t * hdrs, void * arg ) { evhtp_headers_for_each(hdrs, output_header, req->buffer_out); return EVHTP_RES_OK; } static evhtp_res print_path(evhtp_request_t * req, evhtp_path_t * path, void * arg) { if (ext_body) { evbuffer_add_printf(req->buffer_out, "ext_body: '%s'\n", ext_body); } evbuffer_add_printf(req->buffer_out, "print_path() full = '%s'\n" " path = '%s'\n" " file = '%s'\n" " match start = '%s'\n" " match_end = '%s'\n" " methno = '%d'\n", path->full, path->path, path->file, path->match_start, path->match_end, evhtp_request_get_method(req)); return EVHTP_RES_OK; } static evhtp_res print_data(evhtp_request_t * req, evbuf_t * buf, void * arg) { #ifndef NDEBUG 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))); */ #endif evbuffer_drain(buf, -1); return EVHTP_RES_OK; } static evhtp_res print_new_chunk_len(evhtp_request_t * req, uint64_t len, void * arg) { evbuffer_add_printf(req->buffer_out, "started new chunk, %" PRId64 "u bytes\n", len); return EVHTP_RES_OK; } static evhtp_res print_chunk_complete(evhtp_request_t * req, void * arg) { evbuffer_add_printf(req->buffer_out, "ended a single chunk\n"); return EVHTP_RES_OK; } static evhtp_res print_chunks_complete(evhtp_request_t * req, void * arg) { evbuffer_add_printf(req->buffer_out, "all chunks read\n"); return EVHTP_RES_OK; } #ifndef EVHTP_DISABLE_REGEX static evhtp_res test_regex_hdrs_cb(evhtp_request_t * req, evhtp_headers_t * hdrs, void * arg ) { return EVHTP_RES_OK; } #endif static evhtp_res set_max_body(evhtp_request_t * req, evhtp_headers_t * hdrs, void * arg) { evhtp_request_set_max_body_size(req, 1024); return EVHTP_RES_OK; } static evhtp_res test_pre_accept(evhtp_connection_t * c, void * arg) { uint16_t port = *(uint16_t *)arg; if (port > 10000) { return EVHTP_RES_ERROR; } return EVHTP_RES_OK; } static evhtp_res test_fini(evhtp_request_t * r, void * arg) { struct ev_token_bucket_cfg * tcfg = arg; if (tcfg) { ev_token_bucket_cfg_free(tcfg); } return EVHTP_RES_OK; } #if 0 static evhtp_res print_hostname(evhtp_request_t * r, const char * host, void * arg) { printf("%s\n", host); return EVHTP_RES_OK; } #endif static evhtp_res set_my_connection_handlers(evhtp_connection_t * conn, void * arg) { struct timeval tick; struct ev_token_bucket_cfg * tcfg = NULL; evhtp_connection_set_hook(conn, evhtp_hook_on_header, print_kv, "foo"); evhtp_connection_set_hook(conn, evhtp_hook_on_headers, print_kvs, "bar"); evhtp_connection_set_hook(conn, evhtp_hook_on_path, print_path, "baz"); evhtp_connection_set_hook(conn, evhtp_hook_on_read, print_data, "derp"); evhtp_connection_set_hook(conn, evhtp_hook_on_new_chunk, print_new_chunk_len, NULL); evhtp_connection_set_hook(conn, evhtp_hook_on_chunk_complete, print_chunk_complete, NULL); evhtp_connection_set_hook(conn, evhtp_hook_on_chunks_complete, print_chunks_complete, NULL); if (bw_limit > 0) { tick.tv_sec = 0; tick.tv_usec = 500 * 100; tcfg = ev_token_bucket_cfg_new(bw_limit, bw_limit, bw_limit, bw_limit, &tick); bufferevent_set_rate_limit(conn->bev, tcfg); } evhtp_connection_set_hook(conn, evhtp_hook_on_request_fini, test_fini, tcfg); return EVHTP_RES_OK; } #ifndef EVHTP_DISABLE_SSL static int dummy_ssl_verify_callback(int ok, X509_STORE_CTX * x509_store) { return 1; } static int dummy_check_issued_cb(X509_STORE_CTX * ctx, X509 * x, X509 * issuer) { return 1; } #endif const char * optstr = "htn:a:p:r:s:c:C:l:N:m:b:"; const char * help = "Options: \n" " -h : This help text\n" #ifndef EVHTP_DISABLE_EVTHR " -t : Run requests in a thread (default: off)\n" " -n : Number of threads (default: 0 if -t is off, 4 if -t is on)\n" #endif #ifndef EVHTP_DISABLE_SSL " -s : Enable SSL and PEM (default: NULL)\n" " -c : CA cert file (default: NULL)\n" " -C : CA Path (default: NULL)\n" " (to test: \n" " openssl genrsa -out pkey 2048\n" " openssl req -new -key pkey -out cert.req\n" " openssl x509 -req -days 365 -in cert.req -signkey pkey -out cert\n" " cat pkey cert > test_cert\n" " then use -s test_cert\n" #endif " -l : Max bandwidth (in bytes) (default: NULL)\n" " -r : Document root (default: .)\n" " -N : Add this string to body. (default: NULL)\n" " -a : Bind Address (default: 0.0.0.0)\n" " -p : Bind Port (default: 8081)\n" " -m : Max keepalive requests (default: 0)\n"; int parse_args(int argc, char ** argv) { extern char * optarg; extern int optind; extern int opterr; extern int optopt; int c; while ((c = getopt(argc, argv, optstr)) != -1) { switch (c) { case 'h': printf("Usage: %s [opts]\n%s", argv[0], help); return -1; case 'N': ext_body = strdup(optarg); break; case 'a': bind_addr = strdup(optarg); break; case 'p': bind_port = atoi(optarg); break; #ifndef EVHTP_DISABLE_EVTHR case 't': use_threads = 1; break; case 'n': num_threads = atoi(optarg); break; #endif #ifndef EVHTP_DISABLE_SSL case 's': ssl_pem = strdup(optarg); break; case 'c': ssl_ca = strdup(optarg); break; case 'C': ssl_capath = strdup(optarg); break; #endif case 'l': bw_limit = atoll(optarg); break; case 'm': max_keepalives = atoll(optarg); break; case 'b': backlog = atoll(optarg); break; default: printf("Unknown opt %s\n", optarg); return -1; } /* switch */ } #ifndef EVHTP_DISABLE_EVTHR if (use_threads && num_threads == 0) { num_threads = 4; } #endif return 0; } /* parse_args */ static void sigint(int sig, short why, void * data) { event_base_loopexit(data, NULL); } int main(int argc, char ** argv) { struct event * ev_sigint; evbase_t * evbase = NULL; evhtp_t * htp = NULL; evhtp_callback_t * cb_1 = NULL; evhtp_callback_t * cb_2 = NULL; 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; evhtp_callback_t * cb_12 = NULL; if (parse_args(argc, argv) < 0) { exit(1); } srand((unsigned)time(NULL)); evbase = event_base_new(); htp = evhtp_new(evbase, NULL); evhtp_enable_flag(htp, EVHTP_FLAG_ENABLE_REUSEPORT); evhtp_set_parser_flags(htp, EVHTP_PARSE_QUERY_FLAG_LENIENT); evhtp_set_max_keepalive_requests(htp, max_keepalives); cb_1 = evhtp_set_cb(htp, "/ref", test_default_cb, "fjdkls"); evhtp_assert(cb_1 != NULL); cb_2 = evhtp_set_cb(htp, "/foo", test_foo_cb, "bar"); evhtp_assert(cb_2 != NULL); cb_3 = evhtp_set_cb(htp, "/foo/", test_foo_cb, "bar"); evhtp_assert(cb_3 != NULL); cb_4 = evhtp_set_cb(htp, "/bar", test_bar_cb, "baz"); evhtp_assert(cb_4 != NULL); cb_5 = evhtp_set_cb(htp, "/500", test_500_cb, "500"); evhtp_assert(cb_5 != NULL); #ifndef EVHTP_DISABLE_REGEX cb_6 = evhtp_set_regex_cb(htp, "^(/anything/).*", test_regex, NULL); evhtp_assert(cb_6 != NULL); #endif cb_7 = evhtp_set_cb(htp, "/pause", test_pause_cb, NULL); evhtp_assert(cb_7 != NULL); #ifndef EVHTP_DISABLE_REGEX cb_8 = evhtp_set_regex_cb(htp, "^/create/(.*)", create_callback, NULL); evhtp_assert(cb_8 != NULL); #endif cb_9 = evhtp_set_glob_cb(htp, "*/glob/*", test_glob_cb, NULL); evhtp_assert(cb_9 != NULL); cb_10 = evhtp_set_cb(htp, "/max_body_size", test_max_body, NULL); evhtp_assert(cb_10 != NULL); /* set a callback to test out chunking API */ cb_11 = evhtp_set_cb(htp, "/chunkme", test_chunking, NULL); evhtp_assert(cb_11 != NULL); /* set a callback which takes ownership of the underlying bufferevent and * just starts echoing things */ cb_12 = evhtp_set_cb(htp, "/ownme", test_ownership, NULL); evhtp_assert(cb_12 != NULL); /* set a callback to pause on each header for cb_7 */ evhtp_callback_set_hook(cb_7, evhtp_hook_on_path, pause_init_cb, NULL); /* set a callback to set hooks specifically for the cb_6 callback */ #ifndef EVHTP_DISABLE_REGEX evhtp_callback_set_hook(cb_6, evhtp_hook_on_headers, test_regex_hdrs_cb, NULL); #endif evhtp_callback_set_hook(cb_10, evhtp_hook_on_headers, set_max_body, NULL); /* set a default request handler */ evhtp_set_gencb(htp, test_default_cb, "foobarbaz"); /* set a callback invoked before a connection is accepted */ evhtp_set_pre_accept_cb(htp, test_pre_accept, &bind_port); /* set a callback to set per-connection hooks (via a post_accept cb) */ evhtp_set_post_accept_cb(htp, set_my_connection_handlers, NULL); #ifndef EVHTP_DISABLE_SSL if (ssl_pem != NULL) { evhtp_ssl_cfg_t scfg = { .pemfile = ssl_pem, .privfile = ssl_pem, .cafile = ssl_ca, .capath = ssl_capath, .ciphers = "RC4+RSA:HIGH:+MEDIUM:+LOW", .ssl_opts = SSL_OP_NO_SSLv2, .ssl_ctx_timeout = 60 * 60 * 48, .verify_peer = SSL_VERIFY_PEER, .verify_depth = 42, .x509_verify_cb = dummy_ssl_verify_callback, .x509_chk_issued_cb = dummy_check_issued_cb, .scache_type = evhtp_ssl_scache_type_internal, .scache_size = 1024, .scache_timeout = 1024, .scache_init = NULL, .scache_add = NULL, .scache_get = NULL, .scache_del = NULL, }; evhtp_ssl_init(htp, &scfg); #ifndef EVHTP_DISABLE_EVTHR if (use_threads) { #define OPENSSL_THREAD_DEFINES #include #if defined(OPENSSL_THREADS) #else fprintf(stderr, "Your version of OpenSSL does not support threading!\n"); exit(-1); #endif } #endif } #endif #ifndef EVHTP_DISABLE_EVTHR if (use_threads) { evhtp_use_threads_wexit(htp, NULL, NULL, num_threads, NULL); } #endif if (evhtp_bind_socket(htp, bind_addr, bind_port, backlog) < 0) { fprintf(stderr, "Could not bind socket: %s\n", strerror(errno)); exit(-1); } ev_sigint = evsignal_new(evbase, SIGINT, sigint, evbase); evsignal_add(ev_sigint, NULL); event_base_loop(evbase, 0); evhtp_safe_free(ev_sigint, event_free); evhtp_unbind_socket(htp); evhtp_safe_free(htp, evhtp_free); evhtp_safe_free(evbase, event_base_free); return 0; } /* main */ libevhtp-1.2.18/examples/test_basic.c000066400000000000000000000025641342660753300175420ustar00rootroot00000000000000#include #include #include #include #include #include void testcb(evhtp_request_t * req, void * a) { const char * str = a; evbuffer_add(req->buffer_out, str, strlen(str)); evhtp_send_reply(req, EVHTP_RES_OK); } void issue161cb(evhtp_request_t * req, void * a) { struct evbuffer * b = evbuffer_new(); if (evhtp_request_get_proto(req) == EVHTP_PROTO_10) { evhtp_request_set_keepalive(req, 0); } evhtp_send_reply_start(req, EVHTP_RES_OK); evbuffer_add(b, "foo", 3); evhtp_send_reply_body(req, b); evbuffer_add(b, "bar\n\n", 5); evhtp_send_reply_body(req, b); evhtp_send_reply_end(req); evhtp_safe_free(b, evbuffer_free); } int main(int argc, char ** argv) { evbase_t * evbase = event_base_new(); evhtp_t * htp = evhtp_new(evbase, NULL); evhtp_set_cb(htp, "/simple/", testcb, "simple"); evhtp_set_cb(htp, "/1/ping", testcb, "one"); evhtp_set_cb(htp, "/1/ping.json", testcb, "two"); evhtp_set_cb(htp, "/issue161", issue161cb, NULL); #ifndef EVHTP_DISABLE_EVTHR evhtp_use_threads_wexit(htp, NULL, NULL, 8, NULL); #endif evhtp_bind_socket(htp, "0.0.0.0", 8081, 2048); event_base_loop(evbase, 0); evhtp_unbind_socket(htp); evhtp_safe_free(htp, evhtp_free); evhtp_safe_free(evbase, event_base_free); return 0; } libevhtp-1.2.18/examples/test_client.c000066400000000000000000000040361342660753300177330ustar00rootroot00000000000000#include #include #include #include #include #include #include static void request_cb(evhtp_request_t * req, void * arg) { printf("hi %zu\n", evbuffer_get_length(req->buffer_in)); } static evhtp_res print_data(evhtp_request_t * req, evbuf_t * buf, void * arg) { printf("Got %zu bytes\n", evbuffer_get_length(buf)); return EVHTP_RES_OK; } static evhtp_res print_new_chunk_len(evhtp_request_t * req, uint64_t len, void * arg) { printf("started new chunk, %" PRIu64 " bytes\n", len); return EVHTP_RES_OK; } static evhtp_res print_chunk_complete(evhtp_request_t * req, void * arg) { printf("ended a single chunk\n"); return EVHTP_RES_OK; } static evhtp_res print_chunks_complete(evhtp_request_t * req, void * arg) { printf("all chunks read\n"); return EVHTP_RES_OK; } int main(int argc, char ** argv) { evbase_t * evbase; evhtp_connection_t * conn; evhtp_request_t * request; evbase = event_base_new(); conn = evhtp_connection_new(evbase, "104.27.150.225", 80); request = evhtp_request_new(request_cb, evbase); evhtp_request_set_hook(request, evhtp_hook_on_read, print_data, evbase); evhtp_request_set_hook(request, evhtp_hook_on_new_chunk, print_new_chunk_len, NULL); evhtp_request_set_hook(request, evhtp_hook_on_chunk_complete, print_chunk_complete, NULL); evhtp_request_set_hook(request, evhtp_hook_on_chunks_complete, print_chunks_complete, NULL); evhtp_headers_add_header(request->headers_out, evhtp_header_new("Host", "ieatfood.net", 0, 0)); evhtp_headers_add_header(request->headers_out, evhtp_header_new("User-Agent", "libevhtp", 0, 0)); evhtp_headers_add_header(request->headers_out, evhtp_header_new("Connection", "close", 0, 0)); evhtp_make_request(conn, request, htp_method_GET, "/"); event_base_loop(evbase, 0); evhtp_safe_free(evbase, event_base_free); return 0; } libevhtp-1.2.18/examples/test_htparse.c000066400000000000000000000273121342660753300201250ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "htparse.h" #define ADD_DATA_BUF(buf, name, data, len) do { \ strcat(buf, name ": '"); \ strncat(buf, data, len); \ strcat(buf, "'\n"); \ } while (0) struct testobj { char * name; char * data; htp_type type; }; static int _msg_begin(htparser * p) { char * buf = (char *)htparser_get_userdata(p); strcat(buf, "START\n"); return 0; } static int _method(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); if (htparser_get_method(p) == htp_method_UNKNOWN) { ADD_DATA_BUF(buf, "METHOD_UNKNOWN", b, s); return htp_method_UNKNOWN; } else { ADD_DATA_BUF(buf, "METHOD", b, s); } return 0; } static int _scheme(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "SCHEME", b, s); return 0; } static int _host(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "HOST", b, s); return 0; } static int _port(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "PORT", b, s); return 0; } static int _path(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "PATH", b, s); return 0; } static int _args(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "ARGS", b, s); return 0; } static int _uri(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "URI", b, s); return 0; } static int _hdrs_begin(htparser * p) { char * buf = (char *)htparser_get_userdata(p); strcat(buf, "HDRS_BEGIN\n"); return 0; } static int _hdr_key(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "HDR_KEY", b, s); return 0; } static int _hdr_val(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "HDR_VAL", b, s); return 0; } static int _hostname(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "HOSTNAME", b, s); return 0; } static int _hdrs_complete(htparser * p) { char * buf = (char *)htparser_get_userdata(p); strcat(buf, "HDRS_COMPLETE\n"); return 0; } static int _new_chunk(htparser * p) { char * buf = (char *)htparser_get_userdata(p); char tbuf[1024] = { 0 }; sprintf(tbuf, "NEW_CHUNK: %" PRIu64 "\n", htparser_get_content_length(p)); strcat(buf, tbuf); return 0; } static int _chunk_complete(htparser * p) { char * buf = (char *)htparser_get_userdata(p); strcat(buf, "END_CHUNK\n"); return 0; } static int _chunks_complete(htparser * p) { char * buf = (char *)htparser_get_userdata(p); strcat(buf, "END_CHUNKS\n"); return 0; } static int _body(htparser * p, const char * b, size_t s) { char * buf = (char *)htparser_get_userdata(p); ADD_DATA_BUF(buf, "BODY", b, s); return 0; } static int _msg_complete(htparser * p) { char * buf = (char *)htparser_get_userdata(p); strcat(buf, "MSG_COMPLETE\n"); return 0; } htparse_hooks hooks = { .on_msg_begin = _msg_begin, .method = _method, .scheme = _scheme, .host = _host, .port = _port, .path = _path, .args = _args, .uri = _uri, .on_hdrs_begin = _hdrs_begin, .hdr_key = _hdr_key, .hdr_val = _hdr_val, .hostname = _hostname, .on_hdrs_complete = _hdrs_complete, .on_new_chunk = _new_chunk, .on_chunk_complete = _chunk_complete, .on_chunks_complete = _chunks_complete, .body = _body, .on_msg_complete = _msg_complete }; struct testobj t1 = { .name = "small GET request", .type = htp_type_request, .data = "GET / HTTP/1.0\r\n\r\n" }; struct testobj t2 = { .name = "GET request with arguments", .type = htp_type_request, .data = "GET /test?a=b&c=d HTTP/1.1\r\n\r\n" }; struct testobj t3 = { .name = "POST request with 4 bytes of data", .type = htp_type_request, .data = "POST /foo/bar HTTP/1.0\r\n" "Content-Length: 4\r\n\r\n" "abcd" }; struct testobj t4 = { .name = "Simple POST with chunked data", .type = htp_type_request, .data = "POST /test/ HTTP/1.1\r\n" "Transfer-Encoding: chunked\r\n\r\n" "1e\r\nall your base are belong to us\r\n" "0\r\n" "\r\n" }; struct testobj t5 = { .name = "POST request with multiple chunks", .type = htp_type_request, .data = "POST /test/ HTTP/1.1\r\n" "Connection: Keep-Alive\r\n" "Transfer-Encoding: chunked\r\n\r\n" "23\r\n" "This is the data in the first chunk" "\r\n" "1A\r\n" "and this is the second one" "\r\n" "3\r\n" "foo\r\n" "6\r\n" "barbaz\r\n" "0\r\n\r\n" }; struct testobj t6 = { .name = "GET request with a host header", .type = htp_type_request, .data = "GET /test/ HTTP/1.0\r\n" "Host: ieatfood.net\r\n\r\n" }; struct testobj t7 = { .name = "GET request with an empty header value", .type = htp_type_request, .data = "GET /test/ HTTP/1.0\r\n" "Header1: value1\r\n" "Header2: \r\n" "Header3: value3\r\n\r\n" }; struct testobj t8 = { .name = "GET request with a multi-line header value", .type = htp_type_request, .data = "GET /test/ HTTP/1.1\r\n" "Header1: value1\r\n" "Header2: val\r\n" "\tue\r\n" "\t2\r\n" "Header3: value3\r\n\r\n" }; struct testobj t9 = { .name = "[FAILURE TEST] GET REQUEST with LF instead of CRLF on header value", .type = htp_type_request, .data = "GET /test/ HTTP/1.1\r\n" "Header: value\n\n" }; struct testobj t10 = { .name = "[FAILURE TEST] GET request with invalid protocol", .type = htp_type_request, .data = "GET /test/ fdasfs\r\n\r\n" }; struct testobj t11 = { .name = "[FALURE TEST] POST request with invalid chunk length", .type = htp_type_request, .data = "POST /test/ HTTP/1.1\r\n" "Transfer-Encoding: chunked\r\n\r\n" "3\r\n" "foo" "\r\n" "A\r\n" "foobar\r\n" "3\r\n" "baz\r\n" "0\r\n\r\n" }; struct testobj t12 = { .name = "Simple GET on a FTP scheme", .type = htp_type_request, .data = "GET ftp://test.com/foo/bar HTTP/1.1\r\n\r\n" }; struct testobj t13 = { .name = "Multiple GET requests in HTTP/1.1 request", .type = htp_type_request, .data = "GET /request1 HTTP/1.1\r\n\r\n" "GET /request2 HTTP/1.0\r\n" "Connection: close\r\n\r\n" }; struct testobj t14 = { .name = "[FAILURE TEST] invalid request type", .type = htp_type_request, .data = "DERP /test HTTP/1.1\r\n\r\n" }; struct testobj t15 = { .name = "http SCHEME request with port / args / headers", .type = htp_type_request, .data = "GET http://ieatfood.net:80/index.html?foo=bar&baz=buz HTTP/1.0\r\n" "Host: ieatfood.net\r\n" "Header: value\r\n\r\n" }; struct testobj t16 = { .name = "GET request which should run all callbacks minus scheme stuff, this includes multiple requests", .type = htp_type_request, .data = "GET /test1?a=b&c=d&e=f HTTP/1.1\r\n" "Content-Length: 6\r\n\r\n" "foobar" "GET /test2 HTTP/1.1\r\n" "Header: test2\r\n\r\n" "POST /test/ HTTP/1.1\r\n" "Transfer-Encoding: chunked\r\n\r\n" "23\r\n" "This is the data in the first chunk" "\r\n" "1A\r\n" "and this is the second one" "\r\n" "3\r\n" "foo\r\n" "6\r\n" "barbaz\r\n" "0\r\n\r\n" "GET /test/ HTTP/1.1\r\n" "Host: ieatfood.net\r\n" "Connection: close\r\n\r\n" }; struct testobj t17 = { .name = "Like the last test, but with scheme requests", .type = htp_type_request, .data = "GET http://ieatfood.net/test1?a=b&c=d&e=f HTTP/1.1\r\n" "Content-Length: 6\r\n\r\n" "foobar" "GET https://ieatfood.net:443/test2 HTTP/1.1\r\n" "Header: test2\r\n\r\n" "POST /test/ HTTP/1.1\r\n" "Transfer-Encoding: chunked\r\n\r\n" "23\r\n" "This is the data in the first chunk" "\r\n" "1A\r\n" "and this is the second one" "\r\n" "3\r\n" "foo\r\n" "6\r\n" "barbaz\r\n" "0\r\n\r\n" "GET ftp://ackers.net:21/test/ HTTP/1.1\r\n" "Host: ackers.net\r\n" "Connection: close\r\n\r\n" }; struct testobj t18 = { .name = "scheme request with empty path", .type = htp_type_request, .data = "GET http://ackers.net HTTP/1.0\r\n\r\n" }; struct testobj t19 = { .name = "basic HTTP RESPONSE", .type = htp_type_response, .data = "HTTP/1.1 200 OK\r\n\r\n" }; struct testobj t20 = { .name = "HTTP RESPONSE with body", .type = htp_type_response, .data = "HTTP/1.1 200 OK\r\n" "Content-Length: 6\r\n\r\n" "foobar" }; struct testobj t21 = { .name = "HTTP RESPONSE with chunked data", .type = htp_type_response, .data = "HTTP/1.1 200 OK\r\n" "Transfer-Encoding: chunked\r\n\r\n" "23\r\n" "This is the data in the first chunk" "\r\n" "1A\r\n" "and this is the second one" "\r\n" "3\r\n" "foo\r\n" "6\r\n" "barbaz\r\n" "0\r\n\r\n" }; struct testobj t22 = { .name = "Header key with no value", .type = htp_type_request, .data = "GET / HTTP/1.1\r\n" "Accept\r\n\r\n" }; static int _run_test(htparser * p, struct testobj * obj) { size_t data_sz; size_t parsed_sz; char result_buf[5000] = { 0 }; htparser_init(p, obj->type); htparser_set_userdata(p, result_buf); data_sz = strlen(obj->data); parsed_sz = htparser_run(p, &hooks, obj->data, data_sz); strcat(result_buf, "ERROR_STR: "); strcat(result_buf, htparser_get_strerror(p)); strcat(result_buf, "\n"); printf("%s\n", obj->name); printf("-----------------\n"); printf("%s", result_buf); printf("\n"); return 0; } int main(int argc, char ** argv) { htparser * parser; parser = htparser_new(); assert(parser != NULL); _run_test(parser, &t1); _run_test(parser, &t2); _run_test(parser, &t3); _run_test(parser, &t4); _run_test(parser, &t5); _run_test(parser, &t6); _run_test(parser, &t7); _run_test(parser, &t8); _run_test(parser, &t9); _run_test(parser, &t10); _run_test(parser, &t11); _run_test(parser, &t12); _run_test(parser, &t13); _run_test(parser, &t14); _run_test(parser, &t15); _run_test(parser, &t16); _run_test(parser, &t17); _run_test(parser, &t18); _run_test(parser, &t19); _run_test(parser, &t20); _run_test(parser, &t21); _run_test(parser, &t22); return 0; } libevhtp-1.2.18/examples/test_perf.c000066400000000000000000000072661342660753300174210ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "internal.h" #include "evhtp/evhtp.h" static int num_threads = 0; static char * baddr = "127.0.0.1"; static uint16_t bport = 8081; static int backlog = 1024; static int nodelay = 0; static int defer_accept = 0; static int reuse_port = 0; static size_t payload_sz = 100; static void response_cb(evhtp_request_t * r, void * a) { evbuffer_add_reference(r->buffer_out, (const char *)a, payload_sz, NULL, NULL); evhtp_send_reply(r, EVHTP_RES_OK); } int main(int argc, char ** argv) { extern char * optarg; extern int optind; extern int opterr; extern int optopt; int c; while ((c = getopt(argc, argv, "t:a:p:b:ndrs:")) != -1) { switch (c) { case 't': num_threads = atoi(optarg); break; case 'a': baddr = strdup(optarg); break; case 'p': bport = atoi(optarg); break; case 'b': backlog = atoll(optarg); break; case 'n': nodelay = 1; break; case 'd': defer_accept = 1; break; case 'r': reuse_port = 1; break; case 's': payload_sz = atoll(optarg); break; default: fprintf(stdout, "Usage: %s [flags]\n", argv[0]); fprintf(stdout, " -t : number of worker threads [Default: %d]\n", num_threads); fprintf(stdout, " -a : bind address [Default: %s]\n", baddr); fprintf(stdout, " -p : bind port [Default: %d]\n", bport); fprintf(stdout, " -b : listen backlog [Default: %d]\n", backlog); fprintf(stdout, " -s : size of the response [Default: %zu]\n", payload_sz); fprintf(stdout, " -n : disable nagle (nodelay) [Default: %s]\n", nodelay ? "true" : "false"); fprintf(stdout, " -d : enable deferred accept [Default: %s]\n", defer_accept ? "true" : "false"); fprintf(stdout, " -r : enable linux reuseport [Default: %s]\n", reuse_port ? "true" : "false"); exit(EXIT_FAILURE); } /* switch */ } { struct event_base * evbase; evhtp_t * htp; char payload[payload_sz]; evbase = event_base_new(); evhtp_alloc_assert(evbase); htp = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp); evhtp_set_parser_flags(htp, EVHTP_PARSE_QUERY_FLAG_LENIENT); if (nodelay) { evhtp_enable_flag(htp, EVHTP_FLAG_ENABLE_NODELAY); } if (defer_accept) { evhtp_enable_flag(htp, EVHTP_FLAG_ENABLE_DEFER_ACCEPT); } if (reuse_port) { evhtp_enable_flag(htp, EVHTP_FLAG_ENABLE_REUSEPORT); } memset(payload, 0x42, payload_sz); evhtp_assert(evhtp_set_cb(htp, "/data", response_cb, payload)); #ifndef EVHTP_DISABLE_EVTHR if (num_threads > 0) { evhtp_assert(evhtp_use_threads_wexit(htp, NULL, NULL, num_threads, NULL) != -1); } #endif evhtp_errno_assert(evhtp_bind_socket(htp, baddr, bport, backlog) >= 0); event_base_loop(evbase, 0); } return 0; } /* main */ libevhtp-1.2.18/examples/test_proxy.c000066400000000000000000000067451342660753300176470ustar00rootroot00000000000000#include #include #include #include #include #include #include #include int make_request(evbase_t * evbase, evthr_t * evthr, const char * const host, const short port, const char * const path, evhtp_headers_t * headers, evhtp_callback_cb cb, void * arg) { evhtp_connection_t * conn; evhtp_request_t * request; conn = evhtp_connection_new(evbase, host, port); conn->thread = evthr; request = evhtp_request_new(cb, arg); evhtp_headers_add_header(request->headers_out, evhtp_header_new("Host", "localhost", 0, 0)); evhtp_headers_add_header(request->headers_out, evhtp_header_new("User-Agent", "libevhtp", 0, 0)); evhtp_headers_add_header(request->headers_out, evhtp_header_new("Connection", "close", 0, 0)); evhtp_headers_add_headers(request->headers_out, headers); printf("Making backend request...\n"); evhtp_make_request(conn, request, htp_method_GET, path); printf("Ok.\n"); return 0; } static void backend_cb(evhtp_request_t * backend_req, void * arg) { evhtp_request_t * frontend_req = (evhtp_request_t *)arg; evbuffer_prepend_buffer(frontend_req->buffer_out, backend_req->buffer_in); evhtp_headers_add_headers(frontend_req->headers_out, backend_req->headers_in); /* * char body[1024] = { '\0' }; * ev_ssize_t len = evbuffer_copyout(frontend_req->buffer_out, body, sizeof(body)); * printf("Backend %zu: %s\n", len, body); */ evhtp_send_reply(frontend_req, EVHTP_RES_OK); evhtp_request_resume(frontend_req); } static void frontend_cb(evhtp_request_t * req, void * arg) { int * aux; int thr; aux = (int *)evthr_get_aux(req->conn->thread); thr = *aux; printf(" Received frontend request on thread %d... ", thr); /* Pause the frontend request while we run the backend requests. */ evhtp_request_pause(req); make_request(evthr_get_base(req->conn->thread), req->conn->thread, "127.0.0.1", 80, req->uri->path->full, req->headers_in, backend_cb, req); printf("Ok.\n"); } /* Terminate gracefully on SIGTERM */ void sigterm_cb(int fd, short event, void * arg) { evbase_t * evbase = (evbase_t *)arg; struct timeval tv = { .tv_usec = 100000, .tv_sec = 0 }; /* 100 ms */ event_base_loopexit(evbase, &tv); } void init_thread_cb(evhtp_t * htp, evthr_t * thr, void * arg) { static int aux = 0; printf("Spinning up a thread: %d\n", ++aux); evthr_set_aux(thr, &aux); } int main(int argc, char ** argv) { struct event *ev_sigterm; evbase_t * evbase = event_base_new(); evhtp_t * evhtp = evhtp_new(evbase, NULL); evhtp_set_gencb(evhtp, frontend_cb, NULL); #if 0 #ifndef EVHTP_DISABLE_SSL evhtp_ssl_cfg_t scfg1 = { 0 }; scfg1.pemfile = "./server.pem"; scfg1.privfile = "./server.pem"; evhtp_ssl_init(evhtp, &scfg1); #endif #endif evhtp_use_threads_wexit(evhtp, init_thread_cb, NULL, 8, NULL); #ifndef WIN32 ev_sigterm = evsignal_new(evbase, SIGTERM, sigterm_cb, evbase); evsignal_add(ev_sigterm, NULL); #endif evhtp_bind_socket(evhtp, "0.0.0.0", 8081, 1024); event_base_loop(evbase, 0); printf("Clean exit\n"); return 0; } libevhtp-1.2.18/examples/test_query.c000066400000000000000000000172701342660753300176260ustar00rootroot00000000000000#include #include #include #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(""); 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; } evhtp_safe_free(query, evhtp_query_free); 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); unsigned char unescaped_string[1024] = { 0 }; unsigned char * escaped_string = "foo?bar=foo%23bar&baz=ONE%20TWO%20&%20THRee%20-foUr"; unsigned char *ptr = unescaped_string; evhtp_unescape_string(&ptr, escaped_string, strlen(escaped_string)); printf("%s\n", ptr); return 0; } libevhtp-1.2.18/examples/test_vhost.c000066400000000000000000000025511342660753300176200ustar00rootroot00000000000000#include #include #include #include #include #include void testcb(evhtp_request_t * req, void * a) { evbuffer_add_reference(req->buffer_out, "foobar", 6, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_OK); } int main(int argc, char ** argv) { evbase_t * evbase = event_base_new(); evhtp_t * evhtp = evhtp_new(evbase, NULL); evhtp_t * v1 = evhtp_new(evbase, NULL); evhtp_t * v2 = evhtp_new(evbase, NULL); evhtp_set_cb(v1, "/host1", NULL, "host1.com"); evhtp_set_cb(v2, "/localhost", testcb, "localhost"); evhtp_add_vhost(evhtp, "host1.com", v1); evhtp_add_vhost(evhtp, "localhost", v2); evhtp_add_alias(v2, "127.0.0.1"); evhtp_add_alias(v2, "localhost"); evhtp_add_alias(v2, "localhost:8081"); #if 0 scfg1.pemfile = "./server.pem"; scfg1.privfile = "./server.pem"; scfg2.pemfile = "./server1.pem"; scfg2.pemfile = "./server1.pem"; evhtp_ssl_init(evhtp, &scfg1); evhtp_ssl_init(v1, &scfg2); evhtp_ssl_init(v2, &scfg2); #endif evhtp_bind_socket(evhtp, "0.0.0.0", 8081, 1024); event_base_loop(evbase, 0); evhtp_unbind_socket(evhtp); evhtp_safe_free(v2, evhtp_free); evhtp_safe_free(v1, evhtp_free); evhtp_safe_free(evhtp, evhtp_free); evhtp_safe_free(evbase, event_base_free); return 0; } libevhtp-1.2.18/examples/thread_design.c000066400000000000000000000241441342660753300202200ustar00rootroot00000000000000/* * How to exploit the wonders of libevhtp's threading model to avoid using * libevent's locking API. * * In this example we use Redis's Async API (Libhiredis) store and retr the following * information for a request: * * Total requests seen. * Total requests seen by the requestors IP address. * All of the source ports seen used by the requestors IP address. * * We do this all using libevhtp's builtin thread-pool model, without the use of * mutexes or evthread_use_pthreads() type stuff. * * The technique is simple: * 1. Create your evhtp_t structure, assign callbacks like usual. * 2. Call evhtp_use_threads() with a thread init callback. * 3. Each time a thread starts, the thread init callback you defined will be * called with information about that thread. * * First a bit of information about how evhtp does threading: * libevhtp uses the evthr library, which works more like a threaded * co-routine than a threadpool. Each evthr in a pool has its own unique * event_base (and each evthr runs its own event_base_loop()). Under the * hood when libevhtp sends a request to a thread, it calls * "evthr_pool_defer(pool, _run_connection_in_thread, ...). * * The evthr library then finds a thread inside the pool with the lowest backlog, * sends a packet over that threads socketpair containing information about what * function to execute. It uses socketpairs because they can be treated as * an event, thus able to be processed in a threads own unique * event_base_loop(). * * Knowing that, a connection in evhtp is never associated with the initial * event_base that was passed to evhtp_new(), but instead the connection * uses the evthr's unique event_base. This is what makes libevhtp's * safe from thread-related race conditions. * * 4. Use the thread init callback as a place to put event type things on the * threads event_base() instead of using the global one. * * In this code, that function is app_init_thread(). When this function is * called, the first argument is the evthr_t of the thread that just * started. This function uses "evthr_get_base(thread)" to get the * event_base associated with this specific thread. * * Using that event_base, the function will start up an async redis * connection. This redis connection is now tied to that thread, and can be * used on a threaded request without locking (remember that your request * has the same event_base as the thread it was executed in). * * We allocate a dummy structure "struct app" and then call * "evthr_set_aux(thread, app)". This function sets some aux data which can * be fetched at any point using evthr_get_aux(thread). We use this later on * inside process_request() * * This part is the secret to evhtp threading success. * * 5. When a request has been fully processed, it will call the function * "app_process_request()". Note here that the "arg" argument is NULL since no * arguments were passed to evhtp_set_gencb(). * * Since we want to do a bunch of redis stuff before sending a reply to the * client, we must fetch the "struct app" data we allocated and set for the * thread associated with this request (struct app * app = * evthr_get_aux(thread);). * * struct app has our thread-specific redis connection ctx, so using that * redisAsyncCommand() is called a bunch of times to queue up the commands * which will be run. * * The last part of this technique is to call the function * "evhtp_request_pause()". This essentially tells evhtp to flip the * read-side of the connections file-descriptor OFF (This avoids potential * situations where a client disconnected before all of the redis commands * executed). * * 6. Each redis command is executed in order, and each callback will write to * the requests output_buffer with relevant information from the result. * * 7. The last redis callback executed here is "redis_get_srcport_cb". It is * the job os this function to call evhtp_send_reply() and then * evhtp_request_resume(). * * Using this design in conjunction with libevhtp makes the world an easier * place to code. * * Compile: gcc thread_design.c -o thread_design -levhtp -levent -lhiredis * */ #include #include #include #include #include #include #include #include #include struct app_parent { evhtp_t * evhtp; evbase_t * evbase; char * redis_host; uint16_t redis_port; }; struct app { struct app_parent * parent; evbase_t * evbase; redisAsyncContext * redis; }; static evthr_t * get_request_thr(evhtp_request_t * request) { evhtp_connection_t * htpconn; evthr_t * thread; htpconn = evhtp_request_get_connection(request); thread = htpconn->thread; return thread; } void redis_global_incr_cb(redisAsyncContext * redis, void * redis_reply, void * arg) { redisReply * reply = redis_reply; evhtp_request_t * request = arg; printf("global_incr_cb(%p)\n", request); if (reply == NULL || reply->type != REDIS_REPLY_INTEGER) { evbuffer_add_printf(request->buffer_out, "redis_global_incr_cb() failed\n"); return; } evbuffer_add_printf(request->buffer_out, "Total requests = %lld\n", reply->integer); } void redis_srcaddr_incr_cb(redisAsyncContext * redis, void * redis_reply, void * arg) { redisReply * reply = redis_reply; evhtp_request_t * request = arg; printf("incr_cb(%p)\n", request); if (reply == NULL || reply->type != REDIS_REPLY_INTEGER) { evbuffer_add_printf(request->buffer_out, "redis_srcaddr_incr_cb() failed\n"); return; } evbuffer_add_printf(request->buffer_out, "Requests from this source IP = %lld\n", reply->integer); } void redis_set_srcport_cb(redisAsyncContext * redis, void * redis_reply, void * arg) { redisReply * reply = redis_reply; evhtp_request_t * request = arg; printf("set_srcport_cb(%p)\n", request); if (reply == NULL || reply->type != REDIS_REPLY_INTEGER) { evbuffer_add_printf(request->buffer_out, "redis_set_srcport_cb() failed\n"); return; } if (!reply->integer) { evbuffer_add_printf(request->buffer_out, "This source port has been seen already.\n"); } else { evbuffer_add_printf(request->buffer_out, "This source port has never been seen.\n"); } } void redis_get_srcport_cb(redisAsyncContext * redis, void * redis_reply, void * arg) { redisReply * reply = redis_reply; evhtp_request_t * request = arg; int i; printf("get_srcport_cb(%p)\n", request); if (reply == NULL || reply->type != REDIS_REPLY_ARRAY) { evbuffer_add_printf(request->buffer_out, "redis_get_srcport_cb() failed.\n"); return; } evbuffer_add_printf(request->buffer_out, "source ports which have been seen for your ip:\n"); for (i = 0; i < reply->elements; i++) { redisReply * elem = reply->element[i]; evbuffer_add_printf(request->buffer_out, "%s ", elem->str); } evbuffer_add(request->buffer_out, "\n", 1); /* final callback for redis, so send the response */ evhtp_send_reply(request, EVHTP_RES_OK); evhtp_request_resume(request); } void app_process_request(evhtp_request_t * request, void * arg) { struct sockaddr_in * sin; struct app_parent * app_parent; struct app * app; evthr_t * thread; evhtp_connection_t * conn; char tmp[1024]; printf("process_request(%p)\n", request); thread = get_request_thr(request); conn = evhtp_request_get_connection(request); app = (struct app *)evthr_get_aux(thread); sin = (struct sockaddr_in *)conn->saddr; evutil_inet_ntop(AF_INET, &sin->sin_addr, tmp, sizeof(tmp)); /* increment a global counter of hits on redis */ redisAsyncCommand(app->redis, redis_global_incr_cb, (void *)request, "INCR requests:total"); /* increment a counter for hits from this source address on redis */ redisAsyncCommand(app->redis, redis_srcaddr_incr_cb, (void *)request, "INCR requests:ip:%s", tmp); /* add the source port of this request to a source-specific set */ redisAsyncCommand(app->redis, redis_set_srcport_cb, (void *)request, "SADD requests:ip:%s:ports %d", tmp, ntohs(sin->sin_port)); /* get all of the ports this source address has used */ redisAsyncCommand(app->redis, redis_get_srcport_cb, (void *)request, "SMEMBERS requests:ip:%s:ports", tmp); /* pause the request processing */ evhtp_request_pause(request); } void app_init_thread(evhtp_t * htp, evthr_t * thread, void * arg) { struct app_parent * app_parent; struct app * app; app_parent = (struct app_parent *)arg; app = calloc(sizeof(struct app), 1); app->parent = app_parent; app->evbase = evthr_get_base(thread); app->redis = redisAsyncConnect(app_parent->redis_host, app_parent->redis_port); redisLibeventAttach(app->redis, app->evbase); evthr_set_aux(thread, app); } int main(int argc, char ** argv) { evbase_t * evbase; evhtp_t * evhtp; struct app_parent * app_p; evbase = event_base_new(); evhtp = evhtp_new(evbase, NULL); app_p = calloc(sizeof(struct app_parent), 1); app_p->evhtp = evhtp; app_p->evbase = evbase; app_p->redis_host = "127.0.0.1"; app_p->redis_port = 6379; evhtp_set_gencb(evhtp, app_process_request, NULL); evhtp_use_threads(evhtp, app_init_thread, 4, app_p); evhtp_bind_socket(evhtp, "127.0.0.1", 9090, 1024); event_base_loop(evbase, 0); return 0; } libevhtp-1.2.18/examples/v6_v4.c000066400000000000000000000022171342660753300163610ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "evhtp-internal.h" #include "evhtp.h" static void _req_cb(evhtp_request_t * req, void * arg) { const char * ver = (const char *)arg; evbuffer_add(req->buffer_out, ver, strlen(ver)); evhtp_send_reply(req, EVHTP_RES_OK); } int main(int argc, char ** argv) { struct event_base * evbase; evhtp_t * htp_v6; evhtp_t * htp_v4; int rc; evbase = event_base_new(); evhtp_alloc_assert(evbase); htp_v6 = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp_v6); htp_v4 = evhtp_new(evbase, NULL); evhtp_alloc_assert(htp_v4); evhtp_set_gencb(htp_v6, _req_cb, (void *)"ipv6"); evhtp_set_gencb(htp_v4, _req_cb, (void *)"ipv4"); rc = evhtp_bind_socket(htp_v6, "ipv6:::/128", 9090, 1024); evhtp_errno_assert(rc != -1); rc = evhtp_bind_socket(htp_v4, "ipv4:0.0.0.0", 9090, 1024); evhtp_errno_assert(rc != -1); event_base_loop(evbase, 0); return 0; } /* main */libevhtp-1.2.18/include/000077500000000000000000000000001342660753300150545ustar00rootroot00000000000000libevhtp-1.2.18/include/evhtp.h000066400000000000000000000001621342660753300163520ustar00rootroot00000000000000/** * @file evhtp.h * */ #ifndef __EVHTP_BASE_H__ #define __EVHTP_BASE_H__ #include #endif libevhtp-1.2.18/include/evhtp/000077500000000000000000000000001342660753300162025ustar00rootroot00000000000000libevhtp-1.2.18/include/evhtp/config.h.in000066400000000000000000000037431342660753300202340ustar00rootroot00000000000000#ifndef __EVHTP_CONFIG_H__ #define __EVHTP_CONFIG_H__ #ifdef __cplusplus extern "C" { #endif #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 #if defined(_MSC_VER) && _MSC_VER >= 1500 /* MSVC 2008 */ # define DEPRECATED(message) __declspec(deprecated(message)) #elif defined(__clang__) && defined(__has_feature) # if __has_feature(attribute_deprecated_with_message) # define DEPRECATED(message) __attribute__ ((deprecated(message))) # endif # elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) # define DEPRECATED(message) __attribute__ ((deprecated(message))) # elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) # define DEPRECATED(message) __attribute__((__deprecated__)) # else # define DEPRECATED(message) #endif #undef EVHTP_DISABLE_EVTHR #undef EVHTP_DISABLE_REGEX #undef EVHTP_DISABLE_SSL #undef EVHTP_DISABLE_EVTHR #undef EVHTP_DEBUG #cmakedefine EVHTP_DISABLE_EVTHR #cmakedefine EVHTP_DISABLE_REGEX #cmakedefine EVHTP_DISABLE_SSL #cmakedefine EVHTP_DISABLE_EVTHR #cmakedefine EVHTP_USE_TCMALLOC #cmakedefine EVHTP_USE_JEMALLOC #cmakedefine EVHTP_USE_TCMALLOC #cmakedefine EVHTP_DEBUG #cmakedefine EVHTP_DISABLE_MEMFUNCTIONS #ifndef EVHTP_DISABLE_REGEX #include #endif #ifdef EVHTP_USE_TCMALLOC #include #define malloc(size) tc_malloc(size) #define calloc(count, size) tc_calloc(count, size) #define realloc(ptr, size) tc_realloc(ptr, size) #define free(ptr) tc_free(ptr) #endif #ifdef EVHTP_USE_JEMALLOC #define JEMALLOC_NO_DEMANGLE #include #define malloc(size) je_malloc(size) #define calloc(count, size) je_calloc(count, size) #define realloc(ptr, size) je_realloc(ptr, size) #define free(ptr) je_free(ptr) #endif #ifdef __cplusplus } #endif #endif libevhtp-1.2.18/include/evhtp/evhtp.h000066400000000000000000001347071342660753300175150ustar00rootroot00000000000000/** * @file evhtp.h */ #include #ifndef __EVHTP__H__ #define __EVHTP__H__ /** @file */ #ifndef EVHTP_DISABLE_EVTHR #include #endif #include #ifndef EVHTP_DISABLE_REGEX #include #endif #include #include #include #include #include #ifndef EVHTP_DISABLE_SSL #include #include #include #include #include #endif #ifdef __cplusplus extern "C" { #endif struct evhtp_callback; struct evhtp_callbacks; struct evhtp_kvs; #ifndef EVHTP_DISABLE_SSL typedef SSL_SESSION evhtp_ssl_sess_t; typedef SSL evhtp_ssl_t; typedef SSL_CTX evhtp_ssl_ctx_t; typedef X509 evhtp_x509_t; typedef X509_STORE_CTX evhtp_x509_store_ctx_t; #if OPENSSL_VERSION_NUMBER < 0x10100000L typedef unsigned char evhtp_ssl_data_t; #else typedef const unsigned char evhtp_ssl_data_t; #endif #else typedef void evhtp_ssl_sess_t; typedef void evhtp_ssl_t; typedef void evhtp_ssl_ctx_t; typedef void evhtp_x509_t; typedef void evhtp_x509_store_ctx_t; #endif typedef struct evbuffer evbuf_t; typedef struct event event_t; typedef struct evconnlistener evserv_t; typedef struct bufferevent evbev_t; #ifdef EVHTP_DISABLE_EVTHR typedef struct event_base evbase_t; typedef void evthr_t; typedef void evthr_pool_t; typedef void evhtp_mutex_t; #else typedef pthread_mutex_t evhtp_mutex_t; #endif typedef struct evhtp evhtp_t; typedef struct evhtp_defaults evhtp_defaults_t; typedef struct evhtp_callbacks evhtp_callbacks_t; typedef struct evhtp_callback evhtp_callback_t; typedef struct evhtp_kv evhtp_kv_t; typedef struct evhtp_kvs evhtp_kvs_t; typedef struct evhtp_uri evhtp_uri_t; typedef struct evhtp_path evhtp_path_t; typedef struct evhtp_authority evhtp_authority_t; typedef struct evhtp_request evhtp_request_t; typedef struct evhtp_hooks evhtp_hooks_t; typedef struct evhtp_connection evhtp_connection_t; typedef struct evhtp_ssl_cfg evhtp_ssl_cfg_t; typedef struct evhtp_alias evhtp_alias_t; typedef uint16_t evhtp_res; typedef uint8_t evhtp_error_flags; typedef struct evhtp_kv evhtp_header_t; typedef struct evhtp_kvs evhtp_headers_t; typedef struct evhtp_kvs evhtp_query_t; enum evhtp_ssl_scache_type { evhtp_ssl_scache_type_disabled = 0, evhtp_ssl_scache_type_internal, evhtp_ssl_scache_type_user, evhtp_ssl_scache_type_builtin }; /** * @brief types associated with where a developer can hook into * during the request processing cycle. */ enum evhtp_hook_type { evhtp_hook_on_header, /**< type which defines to hook after one header has been parsed */ evhtp_hook_on_headers, /**< type which defines to hook after all headers have been parsed */ evhtp_hook_on_path, /**< type which defines to hook once a path has been parsed */ evhtp_hook_on_read, /**< type which defines to hook whenever the parser recieves data in a body */ evhtp_hook_on_request_fini, /**< type which defines to hook before the request is free'd */ evhtp_hook_on_connection_fini, evhtp_hook_on_new_chunk, evhtp_hook_on_chunk_complete, evhtp_hook_on_chunks_complete, 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_event, evhtp_hook_on_conn_error, /**< type which defines to hook whenever a connection error occurs */ evhtp_hook__max }; enum evhtp_callback_type { evhtp_callback_type_hash, evhtp_callback_type_glob, #ifndef EVHTP_DISABLE_REGEX evhtp_callback_type_regex, #endif }; enum evhtp_proto { EVHTP_PROTO_INVALID, EVHTP_PROTO_10, EVHTP_PROTO_11 }; enum evhtp_type { evhtp_type_client, evhtp_type_server }; typedef enum evhtp_hook_type evhtp_hook_type; typedef enum evhtp_callback_type evhtp_callback_type; typedef enum evhtp_proto evhtp_proto; 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_thread_exit_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_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, struct evbuffer * 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); #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 EVP_PKEY * (* evhtp_ssl_decrypt_cb)(char * privfile); typedef int (* evhtp_ssl_scache_add)(evhtp_connection_t * connection, evhtp_ssl_data_t * sid, int sid_len, evhtp_ssl_sess_t * sess); typedef void (* evhtp_ssl_scache_del)(evhtp_t * htp, evhtp_ssl_data_t * sid, int sid_len); typedef evhtp_ssl_sess_t * (* evhtp_ssl_scache_get)(evhtp_connection_t * connection, evhtp_ssl_data_t * sid, int sid_len); typedef void * (* evhtp_ssl_scache_init)(evhtp_t *); #endif #define EVHTP_VERSION "1.2.18" #define EVHTP_VERSION_MAJOR 1 #define EVHTP_VERSION_MINOR 2 #define EVHTP_VERSION_PATCH 18 #define evhtp_headers_iterator evhtp_kvs_iterator #define EVHTP_RES_ERROR 0 #define EVHTP_RES_PAUSE 1 #define EVHTP_RES_FATAL 2 #define EVHTP_RES_USER 3 #define EVHTP_RES_DATA_TOO_LONG 4 #define EVHTP_RES_OK 200 #ifndef DOXYGEN_SHOULD_SKIP_THIS #define EVHTP_RES_100 100 #define EVHTP_RES_CONTINUE 100 #define EVHTP_RES_SWITCH_PROTO 101 #define EVHTP_RES_PROCESSING 102 #define EVHTP_RES_URI_TOOLONG 122 #define EVHTP_RES_200 200 #define EVHTP_RES_CREATED 201 #define EVHTP_RES_ACCEPTED 202 #define EVHTP_RES_NAUTHINFO 203 #define EVHTP_RES_NOCONTENT 204 #define EVHTP_RES_RSTCONTENT 205 #define EVHTP_RES_PARTIAL 206 #define EVHTP_RES_MSTATUS 207 #define EVHTP_RES_IMUSED 226 #define EVHTP_RES_300 300 #define EVHTP_RES_MCHOICE 300 #define EVHTP_RES_MOVEDPERM 301 #define EVHTP_RES_FOUND 302 #define EVHTP_RES_SEEOTHER 303 #define EVHTP_RES_NOTMOD 304 #define EVHTP_RES_USEPROXY 305 #define EVHTP_RES_SWITCHPROXY 306 #define EVHTP_RES_TMPREDIR 307 #define EVHTP_RES_400 400 #define EVHTP_RES_BADREQ 400 #define EVHTP_RES_UNAUTH 401 #define EVHTP_RES_PAYREQ 402 #define EVHTP_RES_FORBIDDEN 403 #define EVHTP_RES_NOTFOUND 404 #define EVHTP_RES_METHNALLOWED 405 #define EVHTP_RES_NACCEPTABLE 406 #define EVHTP_RES_PROXYAUTHREQ 407 #define EVHTP_RES_TIMEOUT 408 #define EVHTP_RES_CONFLICT 409 #define EVHTP_RES_GONE 410 #define EVHTP_RES_LENREQ 411 #define EVHTP_RES_PRECONDFAIL 412 #define EVHTP_RES_ENTOOLARGE 413 #define EVHTP_RES_URITOOLARGE 414 #define EVHTP_RES_UNSUPPORTED 415 #define EVHTP_RES_RANGENOTSC 416 #define EVHTP_RES_EXPECTFAIL 417 #define EVHTP_RES_IAMATEAPOT 418 #define EVHTP_RES_500 500 #define EVHTP_RES_SERVERR 500 #define EVHTP_RES_NOTIMPL 501 #define EVHTP_RES_BADGATEWAY 502 #define EVHTP_RES_SERVUNAVAIL 503 #define EVHTP_RES_GWTIMEOUT 504 #define EVHTP_RES_VERNSUPPORT 505 #define EVHTP_RES_BWEXEED 509 #endif struct evhtp_defaults { evhtp_callback_cb cb; evhtp_pre_accept_cb pre_accept; evhtp_post_accept_cb post_accept; void * cbarg; void * pre_accept_cbarg; void * post_accept_cbarg; }; struct evhtp_alias { char * alias; TAILQ_ENTRY(evhtp_alias) next; }; /** * @ingroup evhtp_core * @brief main structure containing all configuration information */ struct evhtp { evhtp_t * parent; /**< only when this is a vhost */ struct event_base * evbase; /**< the initialized event_base */ struct evconnlistener * server; /**< the libevent listener struct */ char * server_name; /**< the name included in Host: responses */ void * arg; /**< user-defined evhtp_t specific arguments */ int bev_flags; /**< bufferevent flags to use on bufferevent_*_socket_new() */ uint64_t max_body_size; uint64_t max_keepalive_requests; #define EVHTP_FLAG_ENABLE_100_CONT (1 << 1) #define EVHTP_FLAG_ENABLE_REUSEPORT (1 << 2) #define EVHTP_FLAG_ENABLE_NODELAY (1 << 3) #define EVHTP_FLAG_ENABLE_DEFER_ACCEPT (1 << 4) #define EVHTP_FLAG_DEFAULTS EVHTP_FLAG_ENABLE_100_CONT #define EVHTP_FLAG_ENABLE_ALL EVHTP_FLAG_ENABLE_100_CONT \ | EVHTP_FLAG_ENABLE_REUSEPORT \ | EVHTP_FLAG_ENABLE_NODELAY \ | EVHTP_FLAG_ENABLE_DEFER_ACCEPT uint16_t flags; /**< the base flags set for this context, see: EVHTP_FLAG_* */ uint16_t 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 */ evhtp_ssl_cfg_t * ssl_cfg; #endif #ifndef EVHTP_DISABLE_EVTHR evthr_pool_t * thr_pool; /**< connection threadpool */ pthread_mutex_t * lock; /**< parent lock for add/del cbs in threads */ evhtp_thread_init_cb thread_init_cb; evhtp_thread_exit_cb thread_exit_cb; /* keep backwards compat because I'm dumb and didn't * make these structs private */ #define thread_init_cbarg thread_cbarg void * thread_cbarg; #endif evhtp_callbacks_t * callbacks; evhtp_defaults_t defaults; struct timeval recv_timeo; struct timeval send_timeo; TAILQ_HEAD(, evhtp_alias) aliases; TAILQ_HEAD(, evhtp) vhosts; TAILQ_ENTRY(evhtp) next_vhost; }; /** * @brief a generic key/value structure */ struct evhtp_kv { char * key; char * val; size_t klen; size_t vlen; char k_heaped; /**< set to 1 if the key can be free()'d */ char v_heaped; /**< set to 1 if the val can be free()'d */ TAILQ_ENTRY(evhtp_kv) next; }; TAILQ_HEAD(evhtp_kvs, evhtp_kv); /** * @brief a generic container representing an entire URI strucutre */ struct evhtp_uri { 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 */ }; /** * @brief structure which represents authority information in a URI */ struct evhtp_authority { 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 */ }; /** * @brief structure which represents a URI path and or file */ struct evhtp_path { 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 */ }; /** * @brief a structure containing all information for a http request. */ struct evhtp_request { 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 */ struct evbuffer * buffer_in; /**< buffer containing data from client */ struct evbuffer * 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 */ #define EVHTP_REQ_FLAG_KEEPALIVE (1 << 1) #define EVHTP_REQ_FLAG_FINISHED (1 << 2) #define EVHTP_REQ_FLAG_CHUNKED (1 << 3) #define EVHTP_REQ_FLAG_ERROR (1 << 4) uint16_t flags; 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) next; }; #define evhtp_request_content_len(r) htparser_get_content_length(r->conn->parser) struct evhtp_connection { evhtp_t * htp; struct event_base * evbase; struct bufferevent * bev; #ifndef EVHTP_DISABLE_EVTHR evthr_t * thread; #endif #ifndef EVHTP_DISABLE_SSL evhtp_ssl_t * ssl; #endif evhtp_hooks_t * hooks; htparser * parser; struct event * 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 */ #define EVHTP_CONN_FLAG_ERROR (1 << 1) #define EVHTP_CONN_FLAG_OWNER (1 << 2) /**< set to 1 if this structure owns the bufferevent */ #define EVHTP_CONN_FLAG_VHOST_VIA_SNI (1 << 3) /**< set to 1 if the vhost was found via SSL SNI */ #define EVHTP_CONN_FLAG_PAUSED (1 << 4) /**< this connection has been marked as paused */ #define EVHTP_CONN_FLAG_CONNECTED (1 << 5) /**< client specific - set after successful connection */ #define EVHTP_CONN_FLAG_WAITING (1 << 6) /**< used to make sure resuming happens AFTER sending a reply */ #define EVHTP_CONN_FLAG_FREE_CONN (1 << 7) #define EVHTP_CONN_FLAG_KEEPALIVE (1 << 8) /**< set to 1 after the first request has been processed and the connection is kept open */ uint16_t flags; struct evbuffer * scratch_buf; /**< always zero'd out after used */ #ifdef EVHTP_FUTURE_USE TAILQ_HEAD(, evhtp_request) pending; /**< client pending data */ #endif }; struct evhtp_hooks { evhtp_hook_headers_start_cb on_headers_start; evhtp_hook_header_cb on_header; evhtp_hook_headers_cb on_headers; evhtp_hook_path_cb on_path; 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; void * on_headers_arg; void * on_path_arg; 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 { char * pemfile; char * privfile; char * cafile; char * capath; char * ciphers; char * named_curve; char * dhparams; long ssl_opts; long ssl_ctx_timeout; int verify_peer; int verify_depth; evhtp_ssl_verify_cb x509_verify_cb; evhtp_ssl_chk_issued_cb x509_chk_issued_cb; evhtp_ssl_decrypt_cb decrypt_cb; long store_flags; evhtp_ssl_scache_type scache_type; long scache_timeout; long scache_size; evhtp_ssl_scache_init scache_init; evhtp_ssl_scache_add scache_add; evhtp_ssl_scache_get scache_get; evhtp_ssl_scache_del scache_del; void * args; }; #endif EVHTP_EXPORT void evhtp_set_mem_functions(void *(*malloc_)(size_t), void *(*realloc_)(void *, size_t), void (* free_)(void *)); /** * @brief creates a new evhtp_t instance * * @param evbase the initialized event base * @param arg user-defined argument which is evhtp_t specific * * @return a new evhtp_t structure or NULL on error */ EVHTP_EXPORT evhtp_t * evhtp_new(struct event_base * evbase, void * arg); EVHTP_EXPORT void evhtp_enable_flag(evhtp_t *, int); EVHTP_EXPORT void evhtp_connection_enable_flag(evhtp_connection_t *, int); EVHTP_EXPORT void evhtp_request_enable_flag(evhtp_request_t *, int); EVHTP_EXPORT int evhtp_get_flags(evhtp_t *); EVHTP_EXPORT int evhtp_connection_get_flags(evhtp_connection_t *); EVHTP_EXPORT int evhtp_request_get_flags(evhtp_request_t *); EVHTP_EXPORT void evhtp_disable_flag(evhtp_t *, int); EVHTP_EXPORT void evhtp_connection_disable_flag(evhtp_connection_t *, int); EVHTP_EXPORT void evhtp_request_disable_flag(evhtp_request_t *, int); /** * @brief Frees evhtp_t structure; will stop and free threads associated * with the structure, and free the ssl context as well (if applicable). * * @param evhtp - ptr to evhtp_t structure * */ EVHTP_EXPORT void evhtp_free(evhtp_t * evhtp); /** * @brief set a read/write timeout on all things evhtp_t. When the timeout * expires your error hook will be called with the libevent supplied event * flags. * * @param htp the base evhtp_t struct * @param r read-timeout in timeval * @param w write-timeout in timeval. */ 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 /** * @brief when a client sends an Expect: 100-continue, if this is function is * called, evhtp will not send a HTTP/x.x continue response. * * @param htp */ EVHTP_EXPORT void evhtp_disable_100_continue(evhtp_t * htp) DEPRECATED("evhtp_disable_100 will soon be deprecated, use htp->flags instead"); /** * @brief creates a lock around callbacks and hooks, allowing for threaded * applications to add/remove/modify hooks & callbacks in a thread-safe manner. * * @param htp * * @return 0 on success, -1 on error */ EVHTP_EXPORT int evhtp_use_callback_locks(evhtp_t * htp); /** * @brief sets a callback which is called if no other callbacks are matched * * @param htp the initialized evhtp_t * @param cb the function to be executed * @param arg user-defined argument passed to the callback */ EVHTP_EXPORT void evhtp_set_gencb(evhtp_t * htp, evhtp_callback_cb cb, void * arg); /** * @brief call a user-defined function before the connection is accepted. * * @param htp * @param evhtp_pre_accept_cb * @param arg * * @return */ EVHTP_EXPORT void evhtp_set_pre_accept_cb(evhtp_t * htp, evhtp_pre_accept_cb, void * arg); /** * @brief call a user-defined function right after a connection is accepted. * * @param htp * @param evhtp_post_accept_cb * @param arg * * @return */ 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 * * @param htp the initialized evhtp_t * @param path the path to match * @param cb the function to be executed * @param arg user-defined argument passed to the callback * * @return evhtp_callback_t * on success, NULL on error. */ EVHTP_EXPORT evhtp_callback_t * evhtp_set_cb(evhtp_t * htp, const char * path, evhtp_callback_cb cb, void * arg); /** * @brief sets a callback to be executed based on a regex pattern * * @param htp the initialized evhtp_t * @param pattern a POSIX compat regular expression * @param cb the function to be executed * @param arg user-defined argument passed to the callback * * @return evhtp_callback_t * on success, NULL on error */ #ifndef EVHTP_DISABLE_REGEX EVHTP_EXPORT evhtp_callback_t * evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg); #endif /** * @brief sets a callback to to be executed on simple glob/wildcard patterns * this is useful if the app does not care about what was matched, but * just that it matched. This is technically faster than regex. * * @param htp * @param pattern wildcard pattern, the '*' can be set at either or both the front or end. * @param cb * @param arg * * @return */ EVHTP_EXPORT evhtp_callback_t * evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg); /** * @brief attempts to find the callback matching the exact string 'needle'. This is useful * in cases where we want to get the original handle, but is not in scope. * * with pattern based callbacks, this does not attempt to find a callback that would * match the string if the pattern matcher was executed. * * Meaning: * evhtp_set_glob_cb(htp, "/foo/bar*", ....); * * Calling * evhtp_get_cb(htp, "/foo/bar/baz"); * * Will return NULL since it's not the exact pattern set * * Calling * evhtp_get_cb(htp, "/foo/bar*"); * * Is the correct usage. * * @param htp * @param needle * * @return NULL if callback is not not found */ EVHTP_EXPORT evhtp_callback_t * evhtp_get_cb(evhtp_t * htp, const char * needle); /** * @brief sets a callback hook for either a connection or a path/regex . * * A user may set a variety of hooks either per-connection, or per-callback. * This allows the developer to hook into various parts of the request processing * cycle. * * a per-connection hook can be set at any time, but it is recommended to set these * during either a pre-accept phase, or post-accept phase. This allows a developer * to set hooks before any other hooks are called. * * a per-callback hook works differently. In this mode a developer can setup a set * of hooks prior to starting the event loop for specific callbacks. For example * if you wanted to hook something ONLY for a callback set by evhtp_set_cb or * evhtp_set_regex_cb this is the method of doing so. * * per-callback example: * * evhtp_callback_t * cb = evhtp_set_regex_cb(htp, "/anything/(.*)", default_cb, NULL); * * evhtp_set_hook(&cb->hooks, evhtp_hook_on_headers, anything_headers_cb, NULL); * * evhtp_set_hook(&cb->hooks, evhtp_hook_on_fini, anything_fini_cb, NULL); * * With the above example, once libevhtp has determined that it has a user-defined * callback for /anything/.*; anything_headers_cb will be executed after all headers * have been parsed, and anything_fini_cb will be executed before the request is * free()'d. * * The same logic applies to per-connection hooks, but it should be noted that if * a per-callback hook is set, the per-connection hook will be ignored. * * @param hooks double pointer to the evhtp_hooks_t structure * @param type the hook type * @param cb the callback to be executed. * @param arg optional argument which is passed when the callback is executed * * @return 0 on success, -1 on error (if hooks is NULL, it is allocated) */ EVHTP_EXPORT int evhtp_connection_set_hook(evhtp_connection_t * c, evhtp_hook_type type, evhtp_hook cb, void * arg); EVHTP_EXPORT int evhtp_request_set_hook(evhtp_request_t * r, evhtp_hook_type type, evhtp_hook cb, void * arg); EVHTP_EXPORT int evhtp_callback_set_hook(evhtp_callback_t * cb, evhtp_hook_type type, evhtp_hook hookcb, void * arg); EVHTP_EXPORT evhtp_hooks_t * evhtp_connection_get_hooks(evhtp_connection_t * c); EVHTP_EXPORT evhtp_hooks_t * evhtp_request_get_hooks(evhtp_request_t * r); EVHTP_EXPORT evhtp_hooks_t * evhtp_callback_get_hooks(evhtp_callback_t * cb); /** * @brief removes all hooks. * * @param hooks * * @return */ EVHTP_EXPORT int evhtp_unset_all_hooks(evhtp_hooks_t ** hooks); EVHTP_EXPORT int evhtp_request_unset_hook(evhtp_request_t * req, evhtp_hook_type type); EVHTP_EXPORT int evhtp_connection_unset_hook(evhtp_connection_t * conn, evhtp_hook_type type); EVHTP_EXPORT int evhtp_callback_unset_hook(evhtp_callback_t * callback, evhtp_hook_type type); /** * @brief bind to a socket, optionally with specific protocol support * formatting. The addr can be defined as one of the following: * ipv6: for binding to an IPv6 address. * unix: for binding to a unix named socket * ipv4: for binding to an ipv4 address * Otherwise the addr is assumed to be ipv4. * * @param htp * @param addr * @param port * @param backlog * * @return */ EVHTP_EXPORT int evhtp_bind_socket(evhtp_t * htp, const char * addr, uint16_t port, int backlog); /** * @brief stops the listening socket. * * @param htp */ EVHTP_EXPORT void evhtp_unbind_socket(evhtp_t * htp); /** * @brief create the listener plus setup various options with an already-bound * socket. * * @note Since the file descriptor is passed to the function, it will not * attempt to close it if an error occurs. * * @param htp * @param sock * @param backlog * * @return 0 on success, -1 on error (check errno) */ EVHTP_EXPORT int evhtp_accept_socket(evhtp_t * htp, evutil_socket_t sock, int backlog); /** * @brief bind to an already allocated sockaddr. * @see evhtp_bind_socket * * @param htp - ptr to evhtp_t structure * @param sa - ptr to sockaddr structure * @param sin_len - size of sockaddr structure * @param backlog - backlog flag * * @return 0 on success, -1 on fail */ EVHTP_EXPORT int evhtp_bind_sockaddr(evhtp_t * htp, struct sockaddr *, size_t sin_len, int backlog); /** * @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 exit_cb * @param nthreads * @param arg * * @return */ EVHTP_EXPORT int evhtp_use_threads(evhtp_t *, evhtp_thread_init_cb, int nthreads, void *) DEPRECATED("will take on the syntax of evhtp_use_threads_wexit"); /** * @brief Temporary function which will be renamed evhtp_use_threads in the * future. evhtp_use_threads() has been noted as deprecated for now */ EVHTP_EXPORT int evhtp_use_threads_wexit(evhtp_t *, evhtp_thread_init_cb, evhtp_thread_exit_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, struct evbuffer * buf); EVHTP_EXPORT void evhtp_send_reply_end(evhtp_request_t * request); /** * @brief Determine if a response should have a body. * Follows the rules in RFC 2616 section 4.3. * @return 1 if the response MUST have a body; 0 if the response MUST NOT have * a body. */ 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, * this will be converted to the first chunk. * * @param request * @param code */ EVHTP_EXPORT void evhtp_send_reply_chunk_start(evhtp_request_t * request, evhtp_res code); /** * @brief send a chunk reply. * * @param request * @param buf */ EVHTP_EXPORT void evhtp_send_reply_chunk(evhtp_request_t * request, struct evbuffer * buf); /** * @brief call when all chunks have been sent and you wish to send the last * bits. This will add the last 0CRLFCRCL and call send_reply_end(). * * @param request */ EVHTP_EXPORT void evhtp_send_reply_chunk_end(evhtp_request_t * request); /** * @brief creates a new evhtp_callback_t structure. * * All callbacks are stored in this structure * which define what the final function to be * called after all parsing is done. A callback * can be either a static string or a regular * expression. * * @param path can either be a static path (/path/to/resource/) or * a POSIX compatible regular expression (^/resource/(.*)) * @param type informs the function what type of of information is * is contained within the path argument. This can either be * callback_type_path, or callback_type_regex. * @param cb the callback function to be invoked * @param arg optional argument which is passed when the callback is executed. * * @return 0 on success, -1 on error. */ EVHTP_EXPORT evhtp_callback_t * evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_cb cb, void * arg); /** * @brief safely frees callback structure memory and internals * * @see evhtp_safe_free * * * @param callback - callback to be freed * */ EVHTP_EXPORT void evhtp_callback_free(evhtp_callback_t * callback); /** * @brief Adds a evhtp_callback_t to the evhtp_callbacks_t list * * @param cbs an allocated evhtp_callbacks_t structure * @param cb an initialized evhtp_callback_t structure * * @return 0 on success, -1 on error */ EVHTP_EXPORT int evhtp_callbacks_add_callback(evhtp_callbacks_t * cbs, evhtp_callback_t * cb); /** * @brief add an evhtp_t structure (with its own callbacks) to a base evhtp_t * structure for virtual hosts. It should be noted that if you enable SSL * on the base evhtp_t and your version of OpenSSL supports SNI, the SNI * hostname will always take precedence over the Host header value. * * @param evhtp * @param name * @param vhost * * @return */ EVHTP_EXPORT int evhtp_add_vhost(evhtp_t * evhtp, const char * name, evhtp_t * vhost); /** * @brief Add an alias hostname for a virtual-host specific evhtp_t. This avoids * having multiple evhtp_t virtual hosts with the same callback for the same * vhost. * * @param evhtp * @param name * * @return */ EVHTP_EXPORT int evhtp_add_alias(evhtp_t * evhtp, const char * name); /** * @brief set a variable number of aliases in one call * @reference evhtp_add_alias * @note last argument must be NULL terminated * * @param evhtp * @param name * @param ... * * @return 0 on success, -1 on error */ EVHTP_EXPORT int evhtp_add_aliases(evhtp_t * evhtp, const char * name, ...); /** * @brief Allocates a new key/value structure. * * @param key null terminated string * @param val null terminated string * @param kalloc if set to 1, the key will be copied, if 0 no copy is done. * @param valloc if set to 1, the val will be copied, if 0 no copy is done. * * @return evhtp_kv_t * on success, NULL on error. */ EVHTP_EXPORT evhtp_kv_t * evhtp_kv_new(const char * key, const char * val, char kalloc, char valloc); /** * @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); /** * @brief appends a key/val structure to a evhtp_kvs_t tailq * * @param kvs an evhtp_kvs_t structure * @param kv an evhtp_kv_t structure */ 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 * * @param dst an evhtp_kvs_t structure * @param src an evhtp_kvs_t structure */ EVHTP_EXPORT void evhtp_kvs_add_kvs(evhtp_kvs_t * dst, evhtp_kvs_t * src); /** * @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 #define EVHTP_PARSE_QUERY_FLAG_DEFAULT EVHTP_PARSE_QUERY_FLAG_LENIENT /** * @brief Parses the query portion of the uri into a set of key/values * * 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_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}' * * @param out double pointer where output is stored. This is allocated by the user. * @param str the string to unescape * @param str_len the length of the string to unescape * * @return 0 on success, -1 on error */ 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 * * @param key a null terminated string * @param val a null terminated string * @param kalloc if 1, key will be copied, if 0 no copy performed * @param valloc if 1, val will be copied, if 0 no copy performed * * @return evhtp_header_t * or NULL on error */ 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 * evhtp_headers TAILQ * * @param headers the evhtp_headers_t TAILQ (evhtp_kv_t) * @param key a null terminated string * @param kalloc if 1 the string will be copied, otherwise assigned * * @return an evhtp_header_t pointer or NULL on error */ EVHTP_EXPORT evhtp_header_t * evhtp_header_key_add(evhtp_headers_t * headers, const char * key, char kalloc); /** * @brief finds the last header in the headers tailq and adds the value * * @param headers the evhtp_headers_t TAILQ (evhtp_kv_t) * @param val a null terminated string * @param valloc if 1 the string will be copied, otherwise assigned * * @return an evhtp_header_t pointer or NULL on error */ EVHTP_EXPORT evhtp_header_t * evhtp_header_val_add(evhtp_headers_t * headers, const char * val, char valloc); /** * @brief adds an evhtp_header_t to the end of the evhtp_headers_t tailq * * @param headers * @param 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 * * @param headers the evhtp_headers_t tailq * @param key the key to find * * @return the value of the header key if found, NULL if not found. */ EVHTP_EXPORT const char * evhtp_header_find(evhtp_headers_t * headers, const char * key); #define evhtp_headers_find_header evhtp_kvs_find_kv #define evhtp_headers_for_each evhtp_kvs_for_each #define evhtp_header_free evhtp_kv_free #define evhtp_headers_new evhtp_kvs_new #define evhtp_headers_free evhtp_kvs_free #define evhtp_header_rm_and_free evhtp_kv_rm_and_free #define evhtp_headers_add_headers evhtp_kvs_add_kvs #define evhtp_query_new evhtp_kvs_new #define evhtp_query_free evhtp_kvs_free /** * @brief returns the htp_method enum version of the request method. * * @param r * * @return htp_method enum */ EVHTP_EXPORT htp_method evhtp_request_get_method(evhtp_request_t * r); EVHTP_EXPORT evhtp_proto evhtp_request_get_proto(evhtp_request_t * r); /** * @brief Returns the last status code set for a request (request/response) * * @param r * * @return the HTTP status code or related error. */ EVHTP_EXPORT evhtp_res evhtp_request_get_status_code(evhtp_request_t * r); /** * @brief wrapper around get_status_code that returns the string version * of the last status code set for a request. * * @param r * * @return NULL on error */ EVHTP_EXPORT const char * evhtp_request_get_status_code_str(evhtp_request_t * r); /* the following functions all do the same thing, pause and the processing */ /** * @brief pauses a connection (disables reading) * * @param c a evhtp_connection_t * structure */ EVHTP_EXPORT void evhtp_connection_pause(evhtp_connection_t * connection); /** * @brief resumes a connection (enables reading) and activates resume event. * * @param c */ EVHTP_EXPORT void evhtp_connection_resume(evhtp_connection_t * connection); /** * @brief Wrapper around evhtp_connection_pause * * @see evhtp_connection_pause * * @param request */ EVHTP_EXPORT void evhtp_request_pause(evhtp_request_t * request); /** * @brief Wrapper around evhtp_connection_resume * * @see evhtp_connection_resume * * @param request */ EVHTP_EXPORT void evhtp_request_resume(evhtp_request_t * request); /** * @brief returns the underlying evhtp_connection_t structure from a request * * @param request * * @return evhtp_connection_t on success, otherwise NULL */ EVHTP_EXPORT evhtp_connection_t * evhtp_request_get_connection(evhtp_request_t * request); /** * @brief Sets the connections underlying bufferevent * * @param conn * @param bev */ EVHTP_EXPORT void evhtp_connection_set_bev(evhtp_connection_t * conn, struct bufferevent * bev); /** * @brief sets the underlying bufferevent for a evhtp_request * * @param request * @param bev */ EVHTP_EXPORT void evhtp_request_set_bev(evhtp_request_t * request, struct bufferevent * bev); /** * @brief returns the underlying connections bufferevent * * @param conn * * @return bufferevent on success, otherwise NULL */ EVHTP_EXPORT struct bufferevent * evhtp_connection_get_bev(evhtp_connection_t * conn); /** * @brief sets a connection-specific read/write timeout which overrides the * global read/write settings. * * @param conn * @param r timeval for read * @param w timeval for write */ 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 * * @param request * * @return bufferevent on success, otherwise NULL */ EVHTP_EXPORT struct bufferevent * evhtp_request_get_bev(evhtp_request_t * request); /** * @brief let a user take ownership of the underlying bufferevent and free * all other underlying resources. * * Warning: this will free all evhtp_connection/request structures, remove all * associated hooks and reset the bufferevent to defaults, i.e., disable * EV_READ, and set all callbacks to NULL. * * @param connection * * @return underlying connections bufferevent. */ EVHTP_EXPORT struct bufferevent * evhtp_connection_take_ownership(evhtp_connection_t * connection); /** * @brief free's all connection related resources, this will also call your * request fini hook and request fini hook. * * @param connection */ 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 * default to unlimited. * * @param htp * @param 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 * the size set by evhtp_set_max_body_size * * @param conn * @param 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. * * @param request * @param len */ EVHTP_EXPORT void evhtp_request_set_max_body_size(evhtp_request_t * request, uint64_t len); EVHTP_EXPORT void evhtp_request_set_keepalive(evhtp_request_t * request, int val); /** * @brief sets a maximum number of requests that a single connection can make. * * @param htp * @param num */ EVHTP_EXPORT void evhtp_set_max_keepalive_requests(evhtp_t * htp, uint64_t num); /***************************************************************** * client request functions * *****************************************************************/ /** * @brief allocate a new connection */ EVHTP_EXPORT evhtp_connection_t * evhtp_connection_new_dns( struct event_base * 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(struct event_base * evbase, const char * addr, uint16_t port); #ifndef EVHTP_DISABLE_SSL EVHTP_EXPORT evhtp_connection_t * evhtp_connection_ssl_new( struct event_base * evbase, const char * addr, uint16_t port, evhtp_ssl_ctx_t * ctx); #endif /** * @brief allocate a new request */ EVHTP_EXPORT evhtp_request_t * evhtp_request_new(evhtp_callback_cb cb, void * arg); /** * @brief make a client request */ EVHTP_EXPORT int evhtp_make_request(evhtp_connection_t * c, evhtp_request_t * r, htp_method meth, const char * uri); EVHTP_EXPORT unsigned int evhtp_request_status(evhtp_request_t *); #define evhtp_safe_free(_var, _freefn) do { \ _freefn((_var)); \ (_var) = NULL; \ } while (0) #ifdef __cplusplus } #endif #endif /* __EVHTP__H__ */ libevhtp-1.2.18/include/evhtp/log.h000066400000000000000000000017361342660753300171430ustar00rootroot00000000000000#ifndef __EVHTP_LOG_H__ #define __EVHTP_LOG_H__ /** * @brief create a new request-logging context with the format string `format` * @note The following variable definitions are treated as special in the * format: * $ua - the user-agent * $path - the requested path * $rhost - the IP address of the request * $meth - the HTTP method * $ts - timestamp * $proto - HTTP proto version * $status - the return status * $ref - the HTTP referrer * $host - either the vhost (if defined) or the value of the Host: header * All other characters are treated as-is. * * @param format - format string (see above) */ EVHTP_EXPORT void * evhtp_log_new(const char * format); /** * @brief log a HTTP request to a FILE using a compiled format. * * @param log - The compiled format (see evhtp_log_new) * @param request * @param fp */ EVHTP_EXPORT void evhtp_log_request_f(void * log, evhtp_request_t * request, FILE * fp); #endif libevhtp-1.2.18/include/evhtp/parser.h000066400000000000000000000100641342660753300176500ustar00rootroot00000000000000/** * @file parser.h */ #ifndef __HTPARSE_H__ #define __HTPARSE_H__ #include #include #ifdef __cplusplus extern "C" { #endif struct htparser; enum htp_type { htp_type_request = 0, htp_type_response }; enum htp_scheme { htp_scheme_none = 0, htp_scheme_ftp, htp_scheme_http, htp_scheme_https, htp_scheme_nfs, htp_scheme_unknown }; enum htp_method { htp_method_GET = 0, htp_method_HEAD, htp_method_POST, htp_method_PUT, htp_method_DELETE, htp_method_MKCOL, htp_method_COPY, htp_method_MOVE, htp_method_OPTIONS, htp_method_PROPFIND, htp_method_PROPPATCH, htp_method_LOCK, htp_method_UNLOCK, htp_method_TRACE, htp_method_CONNECT, /* RFC 2616 */ htp_method_PATCH, /* RFC 5789 */ htp_method_UNKNOWN, }; enum htpparse_error { htparse_error_none = 0, htparse_error_too_big, htparse_error_inval_method, htparse_error_inval_reqline, htparse_error_inval_schema, htparse_error_inval_proto, htparse_error_inval_ver, htparse_error_inval_hdr, htparse_error_inval_chunk_sz, htparse_error_inval_chunk, htparse_error_inval_state, htparse_error_user, htparse_error_status, htparse_error_generic }; typedef struct htparser htparser; typedef struct htparse_hooks htparse_hooks; typedef enum htp_scheme htp_scheme; typedef enum htp_method htp_method; typedef enum htp_type htp_type; typedef enum htpparse_error htpparse_error; typedef int (* htparse_hook)(htparser *); typedef int (* htparse_data_hook)(htparser *, const char *, size_t); struct htparse_hooks { htparse_hook on_msg_begin; htparse_data_hook method; htparse_data_hook scheme; /* called if scheme is found */ htparse_data_hook host; /* called if a host was in the request scheme */ htparse_data_hook port; /* called if a port was in the request scheme */ htparse_data_hook path; /* only the path of the uri */ htparse_data_hook args; /* only the arguments of the uri */ htparse_data_hook uri; /* the entire uri including path/args */ htparse_hook on_hdrs_begin; htparse_data_hook hdr_key; htparse_data_hook hdr_val; htparse_data_hook hostname; htparse_hook on_hdrs_complete; htparse_hook on_new_chunk; /* called after parsed chunk octet */ htparse_hook on_chunk_complete; /* called after single parsed chunk */ htparse_hook on_chunks_complete; /* called after all parsed chunks processed */ htparse_data_hook body; htparse_hook on_msg_complete; }; EVHTP_EXPORT size_t htparser_run(htparser *, htparse_hooks *, const char *, size_t); EVHTP_EXPORT int htparser_should_keep_alive(htparser * p); EVHTP_EXPORT htp_scheme htparser_get_scheme(htparser *); EVHTP_EXPORT htp_method htparser_get_method(htparser *); EVHTP_EXPORT const char * htparser_get_methodstr(htparser *); EVHTP_EXPORT const char * htparser_get_methodstr_m(htp_method); EVHTP_EXPORT void htparser_set_major(htparser *, unsigned char); EVHTP_EXPORT void htparser_set_minor(htparser *, unsigned char); EVHTP_EXPORT unsigned char htparser_get_major(htparser *); EVHTP_EXPORT unsigned char htparser_get_minor(htparser *); EVHTP_EXPORT unsigned char htparser_get_multipart(htparser *); EVHTP_EXPORT unsigned int htparser_get_status(htparser *); EVHTP_EXPORT uint64_t htparser_get_content_length(htparser *); EVHTP_EXPORT uint64_t htparser_get_content_pending(htparser *); EVHTP_EXPORT uint64_t htparser_get_total_bytes_read(htparser *); EVHTP_EXPORT htpparse_error htparser_get_error(htparser *); EVHTP_EXPORT const char * htparser_get_strerror(htparser *); EVHTP_EXPORT void * htparser_get_userdata(htparser *); EVHTP_EXPORT void htparser_set_userdata(htparser *, void *); EVHTP_EXPORT void htparser_init(htparser *, htp_type); EVHTP_EXPORT htparser * htparser_new(void); #ifdef __cplusplus } #endif #endif libevhtp-1.2.18/include/evhtp/sslutils.h000066400000000000000000000107061342660753300202410ustar00rootroot00000000000000/** * @file sslutils.h */ #ifndef __EVHTP_SSLUTILS_H__ #define __EVHTP_SSLUTILS_H__ #include #ifdef __cplusplus extern "C" { #endif /** * @defgroup htp_sslutils SSL utility functions */ /** * @brief converts the client certificate DNAME information (CN=, OU=.....) * @ingroup htp_sslutils * * @param ssl the client SSL context * * @return heap allocated str representation, NULL on error. */ EVHTP_EXPORT unsigned char * htp_sslutil_subject_tostr(evhtp_ssl_t * ssl); /** * @brief converts the DN (issuer of cert from the client) * @ingroup htp_sslutils * * @param ssl client SSL context * * @return heap allocated str representation, NULL on error */ EVHTP_EXPORT unsigned char * htp_sslutil_issuer_tostr(evhtp_ssl_t * ssl); /** * @brief converts the `notbefore` date of the cert from the client * @ingroup htp_sslutils * * @param ssl client SSL context * * @return heap allocated str (YYMMDDhhmmss) of the notbefore, NULL on error. */ EVHTP_EXPORT unsigned char * htp_sslutil_notbefore_tostr(evhtp_ssl_t * ssl); /** * @brief converts the `notafter` date of the cert from the client * @ingroup htp_sslutils * * @param ssl ssl client SSL context * * @return heap allocated str (YYMMDDhhmmss) of notafter, NULL on error. */ EVHTP_EXPORT unsigned char * htp_sslutil_notafter_tostr(evhtp_ssl_t * ssl); /** * @brief converts the SHA1 digest in str from the client * @ingroup htp_sslutils * * @param ssl SSL context from client * * @return NULL on error */ EVHTP_EXPORT unsigned char * htp_sslutil_sha1_tostr(evhtp_ssl_t * ssl); /** * @brief convert serial number to string * @ingroup htp_sslutils * * @param ssl SSL context from client * * @return NULL on error */ EVHTP_EXPORT unsigned char * htp_sslutil_serial_tostr(evhtp_ssl_t * ssl); /** * @brief convert the used for this SSL context * @ingroup htp_sslutils * * @param ssl SSL context * * @return heap allocated cipher str, NULL on error */ EVHTP_EXPORT unsigned char * htp_sslutil_cipher_tostr(evhtp_ssl_t * ssl); /** * @brief convert the client cert into a multiline string * @ingroup htp_sslutils * * @param ssl client SSL context * * @return heap allocated string, NULL on error */ EVHTP_EXPORT unsigned char * htp_sslutil_cert_tostr(evhtp_ssl_t * ssl); /** * @brief convert X509 extentions to string * @ingroup htp_sslutils * * @param ssl SSL context * @param oid * * @return */ EVHTP_EXPORT unsigned char * htp_sslutil_x509_ext_tostr(evhtp_ssl_t * ssl, const char * oid); /** * @brief convert a string to the proper verify opts * @ingroup htp_sslutils * * @param opts_str ("on" / "optional" / "off" ) * where: * "on" : client must present a valid cert (otherwise rejected) * "off" : no client cert required at all * "optional" : client MAY present a valid certificate (but not rejected) * * @note if `opts_str` is NULL, defaults to "off" * * @return OR'd mask SSL_VERIFY_* flags, -1 on error */ EVHTP_EXPORT int htp_sslutil_verify2opts(const char * opts_str); /* * @ingroup htp_sslutils * @ { */ #define HTP_SSLUTILS_XHDR_SUBJ (1 << 0) #define HTP_SSLUTILS_XHDR_ISSR (1 << 1) #define HTP_SSLUTILS_XHDR_NBFR (1 << 2) #define HTP_SSLUTILS_XHDR_NAFR (1 << 3) #define HTP_SSLUTILS_XHDR_SERL (1 << 4) #define HTP_SSLUTILS_XHDR_SHA1 (1 << 5) #define HTP_SSLUTILS_XHDR_CERT (1 << 6) #define HTP_SSLUTILS_XHDR_CIPH (1 << 7) #define HTP_SSLUTILS_XHDR_ALL \ HTP_SSLUTILS_XHDR_SUBJ \ | HTP_SSLUTILS_XHDR_ISSR \ | HTP_SSLUTILS_XHDR_NBFR \ | HTP_SSLUTILS_XHDR_NAFR \ | HTP_SSLUTILS_XHDR_SERL \ | HTP_SSLUTILS_XHDR_SHA1 \ | HTP_SSLUTILS_XHDR_CERT \ | HTP_SSLUTILS_XHDR_CIPH /** @} */ /** * @brief add SSL-type X-Header flags to an evhtp_headers_t context * @ingroup htp_sslutils * * @param hdrs headers structure to append into * @param ssl the SSL context * HTP_SSLUTILS_XHDR_SUBJ: `X-SSL-Subject` * HTP_SSLUTILS_XHDR_ISSR: `X-SSL-Issuer` * HTP_SSLUTILS_XHDR_NBFR: `X-SSL-Notbefore` * HTP_SSLUTILS_XHDR_NAFR: `X-SSL-Notafter` * HTP_SSLUTILS_XHDR_SERL: `X-SSL-Serial` * HTP_SSLUTILS_XHDR_CIPH: `X-SSL-Cipher` * HTP_SSLUTILS_XHDR_CERT: `X-SSL-Certificate` * HTP_SSLUTILS_XHDR_SHA1: `X-SSL-SHA1` * * @param flags flags (See XHDR defines above) * * @return 0 on success, -1 on error */ EVHTP_EXPORT int htp_sslutil_add_xheaders(evhtp_headers_t * hdrs, evhtp_ssl_t * ssl, short flags); #ifdef __cplusplus } #endif #endif libevhtp-1.2.18/include/evhtp/thread.h000066400000000000000000000036611342660753300176300ustar00rootroot00000000000000/** * @file thread.h */ #ifndef __EVTHR_H__ #define __EVTHR_H__ #include #include #include #ifdef __cplusplus extern "C" { #endif enum evthr_res { EVTHR_RES_OK = 0, EVTHR_RES_BACKLOG, EVTHR_RES_RETRY, EVTHR_RES_NOCB, EVTHR_RES_FATAL }; struct evthr_pool; struct evthr; typedef struct event_base evbase_t; typedef struct event ev_t; typedef struct evthr_pool evthr_pool_t; typedef struct evthr evthr_t; typedef enum evthr_res evthr_res; typedef void (* evthr_cb)(evthr_t * thr, void * cmd_arg, void * shared); typedef void (* evthr_init_cb)(evthr_t * thr, void * shared); typedef void (* evthr_exit_cb)(evthr_t * thr, void * shared); EVHTP_EXPORT evthr_t * evthr_new(evthr_init_cb, void *) DEPRECATED("will take on the syntax of evthr_wexit_new"); EVHTP_EXPORT evbase_t * evthr_get_base(evthr_t * thr); EVHTP_EXPORT void evthr_set_aux(evthr_t * thr, void * aux); EVHTP_EXPORT void * evthr_get_aux(evthr_t * thr); EVHTP_EXPORT int evthr_start(evthr_t * evthr); EVHTP_EXPORT evthr_res evthr_stop(evthr_t * evthr); EVHTP_EXPORT evthr_res evthr_defer(evthr_t * evthr, evthr_cb cb, void *); EVHTP_EXPORT void evthr_free(evthr_t * evthr); EVHTP_EXPORT evthr_pool_t * evthr_pool_new(int nthreads, evthr_init_cb, void *) DEPRECATED("will take on the syntax of evthr_pool_wexit_new"); EVHTP_EXPORT int evthr_pool_start(evthr_pool_t * pool); EVHTP_EXPORT evthr_res evthr_pool_stop(evthr_pool_t * pool); EVHTP_EXPORT evthr_res evthr_pool_defer(evthr_pool_t * pool, evthr_cb cb, void * arg); EVHTP_EXPORT void evthr_pool_free(evthr_pool_t * pool); EVHTP_EXPORT evthr_t * evthr_wexit_new(evthr_init_cb, evthr_exit_cb, void * shared); EVHTP_EXPORT evthr_pool_t * evthr_pool_wexit_new(int nthreads, evthr_init_cb, evthr_exit_cb, void *); #ifdef __cplusplus } #endif #endif /* __EVTHR_H__ */ libevhtp-1.2.18/include/internal.h000066400000000000000000000122051342660753300170410ustar00rootroot00000000000000/** * @file internal.h */ #ifndef __EVHTP_INTERNAL_H__ #define __EVHTP_INTERNAL_H__ #ifdef __cplusplus extern "C" { #endif #if defined __GNUC__ || defined __llvm__ # define evhtp_likely(x) __builtin_expect(!!(x), 1) # define evhtp_unlikely(x) __builtin_expect(!!(x), 0) #else # define evhtp_likely(x) (x) # define evhtp_unlikely(x) (x) #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 __FILENAME__ \ (strrchr(__FILE__, '/') ? \ strrchr(__FILE__, '/') + 1 : __FILE__) #define clean_errno() \ (errno == 0 ? "None" : strerror(errno)) #define __log_debug_color(X) "[\x1b[1;36m" X "\x1b[0;39m]" #define __log_error_color(X) "[\x1b[1;31m" X "\x1b[0;39m]" #define __log_warn_color(X) "[\x1b[1;33m" X "\x1b[0;39m]" #define __log_info_color(X) "[\x1b[32m" X "\x1b[0;39m]" #define __log_func_color(X) "\x1b[33m" X "\x1b[39m" #define __log_args_color(X) "\x1b[94m" X "\x1b[39m" #define __log_errno_color(X) "\x1b[35m" X "\x1b[39m" #if !defined(EVHTP_DEBUG) /* compile with all debug messages removed */ #define log_debug(M, ...) #else #define log_debug(M, ...) \ fprintf(stderr, __log_debug_color("DEBUG") " " \ __log_func_color("%s/%s:%-9d") \ __log_args_color(M) \ "\n", \ __FILENAME__, __FUNCTION__, __LINE__, ## __VA_ARGS__) #endif #define log_error(M, ...) \ fprintf(stderr, __log_error_color("ERROR") " " \ __log_func_color("%s:%-9d") \ __log_args_color(M) \ " :: " \ __log_errno_color("(errno: %s)") \ "\n", \ __FILENAME__, __LINE__, ## __VA_ARGS__, clean_errno()) #define log_warn(M, ...) \ fprintf(stderr, __log_warn_color("WARN") " " \ __log_func_color("%s:%-9d") \ __log_args_color(M) \ " :: " \ __log_errno_color("(errno: %s)") \ "\n", \ __FILENAME__, __LINE__, ## __VA_ARGS__, clean_errno()) #define log_info(M, ...) \ fprintf(stderr, __log_info_color("INFO") " " \ __log_func_color("%4s:%-9d") \ __log_args_color(M) "\n", \ __FILENAME__, __LINE__, ## __VA_ARGS__) #ifndef NDEBUG #define evhtp_assert(x) \ do { \ if (evhtp_unlikely(!(x))) { \ fprintf(stderr, "Assertion failed: %s (%s:%s:%d)\n", # x, \ __func__, __FILE__, __LINE__); \ fflush(stderr); \ abort(); \ } \ } while (0) #define evhtp_alloc_assert(x) \ do { \ if (evhtp_unlikely(!x)) { \ fprintf(stderr, "Out of memory (%s:%s:%d)\n", \ __func__, __FILE__, __LINE__); \ fflush(stderr); \ abort(); \ } \ } while (0) #define evhtp_assert_fmt(x, fmt, ...) \ do { \ if (evhtp_unlikely(!(x))) { \ fprintf(stderr, "Assertion failed: %s (%s:%s:%d) " fmt "\n", \ # x, __func__, __FILE__, __LINE__, __VA_ARGS__); \ fflush(stderr); \ abort(); \ } \ } while (0) #define evhtp_errno_assert(x) \ do { \ if (evhtp_unlikely(!(x))) { \ fprintf(stderr, "%s [%d] (%s:%s:%d)\n", \ strerror(errno), errno, \ __func__, __FILE__, __LINE__); \ fflush(stderr); \ abort(); \ } \ } while (0) #else #define evhtp_assert(x) #define evhtp_alloc_assert(x) #define evhtp_assert_fmt(x) #define evhtp_errno_assert(x) #endif #ifdef __cplusplus } #endif #endif libevhtp-1.2.18/include/numtoa.h000066400000000000000000000017021342660753300165300ustar00rootroot00000000000000#ifndef __EVHTP_NUMTOA_H__ #define __EVHTP_NUMTOA_H__ #ifdef __cplusplus extern "C" { #endif #include #include "evhtp/config.h" /** * @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 */ EVHTP_EXPORT 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 */ EVHTP_EXPORT 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 */ EVHTP_EXPORT size_t evhtp_modp_u64toa(uint64_t value, char * str); #define evhtp_modp_uchartoa(_val) (unsigned char)('0' + _val) #ifdef __cplusplus } #endif #endif libevhtp-1.2.18/log.c000066400000000000000000000167641342660753300143740ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "evhtp/evhtp.h" #include "evhtp/log.h" typedef enum { HTP_LOG_OP_USERAGENT = 1, HTP_LOG_OP_PATH, HTP_LOG_OP_RHOST, HTP_LOG_OP_METHOD, HTP_LOG_OP_TIMESTAMP, HTP_LOG_OP_PROTO, HTP_LOG_OP_STATUS, HTP_LOG_OP_REFERRER, HTP_LOG_OP_HOST, HTP_LOG_OP_HEADER, HTP_LOG_OP_PRINTABLE } htp_log_op_type; struct { char * fmt_; htp_log_op_type type_; } op_type_strmap_[] = { { "$ua", HTP_LOG_OP_USERAGENT }, { "$path", HTP_LOG_OP_PATH }, { "$rhost", HTP_LOG_OP_RHOST }, { "$meth", HTP_LOG_OP_METHOD }, { "$ts", HTP_LOG_OP_TIMESTAMP }, { "$proto", HTP_LOG_OP_PROTO }, { "$status", HTP_LOG_OP_STATUS }, { "$ref", HTP_LOG_OP_REFERRER }, { "$host", HTP_LOG_OP_HOST }, { "$hdr::", HTP_LOG_OP_HEADER }, { NULL, HTP_LOG_OP_PRINTABLE } }; #define HTP_LOG_OP_TAGSZ 1024 struct htp_log_op { htp_log_op_type type; size_t len; char tag[HTP_LOG_OP_TAGSZ]; TAILQ_ENTRY(htp_log_op) next; }; TAILQ_HEAD(htp_log_format, htp_log_op); htp_log_op_type htp_log_str_to_op_type_(const char * fmt, int * arglen) { int i; for (i = 0; op_type_strmap_[i].fmt_; i++) { const char * fmt_ = op_type_strmap_[i].fmt_; htp_log_op_type type_ = op_type_strmap_[i].type_; if (!strncasecmp(fmt_, fmt, strlen(fmt_))) { *arglen = strlen(fmt_); return type_; } } return 0; } static struct htp_log_format * htp_log_format_new_(void) { struct htp_log_format * format; format = calloc(1, sizeof(*format)); TAILQ_INIT(format); return format; } static struct htp_log_op * htp_log_op_new_(htp_log_op_type type) { struct htp_log_op * op; op = calloc(1, sizeof(*op)); op->type = type; return op; } /** * @brief [debug] print the psuedo-stack * * @param stack */ static void dump_(struct htp_log_format * format) { struct htp_log_op * op; TAILQ_FOREACH(op, format, next) { switch (op->type) { case HTP_LOG_OP_USERAGENT: printf("$ua"); break; case HTP_LOG_OP_PATH: printf("$path"); break; case HTP_LOG_OP_METHOD: printf("$meth"); break; case HTP_LOG_OP_TIMESTAMP: printf("$ts"); break; case HTP_LOG_OP_REFERRER: printf("$ref"); break; case HTP_LOG_OP_RHOST: printf("$rhost"); break; case HTP_LOG_OP_STATUS: printf("$status"); break; case HTP_LOG_OP_HOST: printf("$host"); break; case HTP_LOG_OP_HEADER: printf("$hdr::"); break; case HTP_LOG_OP_PROTO: printf("$proto"); break; case HTP_LOG_OP_PRINTABLE: printf("%s", op->tag); break; } /* switch */ } printf("\n"); } /* log_format_dump_ */ static void htp_log_format_addchar_(struct htp_log_format * format, const char c) { struct htp_log_op * op; if (TAILQ_EMPTY(format)) { /* the format op stack is empty, we want to allocate a new one */ op = NULL; } else { /* reuse the last format op, appending the character */ op = TAILQ_LAST(format, htp_log_format); } if (op == NULL || op->type != HTP_LOG_OP_PRINTABLE) { /* the last op in the format stack was not a type of PRINTABLE or * NULL, so allocate a new one. */ op = htp_log_op_new_(HTP_LOG_OP_PRINTABLE); /* insert the newly allocated stack_ent into the stack */ TAILQ_INSERT_TAIL(format, op, next); } /* append the character to the stack_ent */ op->tag[op->len++] = c; op->tag[op->len] = '\0'; } #define IS_FMTVAR(X) (*X == '$' && *(X + 1) != '$') /** * @brief using an input string, create a stack of information that will be * logged. * * @param fmt * * @return */ static struct htp_log_format * htp_log_format_compile_(const char * strfmt) { const char * strp; struct htp_log_format * format; format = htp_log_format_new_(); for (strp = strfmt; *strp != '\0'; strp++) { struct htp_log_op * op; /* if the character is a format variable ($var), * then create a new stack entry, and insert it * into the stack. Otherwise append the character * to the last stack entry */ if (IS_FMTVAR(strp)) { int arglen; op = htp_log_op_new_(htp_log_str_to_op_type_(strp, &arglen)); strp += arglen - 1; TAILQ_INSERT_TAIL(format, op, next); } else { htp_log_format_addchar_(format, *strp); } } return format; } void * evhtp_log_new(const char * fmtstr) { return htp_log_format_compile_(fmtstr); } void evhtp_log_request_f(void * format_p, evhtp_request_t * request, FILE * fp) { struct htp_log_format * format = format_p; struct htp_log_op * op; struct timeval tv; struct tm * tm; struct sockaddr_in * sin; char tmp[64]; TAILQ_FOREACH(op, format, next) { const char * logstr = NULL; switch (op->type) { case HTP_LOG_OP_USERAGENT: logstr = evhtp_header_find(request->headers_in, "user-agent"); break; case HTP_LOG_OP_PATH: if (request->uri && request->uri->path && request->uri->path->full) { logstr = request->uri->path->full; } break; case HTP_LOG_OP_METHOD: if (request->conn->parser) { logstr = htparser_get_methodstr(request->conn->parser); } break; case HTP_LOG_OP_TIMESTAMP: event_base_gettimeofday_cached(request->conn->evbase, &tv); tm = localtime(&tv.tv_sec); strftime(tmp, sizeof(tmp), "%d/%b/%Y:%X %z", tm); logstr = tmp; break; case HTP_LOG_OP_REFERRER: logstr = evhtp_header_find(request->headers_in, "referer"); break; case HTP_LOG_OP_RHOST: sin = (struct sockaddr_in *)request->conn->saddr; evutil_inet_ntop(AF_INET, &sin->sin_addr, tmp, sizeof(tmp)); logstr = tmp; break; case HTP_LOG_OP_STATUS: fprintf(fp, "%d", evhtp_request_status(request)); continue; case HTP_LOG_OP_HOST: logstr = request->htp->server_name ? : evhtp_header_find(request->headers_in, "host"); break; case HTP_LOG_OP_PROTO: logstr = evhtp_request_get_proto(request) == EVHTP_PROTO_11 ? "1.1" : "1.0"; break; case HTP_LOG_OP_HEADER: /* not implemented yet - fallthrough */ case HTP_LOG_OP_PRINTABLE: logstr = op->tag; break; } /* switch */ fputs(logstr ? : "-", fp); } fputc('\n', fp); } /* evhtp_log_request_f */ libevhtp-1.2.18/numtoa.c000066400000000000000000000025601342660753300151030ustar00rootroot00000000000000/* * 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 #include #include #include #include "internal.h" #include "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); } 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); } 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 #warning "UNKNOWN ARCH" #endif } libevhtp-1.2.18/parser.c000066400000000000000000002070421342660753300150760ustar00rootroot00000000000000#include #include #include #include #ifdef EVHTP_HAS_SYS_TYPES #include #endif #include "internal.h" #include "evhtp/parser.h" #include "evhtp/config.h" #if '\n' != '\x0a' || 'A' != 65 #error "You have somehow found a non-ASCII host. We can't build here." #endif #define PARSER_STACK_MAX 8192 #define LF (unsigned char)10 #define CR (unsigned char)13 #define CRLF "\x0d\x0a" enum eval_hdr_val { eval_hdr_val_none = 0, eval_hdr_val_connection, eval_hdr_val_proxy_connection, eval_hdr_val_content_length, eval_hdr_val_transfer_encoding, eval_hdr_val_hostname, eval_hdr_val_content_type }; 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), }; enum parser_state { s_start = 0, s_method, s_spaces_before_uri, s_schema, s_schema_slash, s_schema_slash_slash, s_host, s_host_ipv6, s_host_done, s_port, s_after_slash_in_uri, s_check_uri, s_uri, s_http_09, s_http_H, s_http_HT, s_http_HTT, s_http_HTTP, s_first_major_digit, s_major_digit, s_first_minor_digit, s_minor_digit, s_spaces_after_digit, s_almost_done, s_done, s_hdrline_start, s_hdrline_hdr_almost_done, s_hdrline_hdr_done, s_hdrline_hdr_key, s_hdrline_hdr_space_before_val, s_hdrline_hdr_val, s_hdrline_almost_done, s_hdrline_done, s_body_read, s_chunk_size_start, s_chunk_size, s_chunk_size_almost_done, s_chunk_data, s_chunk_data_almost_done, s_chunk_data_done, s_status, s_space_after_status, s_status_text }; typedef enum eval_hdr_val eval_hdr_val; typedef enum parser_flags parser_flags; typedef enum parser_state parser_state; struct htparser { htpparse_error error; parser_state state; parser_flags flags; eval_hdr_val heval; htp_type type; htp_scheme scheme; htp_method method; unsigned char multipart; unsigned char major; unsigned char minor; uint64_t content_len; /* this gets decremented as data passes through */ uint64_t orig_content_len; /* this contains the original length of the body */ uint64_t bytes_read; uint64_t total_bytes_read; unsigned int status; /* only for responses */ unsigned int status_count; /* only for responses */ char * scheme_offset; char * host_offset; char * port_offset; char * path_offset; char * args_offset; void * userdata; size_t buf_idx; /* Must be last since htparser_init memsets up to the offset of this buffer */ char buf[PARSER_STACK_MAX]; }; #ifdef EVHTP_DEBUG static void log_htparser__s_(struct htparser * p) { log_debug( "struct htparser {\n" " htpparse_error = %d\n" " parser_state = %d\n" " parser_flags = %d\n" " eval_hdr_val = %d\n" " htp_type = %d\n" " htp_scheme = %d\n" " htp_method = %d\n" " multipart = %c\n" " major = %c\n" " minor = %c\n" " content_len = %zu\n" " orig_clen = %zu\n" " bytes_read = %zu\n" " total_read = %zu\n" " status = %d\n" " status_count = %d\n" " scheme_offset = %s\n" " host_offset = %s\n" " port_offset = %s\n" " path_offset = %s\n" " args_offset = %s\n" " userdata = %p\n" " buf_idx = %zu\n" " buf = %s\n" "};", p->error, p->state, p->flags, p->heval, p->type, p->scheme, p->method, p->multipart, p->major, p->minor, p->content_len, p->orig_content_len, p->bytes_read, p->total_bytes_read, p->status, p->status_count, p->scheme_offset, p->host_offset, p->port_offset, p->path_offset, p->args_offset, p->userdata, p->buf_idx, p->buf); } /* log_htparser__s_ */ #else #define log_htparser__s_(p) #endif static uint32_t usual[] = { 0xffffdbfe, 0x7fff37d6, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; static int8_t unhex[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; static const char * errstr_map[] = { "htparse_error_none", "htparse_error_too_big", "htparse_error_invalid_method", "htparse_error_invalid_requestline", "htparse_error_invalid_schema", "htparse_error_invalid_protocol", "htparse_error_invalid_version", "htparse_error_invalid_header", "htparse_error_invalid_chunk_size", "htparse_error_invalid_chunk", "htparse_error_invalid_state", "htparse_error_user", "htparse_error_status", "htparse_error_unknown" }; static const char * method_strmap[] = { "GET", "HEAD", "POST", "PUT", "DELETE", "MKCOL", "COPY", "MOVE", "OPTIONS", "PROPFIND", "PROPATCH", "LOCK", "UNLOCK", "TRACE", "CONNECT", "PATCH", }; #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) #define _str3Ocmp(m, c0, c1, c2, c3) \ *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) #define _str4cmp(m, c0, c1, c2, c3) \ *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) #define _str5cmp(m, c0, c1, c2, c3, c4) \ *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \ && m[4] == c4 #define _str6cmp(m, c0, c1, c2, c3, c4, c5) \ *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \ && (((uint32_t *)m)[1] & 0xffff) == ((c5 << 8) | c4) #define _str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \ *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \ && ((uint32_t *)m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4) #define _str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \ *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \ && ((uint32_t *)m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4) #define _str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \ *(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) { \ log_debug("enter"); \ if (hooks && (hooks)->__n) \ { \ return (hooks)->__n(p); \ } \ \ return 0; \ } #define __HTPARSE_GENDHOOK(__n) \ static inline int hook_ ## __n ## _run(htparser * p, \ htparse_hooks * hooks, \ const char * s, size_t l) { \ log_debug("enter"); \ if (hooks && (hooks)->__n) \ { \ return (hooks)->__n(p, s, l); \ } \ \ return 0; \ } __HTPARSE_GENHOOK(on_msg_begin) __HTPARSE_GENHOOK(on_hdrs_begin) __HTPARSE_GENHOOK(on_hdrs_complete) __HTPARSE_GENHOOK(on_new_chunk) __HTPARSE_GENHOOK(on_chunk_complete) __HTPARSE_GENHOOK(on_chunks_complete) __HTPARSE_GENHOOK(on_msg_complete) __HTPARSE_GENDHOOK(method) __HTPARSE_GENDHOOK(scheme) __HTPARSE_GENDHOOK(host) __HTPARSE_GENDHOOK(port) __HTPARSE_GENDHOOK(path) __HTPARSE_GENDHOOK(args) __HTPARSE_GENDHOOK(uri) __HTPARSE_GENDHOOK(hdr_key) __HTPARSE_GENDHOOK(hdr_val) __HTPARSE_GENDHOOK(body) __HTPARSE_GENDHOOK(hostname) static inline uint64_t 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; return 0; } for (value = 0; n--; str++) { uint64_t check; if (*str < '0' || *str > '9') { *err = 1; return 0; } check = value * 10 + (*str - '0'); if ((value && check <= value)) { *err = 1; return 0; } value = check; } return value; } static inline ssize_t _str_to_ssize_t(char * str, size_t n) { ssize_t value; if (n == 0) { return -1; } for (value = 0; n--; str++) { if (*str < '0' || *str > '9') { return -1; } value = value * 10 + (*str - '0'); #if 0 if (value > INTMAX_MAX) { return -1; } #endif } return value; } htpparse_error htparser_get_error(htparser * p) { return p->error; } const char * htparser_get_strerror(htparser * p) { htpparse_error e = htparser_get_error(p); if (e > htparse_error_generic) { return "htparse_no_such_error"; } return errstr_map[e]; } unsigned int htparser_get_status(htparser * p) { return p->status; } int htparser_should_keep_alive(htparser * p) { if (p->major > 0 && p->minor > 0) { if (p->flags & parser_flag_connection_close) { return 0; } else { return 1; } } else { if (p->flags & parser_flag_connection_keep_alive) { return 1; } else { return 0; } } return 0; } htp_scheme htparser_get_scheme(htparser * p) { return p->scheme; } htp_method htparser_get_method(htparser * p) { return p->method; } const char * htparser_get_methodstr_m(htp_method meth) { if (meth >= htp_method_UNKNOWN) { return NULL; } return method_strmap[meth]; } const char * htparser_get_methodstr(htparser * p) { return htparser_get_methodstr_m(p->method); } void htparser_set_major(htparser * p, unsigned char major) { p->major = major; } void htparser_set_minor(htparser * p, unsigned char minor) { p->minor = minor; } unsigned char htparser_get_major(htparser * p) { return p->major; } unsigned char htparser_get_minor(htparser * p) { return p->minor; } unsigned char htparser_get_multipart(htparser * p) { return p->multipart; } void * htparser_get_userdata(htparser * p) { return p->userdata; } void htparser_set_userdata(htparser * p, void * ud) { p->userdata = ud; } uint64_t htparser_get_content_pending(htparser * p) { return p->content_len; } uint64_t htparser_get_content_length(htparser * p) { return p->orig_content_len; } uint64_t htparser_get_bytes_read(htparser * p) { return p->bytes_read; } uint64_t htparser_get_total_bytes_read(htparser * p) { return p->total_bytes_read; } void htparser_init(htparser * p, htp_type type) { /* Do not memset entire string buffer. */ memset(p, 0, offsetof(htparser, buf)); p->buf[0] = '\0'; p->state = s_start; p->error = htparse_error_none; p->method = htp_method_UNKNOWN; p->type = type; } htparser * htparser_new(void) { 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; } static htp_method get_method(const char * m, const size_t sz) { switch (sz) { case 3: if (_str3_cmp(m, 'G', 'E', 'T', '\0')) { return htp_method_GET; } if (_str3_cmp(m, 'P', 'U', 'T', '\0')) { return htp_method_PUT; } break; case 4: if (m[1] == 'O') { if (_str3Ocmp(m, 'P', 'O', 'S', 'T')) { return htp_method_POST; } if (_str3Ocmp(m, 'C', 'O', 'P', 'Y')) { return htp_method_COPY; } if (_str3Ocmp(m, 'M', 'O', 'V', 'E')) { return htp_method_MOVE; } if (_str3Ocmp(m, 'L', 'O', 'C', 'K')) { return htp_method_LOCK; } } else { if (_str4cmp(m, 'H', 'E', 'A', 'D')) { return htp_method_HEAD; } } break; case 5: if (_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) { return htp_method_MKCOL; } if (_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) { return htp_method_TRACE; } if (_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) { return htp_method_PATCH; } break; case 6: if (_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) { return htp_method_DELETE; } if (_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) { return htp_method_UNLOCK; } break; case 7: if (_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', '\0')) { return htp_method_OPTIONS; } if (_str7_cmp(m, 'C', 'O', 'N', 'N', 'E', 'C', 'T', '\0')) { return htp_method_CONNECT; } break; case 8: if (_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D')) { return htp_method_PROPFIND; } break; case 9: if (_str9cmp(m, 'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H')) { return htp_method_PROPPATCH; } break; } /* switch */ return htp_method_UNKNOWN; } /* get_method */ #define HTP_SET_BUF(CH) do { \ if (evhtp_likely((p->buf_idx + 1) < PARSER_STACK_MAX)) { \ p->buf[p->buf_idx++] = CH; \ p->buf[p->buf_idx] = '\0'; \ } else { \ p->error = htparse_error_too_big; \ return i + 1; \ } \ } while (0) size_t htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) { unsigned char ch; char c; size_t i; log_debug("enter"); log_debug("p == %p", p); p->error = htparse_error_none; p->bytes_read = 0; for (i = 0; i < len; i++) { int res; int err; ch = data[i]; log_debug("[%p] data[%zu] = %c (%x)", p, i, isprint(ch) ? ch : ' ', ch); p->total_bytes_read += 1; p->bytes_read += 1; switch (p->state) { case s_start: log_debug("[%p] s_start", p); if (ch == CR || ch == LF) { break; } if ((ch < 'A' || ch > 'Z') && ch != '_') { p->error = htparse_error_inval_reqline; log_debug("s_start invalid fist char '%c'", ch); log_htparser__s_(p); return i + 1; } p->flags = 0; p->error = htparse_error_none; p->method = htp_method_UNKNOWN; p->multipart = 0; p->major = 0; p->minor = 0; p->content_len = 0; p->orig_content_len = 0; p->status = 0; p->status_count = 0; p->scheme_offset = NULL; p->host_offset = NULL; p->port_offset = NULL; p->path_offset = NULL; p->args_offset = NULL; res = hook_on_msg_begin_run(p, hooks); HTP_SET_BUF(ch); if (evhtp_likely(p->type == htp_type_request)) { p->state = s_method; } else if (p->type == htp_type_response && ch == 'H') { p->state = s_http_H; } else { log_debug("not type of request or response?"); log_htparser__s_(p); p->error = htparse_error_inval_reqline; return i + 1; } if (res) { p->error = htparse_error_user; return i + 1; } break; case s_method: log_debug("[%p] s_method", p); do { if (ch == ' ') { p->method = get_method(p->buf, p->buf_idx); res = hook_method_run(p, hooks, p->buf, p->buf_idx); p->buf_idx = 0; p->state = s_spaces_before_uri; if (res) { p->error = htparse_error_user; return i + 1; } break; } else { if ((ch < 'A' || ch > 'Z') && ch != '_') { p->error = htparse_error_inval_method; return i + 1; } HTP_SET_BUF(ch); } ch = data[++i]; } while (i < len); break; case s_spaces_before_uri: 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. */ HTP_SET_BUF(ch); 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; log_htparser__s_(p); return i + 1; } p->host_offset = &p->buf[p->buf_idx]; HTP_SET_BUF(ch); p->state = s_host; break; } /* switch */ break; } switch (ch) { case ' ': break; case '/': p->path_offset = &p->buf[p->buf_idx]; HTP_SET_BUF(ch); p->state = s_after_slash_in_uri; break; default: c = (unsigned char)(ch | 0x20); if (c >= 'a' && c <= 'z') { p->scheme_offset = &p->buf[p->buf_idx]; HTP_SET_BUF(ch); p->state = s_schema; break; } p->error = htparse_error_inval_reqline; log_htparser__s_(p); return i + 1; } /* switch */ break; case s_schema: log_debug("[%p] s_schema", p); c = (unsigned char)(ch | 0x20); if (c >= 'a' && c <= 'z') { HTP_SET_BUF(ch); break; } switch (ch) { case ':': p->scheme = htp_scheme_unknown; switch (p->buf_idx) { case 3: if (_str3_cmp(p->scheme_offset, 'f', 't', 'p', '\0')) { p->scheme = htp_scheme_ftp; break; } if (_str3_cmp(p->scheme_offset, 'n', 'f', 's', '\0')) { p->scheme = htp_scheme_nfs; break; } break; case 4: if (_str4cmp(p->scheme_offset, 'h', 't', 't', 'p')) { p->scheme = htp_scheme_http; break; } break; case 5: if (_str5cmp(p->scheme_offset, 'h', 't', 't', 'p', 's')) { p->scheme = htp_scheme_https; break; } break; } /* switch */ res = hook_scheme_run(p, hooks, p->scheme_offset, (&p->buf[p->buf_idx] - p->scheme_offset)); HTP_SET_BUF(ch); p->state = s_schema_slash; if (res) { p->error = htparse_error_user; return i + 1; } break; default: p->error = htparse_error_inval_schema; return i + 1; } /* switch */ break; case s_schema_slash: log_debug("[%p] s_schema_slash", p); switch (ch) { case '/': HTP_SET_BUF(ch); p->state = s_schema_slash_slash; break; default: p->error = htparse_error_inval_schema; return i + 1; } break; case s_schema_slash_slash: log_debug("[%p] s_schema_slash_slash", p); switch (ch) { case '/': HTP_SET_BUF(ch); p->host_offset = &p->buf[p->buf_idx]; p->state = s_host; break; default: p->error = htparse_error_inval_schema; return i + 1; } break; case s_host: if (ch == '[') { /* Literal IPv6 address start. */ HTP_SET_BUF(ch); p->host_offset = &p->buf[p->buf_idx]; p->state = s_host_ipv6; break; } if (is_host_char(ch)) { HTP_SET_BUF(ch); break; } res = hook_host_run(p, hooks, p->host_offset, (&p->buf[p->buf_idx] - p->host_offset)); if (res) { p->error = htparse_error_user; return i + 1; } /* successfully parsed a NON-IPV6 hostname, knowing this, the * current character in 'ch' is actually the next state, so we * we fall through to avoid another loop. */ case s_host_done: res = 0; switch (ch) { case ':': HTP_SET_BUF(ch); p->port_offset = &p->buf[p->buf_idx]; p->state = s_port; break; case ' ': /* this technically should never happen, but we should * check anyway */ if (i == 0) { p->error = htparse_error_inval_state; return i + 1; } i--; ch = '/'; /* to accept requests like :// * we fallthrough to the next case. */ case '/': p->path_offset = &p->buf[p->buf_idx]; HTP_SET_BUF(ch); p->state = s_after_slash_in_uri; break; default: p->error = htparse_error_inval_schema; return i + 1; } /* switch */ if (res) { p->error = htparse_error_user; return i + 1; } break; case s_host_ipv6: c = (unsigned char)(ch | 0x20); if ((c >= 'a' && c <= 'f') || (ch >= '0' && ch <= '9') || ch == ':' || ch == '.') { HTP_SET_BUF(ch); break; } switch (ch) { case ']': res = hook_host_run(p, hooks, p->host_offset, (&p->buf[p->buf_idx] - p->host_offset)); if (res) { p->error = htparse_error_user; return i + 1; } HTP_SET_BUF(ch); p->state = s_host_done; break; default: p->error = htparse_error_inval_schema; return i + 1; } /* switch */ break; case s_port: if (ch >= '0' && ch <= '9') { HTP_SET_BUF(ch); break; } res = hook_port_run(p, hooks, p->port_offset, (&p->buf[p->buf_idx] - p->port_offset)); switch (ch) { case ' ': /* this technically should never happen, but we should * check anyway */ if (i == 0) { p->error = htparse_error_inval_state; return i + 1; } i--; ch = '/'; /* to accept requests like :// * we fallthrough to the next case. */ case '/': HTP_SET_BUF(ch); p->path_offset = &p->buf[p->buf_idx - 1]; p->state = s_after_slash_in_uri; break; default: p->error = htparse_error_inval_reqline; log_debug("[s_port] inval_reqline"); log_htparser__s_(p); return i + 1; } /* switch */ if (res) { p->error = htparse_error_user; return i + 1; } break; case s_after_slash_in_uri: log_debug("[%p] s_after_slash_in_uri", p); res = 0; if (usual[ch >> 5] & (1 << (ch & 0x1f))) { HTP_SET_BUF(ch); p->state = s_check_uri; break; } switch (ch) { case ' ': { int r1 = hook_path_run(p, hooks, p->path_offset, (&p->buf[p->buf_idx] - p->path_offset)); int r2 = hook_uri_run(p, hooks, p->buf, p->buf_idx); p->state = s_http_09; p->buf_idx = 0; if (r1 || r2) { res = 1; } } break; case CR: p->minor = 9; p->state = s_almost_done; break; case LF: p->minor = 9; p->state = s_hdrline_start; break; case '.': case '%': case '/': case '#': HTP_SET_BUF(ch); p->state = s_uri; break; case '?': res = hook_path_run(p, hooks, p->path_offset, (&p->buf[p->buf_idx] - p->path_offset)); HTP_SET_BUF(ch); p->args_offset = &p->buf[p->buf_idx]; p->state = s_uri; break; default: HTP_SET_BUF(ch); p->state = s_check_uri; break; } /* switch */ if (res) { p->error = htparse_error_user; return i + 1; } break; case s_check_uri: res = 0; do { log_debug("[%p] s_check_uri", p); if (usual[ch >> 5] & (1 << (ch & 0x1f))) { HTP_SET_BUF(ch); } else { break; } ch = data[++i]; } while (i < len); switch (ch) { case ' ': { int r1 = 0; int r2 = 0; if (p->args_offset) { r1 = hook_args_run(p, hooks, p->args_offset, (&p->buf[p->buf_idx] - p->args_offset)); } else { r1 = hook_path_run(p, hooks, p->path_offset, (&p->buf[p->buf_idx] - p->path_offset)); } r2 = hook_uri_run(p, hooks, p->buf, p->buf_idx); p->buf_idx = 0; p->state = s_http_09; if (r1 || r2) { res = 1; } } break; case '/': HTP_SET_BUF(ch); p->state = s_after_slash_in_uri; break; case CR: p->minor = 9; p->buf_idx = 0; p->state = s_almost_done; break; case LF: p->minor = 9; p->buf_idx = 0; p->state = s_hdrline_start; break; case '?': res = hook_path_run(p, hooks, p->path_offset, (&p->buf[p->buf_idx] - p->path_offset)); HTP_SET_BUF(ch); p->args_offset = &p->buf[p->buf_idx]; p->state = s_uri; break; default: HTP_SET_BUF(ch); p->state = s_uri; break; } /* switch */ if (res) { p->error = htparse_error_user; return i + 1; } break; case s_uri: log_debug("[%p] s_uri", p); res = 0; do { if (usual[ch >> 5] & (1 << (ch & 0x1f))) { HTP_SET_BUF(ch); } else { break; } ch = data[++i]; } while (i < len); switch (ch) { case ' ': { int r1 = 0; int r2 = 0; if (p->args_offset) { r1 = hook_args_run(p, hooks, p->args_offset, (&p->buf[p->buf_idx] - p->args_offset)); } else { r1 = hook_path_run(p, hooks, p->path_offset, (&p->buf[p->buf_idx] - p->path_offset)); } p->buf_idx = 0; p->state = s_http_09; if (r1 || r2) { res = 1; } } break; case CR: p->minor = 9; p->buf_idx = 0; p->state = s_almost_done; break; case LF: p->minor = 9; p->buf_idx = 0; p->state = s_hdrline_start; break; case '?': /* RFC 3986 section 3.4: * The query component is indicated by the * first question mark ("?") character and * terminated by a number sign ("#") character * or by the end of the URI. */ if (!p->args_offset) { res = hook_path_run(p, hooks, p->path_offset, (&p->buf[p->buf_idx] - p->path_offset)); HTP_SET_BUF(ch); p->args_offset = &p->buf[p->buf_idx]; break; } /* Fall through. */ default: HTP_SET_BUF(ch); break; } /* switch */ if (res) { p->error = htparse_error_user; return i + 1; } break; case s_http_09: log_debug("[%p] s_http_09", p); switch (ch) { case ' ': break; case CR: p->minor = 9; p->buf_idx = 0; p->state = s_almost_done; break; case LF: p->minor = 9; p->buf_idx = 0; p->state = s_hdrline_start; break; case 'H': p->buf_idx = 0; p->state = s_http_H; break; default: p->error = htparse_error_inval_proto; return i + 1; } /* switch */ break; case s_http_H: log_debug("[%p] s_http_H", p); switch (ch) { case 'T': p->state = s_http_HT; break; default: p->error = htparse_error_inval_proto; return i + 1; } break; case s_http_HT: switch (ch) { case 'T': p->state = s_http_HTT; break; default: p->error = htparse_error_inval_proto; return i + 1; } break; case s_http_HTT: switch (ch) { case 'P': p->state = s_http_HTTP; break; default: p->error = htparse_error_inval_proto; return i + 1; } break; case s_http_HTTP: switch (ch) { case '/': p->state = s_first_major_digit; break; default: p->error = htparse_error_inval_proto; return i + 1; } break; case s_first_major_digit: if (ch < '1' || ch > '9') { p->error = htparse_error_inval_ver; return i + 1; } p->major = ch - '0'; p->state = s_major_digit; break; case s_major_digit: if (ch == '.') { p->state = s_first_minor_digit; break; } if (ch < '0' || ch > '9') { p->error = htparse_error_inval_ver; return i + 1; } p->major = p->major * 10 + ch - '0'; break; case s_first_minor_digit: if (ch < '0' || ch > '9') { p->error = htparse_error_inval_ver; return i + 1; } p->minor = ch - '0'; p->state = s_minor_digit; break; case s_minor_digit: switch (ch) { case ' ': if (evhtp_likely(p->type == htp_type_request)) { p->state = s_spaces_after_digit; } else if (p->type == htp_type_response) { p->state = s_status; } break; case CR: p->state = s_almost_done; break; case LF: /* LF without a CR? error.... */ p->error = htparse_error_inval_reqline; log_debug("[s_minor_digit] LF without CR!"); log_htparser__s_(p); return i + 1; default: if (ch < '0' || ch > '9') { p->error = htparse_error_inval_ver; return i + 1; } p->minor = p->minor * 10 + ch - '0'; break; } /* switch */ break; case s_status: /* http response status code */ if (ch == ' ') { if (p->status) { p->state = s_status_text; } break; } if (ch < '0' || ch > '9') { p->error = htparse_error_status; return i + 1; } p->status = p->status * 10 + ch - '0'; if (++p->status_count == 3) { p->state = s_space_after_status; } break; case s_space_after_status: switch (ch) { case ' ': p->state = s_status_text; break; case CR: p->state = s_almost_done; break; case LF: p->state = s_hdrline_start; break; default: p->error = htparse_error_generic; return i + 1; } break; case s_status_text: switch (ch) { case CR: p->state = s_almost_done; break; case LF: p->state = s_hdrline_start; break; default: break; } break; case s_spaces_after_digit: switch (ch) { case ' ': break; case CR: p->state = s_almost_done; break; case LF: p->state = s_hdrline_start; break; default: p->error = htparse_error_inval_ver; return i + 1; } break; case s_almost_done: switch (ch) { case LF: if (p->type == htp_type_response && p->status >= 100 && p->status < 200) { res = hook_on_hdrs_begin_run(p, hooks); if (res) { p->error = htparse_error_user; return i + 1; } p->status = 0; p->status_count = 0; p->state = s_start; break; } p->state = s_done; res = hook_on_hdrs_begin_run(p, hooks); if (res) { p->error = htparse_error_user; return i + 1; } break; default: p->error = htparse_error_inval_reqline; log_htparser__s_(p); return i + 1; } /* switch */ break; case s_done: switch (ch) { case CR: p->state = s_hdrline_almost_done; break; case LF: return i + 1; default: goto hdrline_start; } break; hdrline_start: case s_hdrline_start: log_debug("[%p] s_hdrline_start", p); p->buf_idx = 0; switch (ch) { case CR: p->state = s_hdrline_hdr_almost_done; break; case LF: p->state = s_hdrline_hdr_done; break; default: HTP_SET_BUF(ch); p->state = s_hdrline_hdr_key; break; } break; case s_hdrline_hdr_key: log_debug("[%p] s_hdrline_hdr_key", p); do { if (evhtp_unlikely(ch == ':')) { res = hook_hdr_key_run(p, hooks, p->buf, p->buf_idx); /* figure out if the value of this header is valueable */ p->heval = eval_hdr_val_none; switch (p->buf_idx + 1) { case 5: if (!strcasecmp(p->buf, "host")) { p->heval = eval_hdr_val_hostname; } break; case 11: if (!strcasecmp(p->buf, "connection")) { p->heval = eval_hdr_val_connection; } break; case 13: if (!strcasecmp(p->buf, "content-type")) { p->heval = eval_hdr_val_content_type; } break; case 15: if (!strcasecmp(p->buf, "content-length")) { p->heval = eval_hdr_val_content_length; } break; case 17: if (!strcasecmp(p->buf, "proxy-connection")) { p->heval = eval_hdr_val_proxy_connection; } break; case 18: if (!strcasecmp(p->buf, "transfer-encoding")) { p->heval = eval_hdr_val_transfer_encoding; } break; } /* switch */ p->buf_idx = 0; p->state = s_hdrline_hdr_space_before_val; if (res) { p->error = htparse_error_user; return i + 1; } break; } switch (ch) { case CR: p->state = s_hdrline_hdr_almost_done; break; case LF: p->state = s_hdrline_hdr_done; break; default: HTP_SET_BUF(ch); break; } if (p->state != s_hdrline_hdr_key) { break; } ch = data[++i]; } while (i < len); break; case s_hdrline_hdr_space_before_val: log_debug("[%p] s_hdrline_hdr_space_before_val", p); switch (ch) { case ' ': break; case CR: /* * we have an empty header value here, so we set the buf * to empty, set the state to hdrline_hdr_val, and * decrement the start byte counter. */ HTP_SET_BUF(' '); p->state = s_hdrline_hdr_val; /* * make sure the next pass comes back to this CR byte, * so it matches in s_hdrline_hdr_val. */ i--; break; case LF: /* never got a CR for an empty header, this is an * invalid state. */ p->error = htparse_error_inval_hdr; return i + 1; default: HTP_SET_BUF(ch); p->state = s_hdrline_hdr_val; break; } /* switch */ break; case s_hdrline_hdr_val: err = 0; do { log_debug("[%p] s_hdrline_hdr_val", p); if (ch == CR) { switch (p->heval) { case eval_hdr_val_none: break; case eval_hdr_val_hostname: if (hook_hostname_run(p, hooks, p->buf, p->buf_idx)) { p->state = s_hdrline_hdr_almost_done; p->error = htparse_error_user; return i + 1; } break; case eval_hdr_val_content_length: p->content_len = str_to_uint64(p->buf, p->buf_idx, &err); p->orig_content_len = p->content_len; log_debug("[%p] s_hdrline_hdr_val content-lenth = %zu", p, p->content_len); if (err == 1) { p->error = htparse_error_too_big; return i + 1; } break; case eval_hdr_val_connection: switch (p->buf[0]) { char A_case; char C_case; const char * S_buf; case 'K': case 'k': if (p->buf_idx != 10) { break; } A_case = (p->buf[5] == 'A') ? 'A' : 'a'; S_buf = (const char *)(p->buf + 1); if (_str9cmp(S_buf, 'e', 'e', 'p', '-', A_case, 'l', 'i', 'v', 'e')) { p->flags |= parser_flag_connection_keep_alive; } break; case 'c': case 'C': if (p->buf_idx != 5) { break; } C_case = (p->buf[0] == 'C') ? 'C' : 'c'; S_buf = (const char *)p->buf; if (_str5cmp(S_buf, C_case, 'l', 'o', 's', 'e')) { p->flags |= parser_flag_connection_close; } break; } /* switch */ break; case eval_hdr_val_transfer_encoding: if (p->buf_idx != 7) { break; } switch (p->buf[0]) { const char * S_buf; case 'c': case 'C': if (p->buf_idx != 7) { break; } S_buf = (const char *)(p->buf + 1); if (_str6cmp(S_buf, 'h', 'u', 'n', 'k', 'e', 'd')) { p->flags |= parser_flag_chunked; } break; } break; case eval_hdr_val_content_type: if (p->buf_idx != 9) { break; } switch (p->buf[0]) { const char * S_buf; case 'm': case 'M': S_buf = (const char *)(p->buf + 1); if (_str8cmp(S_buf, 'u', 'l', 't', 'i', 'p', 'a', 'r', 't')) { p->multipart = 1; } break; } break; case eval_hdr_val_proxy_connection: default: break; } /* switch */ p->state = s_hdrline_hdr_almost_done; break; } switch (ch) { case LF: /* LF before CR? invalid */ p->error = htparse_error_inval_hdr; return i + 1; default: HTP_SET_BUF(ch); break; } /* switch */ if (p->state != s_hdrline_hdr_val) { break; } ch = data[++i]; } while (i < len); break; case s_hdrline_hdr_almost_done: log_debug("[%p] s_hdrline_hdr_almost_done", p); res = 0; switch (ch) { case LF: if (p->flags & parser_flag_trailing) { res = hook_on_msg_complete_run(p, hooks); p->state = s_start; break; } p->state = s_hdrline_hdr_done; break; default: p->error = htparse_error_inval_hdr; return i + 1; } if (res) { p->error = htparse_error_user; return i + 1; } break; case s_hdrline_hdr_done: log_debug("[%p] s_hdrline_hdr_done", p); switch (ch) { case CR: res = hook_hdr_val_run(p, hooks, p->buf, p->buf_idx); p->state = s_hdrline_almost_done; if (res) { p->error = htparse_error_user; return i + 1; } break; case LF: /* got LFLF? is this valid? */ p->error = htparse_error_inval_hdr; return i + 1; case '\t': /* this is a multiline header value, we must go back to * reading as a header value */ p->state = s_hdrline_hdr_val; break; default: res = hook_hdr_val_run(p, hooks, p->buf, p->buf_idx); p->buf_idx = 0; HTP_SET_BUF(ch); p->state = s_hdrline_hdr_key; if (res) { p->error = htparse_error_user; return i + 1; } break; } /* switch */ break; case s_hdrline_almost_done: log_debug("[%p] s_hdrline_almost_done", p); switch (ch) { case LF: res = hook_on_hdrs_complete_run(p, hooks); if (res != 0) { p->error = htparse_error_user; return i + 1; } p->buf_idx = 0; if (p->flags & parser_flag_trailing) { res = hook_on_msg_complete_run(p, hooks); p->state = s_start; } else if (p->flags & parser_flag_chunked) { p->state = s_chunk_size_start; } else if (p->content_len > 0) { p->state = s_body_read; } else if (p->content_len == 0) { res = hook_on_msg_complete_run(p, hooks); p->state = s_start; } else { p->state = s_hdrline_done; } if (res != 0) { p->error = htparse_error_user; return i + 1; } break; default: p->error = htparse_error_inval_hdr; return i + 1; } /* switch */ if (res != 0) { p->error = htparse_error_user; return i + 1; } break; case s_hdrline_done: log_debug("[%p] s_hdrline_done", p); res = 0; if (p->flags & parser_flag_trailing) { res = hook_on_msg_complete_run(p, hooks); p->state = s_start; } else if (p->flags & parser_flag_chunked) { p->state = s_chunk_size_start; i--; } else if (p->content_len > 0) { p->state = s_body_read; i--; } 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; } break; case s_chunk_size_start: c = unhex[(unsigned char)ch]; if (c == -1) { p->error = htparse_error_inval_chunk_sz; return i + 1; } p->content_len = c; p->state = s_chunk_size; break; case s_chunk_size: if (ch == CR) { p->state = s_chunk_size_almost_done; break; } c = unhex[(unsigned char)ch]; if (c == -1) { p->error = htparse_error_inval_chunk_sz; return i + 1; } p->content_len *= 16; p->content_len += c; break; case s_chunk_size_almost_done: if (ch != LF) { p->error = htparse_error_inval_chunk_sz; return i + 1; } p->orig_content_len = p->content_len; if (p->content_len == 0) { res = hook_on_chunks_complete_run(p, hooks); p->flags |= parser_flag_trailing; p->state = s_hdrline_start; } else { res = hook_on_new_chunk_run(p, hooks); p->state = s_chunk_data; } if (res) { p->error = htparse_error_user; return i + 1; } break; case s_chunk_data: res = 0; { const char * pp = &data[i]; const char * pe = (const char *)(data + len); size_t to_read = _MIN_READ(pe - pp, p->content_len); if (to_read > 0) { res = hook_body_run(p, hooks, pp, to_read); i += to_read - 1; } if (to_read == p->content_len) { p->state = s_chunk_data_almost_done; } p->content_len -= to_read; } if (res) { p->error = htparse_error_user; return i + 1; } break; case s_chunk_data_almost_done: if (ch != CR) { p->error = htparse_error_inval_chunk; return i + 1; } p->state = s_chunk_data_done; break; case s_chunk_data_done: if (ch != LF) { p->error = htparse_error_inval_chunk; return i + 1; } p->orig_content_len = 0; p->state = s_chunk_size_start; if (hook_on_chunk_complete_run(p, hooks)) { p->error = htparse_error_user; return i + 1; } break; case s_body_read: res = 0; { const char * pp = &data[i]; const char * pe = (const char *)(data + len); size_t to_read = _MIN_READ(pe - pp, p->content_len); if (to_read > 0) { res = hook_body_run(p, hooks, pp, to_read); i += to_read - 1; p->content_len -= to_read; } if (res) { p->error = htparse_error_user; return i + 1; } 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; } } break; default: log_debug("[%p] This is a silly state....", p); 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; } } /* switch */ return i; } /* htparser_run */ libevhtp-1.2.18/refcount.h000066400000000000000000000036251342660753300154350ustar00rootroot00000000000000#pragma once #include #include #include #include static void (*REF_free)(void *) = free; static void * (* REF_realloc)(void *, size_t) = realloc; static void * (* REF_malloc)(size_t) = malloc; struct refcount_ { pthread_mutex_t mux; unsigned int count; char data[]; }; #define ref_upcast(DAT) \ (struct refcount_ *)((char *)(DAT - offsetof(struct refcount_, data))) #define ref_barrier(CODE) \ pthread_mutex_lock(&refc->mux); \ CODE \ pthread_mutex_unlock(&refc->mux) static inline void ref_init_functions(void *(*mallocf)(size_t), void * (*callocf)(size_t, size_t), void *(*reallocf)(void *, size_t), void (*freef)(void *)) { (void)callocf; REF_malloc = mallocf; REF_realloc = reallocf; REF_free = freef; } static unsigned int ref_inc(void * buf) { struct refcount_ * refc = ref_upcast(buf); unsigned int refs; ref_barrier({ refs = ++refc->count; }); return refs; } static unsigned int ref_dec(void * buf) { struct refcount_ * refc = ref_upcast(buf); unsigned int refs; ref_barrier({ refc->count -= 1; refs = refc->count; }); return refs; } static inline void * ref_malloc(size_t size) { struct refcount_ * refc; pthread_mutexattr_t attr; refc = REF_malloc(sizeof(*refc) + size); refc->count = 1; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&refc->mux, &attr); return refc->data; } static void ref_free(void * buf) { struct refcount_ * refc = ref_upcast(buf); ref_barrier({ if (--refc->count == 0) { pthread_mutex_unlock(&refc->mux); pthread_mutex_destroy(&refc->mux); return REF_free(refc); } }); } libevhtp-1.2.18/sslutils.c000066400000000000000000000267631342660753300154750ustar00rootroot00000000000000#include #include #include #include #include #include #include "evhtp/config.h" #include "evhtp/evhtp.h" #include "evhtp/sslutils.h" #include "internal.h" #if OPENSSL_VERSION_NUMBER < 0x10100000L #define X509_get0_notBefore(x) X509_get_notBefore(x) #define X509_get0_notAfter(x) X509_get_notAfter(x) #endif unsigned char * htp_sslutil_subject_tostr(evhtp_ssl_t * ssl) { unsigned char * subj_str; char * p; X509 * cert; X509_NAME * name; if (!ssl) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } if (!(name = X509_get_subject_name(cert))) { X509_free(cert); return NULL; } if (!(p = X509_NAME_oneline(name, NULL, 0))) { X509_free(cert); return NULL; } subj_str = (unsigned char *)strdup(p); OPENSSL_free(p); X509_free(cert); return subj_str; } unsigned char * htp_sslutil_issuer_tostr(evhtp_ssl_t * ssl) { X509 * cert; X509_NAME * name; char * p; unsigned char * issr_str; if (!ssl) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } if (!(name = X509_get_issuer_name(cert))) { X509_free(cert); return NULL; } if (!(p = X509_NAME_oneline(name, NULL, 0))) { X509_free(cert); return NULL; } issr_str = (unsigned char *)strdup(p); OPENSSL_free(p); X509_free(cert); return issr_str; } unsigned char * htp_sslutil_notbefore_tostr(evhtp_ssl_t * ssl) { BIO * bio; X509 * cert; const ASN1_TIME * time; size_t len; unsigned char * time_str; if (!ssl) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } if (!(time = X509_get0_notBefore(cert))) { X509_free(cert); return NULL; } if (!(bio = BIO_new(BIO_s_mem()))) { X509_free(cert); return NULL; } if (!ASN1_TIME_print(bio, time)) { BIO_free(bio); X509_free(cert); return NULL; } if ((len = BIO_pending(bio)) == 0) { BIO_free(bio); X509_free(cert); return NULL; } if (!(time_str = calloc(len + 1, 1))) { return NULL; } BIO_read(bio, time_str, len); BIO_free(bio); X509_free(cert); return time_str; } /* htp_sslutil_notbefore_tostr */ unsigned char * htp_sslutil_notafter_tostr(evhtp_ssl_t * ssl) { BIO * bio; X509 * cert; const ASN1_TIME * time; size_t len; unsigned char * time_str; if (!ssl) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } if (!(time = X509_get0_notAfter(cert))) { X509_free(cert); return NULL; } if (!(bio = BIO_new(BIO_s_mem()))) { X509_free(cert); return NULL; } if (!ASN1_TIME_print(bio, time)) { BIO_free(bio); X509_free(cert); return NULL; } if ((len = BIO_pending(bio)) == 0) { BIO_free(bio); X509_free(cert); return NULL; } if (!(time_str = calloc(len + 1, 1))) { return NULL; } BIO_read(bio, time_str, len); BIO_free(bio); X509_free(cert); return time_str; } /* htp_sslutil_notafter_tostr */ unsigned char * htp_sslutil_sha1_tostr(evhtp_ssl_t * ssl) { const EVP_MD * md_alg; X509 * cert; unsigned int n; unsigned char md[EVP_MAX_MD_SIZE]; unsigned char * buf = NULL; size_t offset; size_t nsz; int sz; int i; if (!ssl) { return NULL; } md_alg = EVP_sha1(); if (!md_alg) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } n = 0; if (!X509_digest(cert, md_alg, md, &n)) { return NULL; } nsz = 3 * n + 1; buf = (unsigned char *)calloc(nsz, 1); if (buf) { offset = 0; for (i = 0; i < n; i++) { sz = snprintf(buf + offset, nsz - offset, "%02X%c", md[i], (i + 1 == n) ? 0 : ':'); offset += sz; if (sz < 0 || offset >= nsz) { free(buf); buf = NULL; break; } } } X509_free(cert); return buf; } /* htp_sslutil_sha1_tostr */ unsigned char * htp_sslutil_serial_tostr(evhtp_ssl_t * ssl) { BIO * bio; X509 * cert; size_t len; unsigned char * ser_str; if (!ssl) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } if (!(bio = BIO_new(BIO_s_mem()))) { X509_free(cert); return NULL; } i2a_ASN1_INTEGER(bio, X509_get_serialNumber(cert)); if ((len = BIO_pending(bio)) == 0) { BIO_free(bio); X509_free(cert); return NULL; } if (!(ser_str = calloc(len + 1, 1))) { return NULL; } BIO_read(bio, ser_str, len); X509_free(cert); BIO_free(bio); return ser_str; } /* htp_sslutil_serial_tostr */ unsigned char * htp_sslutil_cipher_tostr(evhtp_ssl_t * ssl) { const SSL_CIPHER * cipher; const char * p; unsigned char * cipher_str; if (!ssl) { return NULL; } if (!(cipher = SSL_get_current_cipher(ssl))) { return NULL; } if (!(p = SSL_CIPHER_get_name(cipher))) { return NULL; } cipher_str = (unsigned char *)strdup(p); return cipher_str; } unsigned char * htp_sslutil_cert_tostr(evhtp_ssl_t * ssl) { X509 * cert; BIO * bio; unsigned char * raw_cert_str; unsigned char * cert_str; unsigned char * p; size_t raw_cert_len; size_t cert_len; int i; if (!ssl) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } if (!(bio = BIO_new(BIO_s_mem()))) { X509_free(cert); return NULL; } if (!PEM_write_bio_X509(bio, cert)) { BIO_free(bio); X509_free(cert); return NULL; } raw_cert_len = BIO_pending(bio); raw_cert_str = calloc(raw_cert_len + 1, 1); BIO_read(bio, raw_cert_str, raw_cert_len); cert_len = raw_cert_len - 1; for (i = 0; i < raw_cert_len - 1; i++) { if (raw_cert_str[i] == '\n') { /* * \n's will be converted to \r\n\t, so we must reserve * enough space for that much data. */ cert_len += 2; } } /* 2 extra chars, one for possible last char (if not '\n'), and one for NULL terminator */ cert_str = calloc(cert_len + 2, 1); p = cert_str; for (i = 0; i < raw_cert_len - 1; i++) { if (raw_cert_str[i] == '\n') { *p++ = '\r'; *p++ = '\n'; *p++ = '\t'; } else { *p++ = raw_cert_str[i]; } } /* Don't assume last character is '\n' */ if (raw_cert_str[i] != '\n') { *p++ = raw_cert_str[i]; } BIO_free(bio); X509_free(cert); free(raw_cert_str); return cert_str; } /* htp_sslutil_cert_tostr */ unsigned char * htp_sslutil_x509_ext_tostr(evhtp_ssl_t * ssl, const char * oid) { unsigned char * ext_str; X509 * cert; ASN1_OBJECT * oid_obj; int oid_pos; X509_EXTENSION * ext; ASN1_OCTET_STRING * octet; const unsigned char * octet_data; long xlen; int xtag; int xclass; if (!ssl) { return NULL; } if (!(cert = SSL_get_peer_certificate(ssl))) { return NULL; } if (!(oid_obj = OBJ_txt2obj(oid, 1))) { X509_free(cert); return NULL; } ext_str = NULL; oid_pos = X509_get_ext_by_OBJ(cert, oid_obj, -1); if (!(ext = X509_get_ext(cert, oid_pos))) { ASN1_OBJECT_free(oid_obj); X509_free(cert); return NULL; } if (!(octet = X509_EXTENSION_get_data(ext))) { ASN1_OBJECT_free(oid_obj); X509_free(cert); return NULL; } octet_data = octet->data; if (ASN1_get_object(&octet_data, &xlen, &xtag, &xclass, octet->length)) { ASN1_OBJECT_free(oid_obj); X509_free(cert); return NULL; } /* We're only supporting string data. Could optionally add support * for encoded binary data */ if (xlen > 0 && xtag == 0x0C && octet->type == V_ASN1_OCTET_STRING) { ext_str = (unsigned char *)strndup((const char *)octet_data, xlen); } ASN1_OBJECT_free(oid_obj); X509_free(cert); return ext_str; } /* htp_sslutil_x509_ext_tostr */ int htp_sslutil_verify2opts(const char * opts_str) { if (!opts_str || !strcasecmp(opts_str, "off")) { return SSL_VERIFY_NONE; } if (!strcasecmp(opts_str, "optional")) { return SSL_VERIFY_PEER; } if (!strcasecmp(opts_str, "on")) { return SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; } return -1; } int htp_sslutil_add_xheaders(evhtp_headers_t * hdrs, evhtp_ssl_t * ssl, short flags) { int i; if (!hdrs || !ssl) { return -1; } struct { const char * hdr_str; short flag; unsigned char * (* convfn)(evhtp_ssl_t *); } ssl_x_hdrs_[] = { { "X-SSL-Subject", HTP_SSLUTILS_XHDR_SUBJ, htp_sslutil_subject_tostr }, { "X-SSL-Issuer", HTP_SSLUTILS_XHDR_ISSR, htp_sslutil_issuer_tostr }, { "X-SSL-Notbefore", HTP_SSLUTILS_XHDR_NBFR, htp_sslutil_notbefore_tostr }, { "X-SSL-Notafter", HTP_SSLUTILS_XHDR_NAFR, htp_sslutil_notafter_tostr }, { "X-SSL-Serial", HTP_SSLUTILS_XHDR_SERL, htp_sslutil_serial_tostr }, { "X-SSL-Cipher", HTP_SSLUTILS_XHDR_CIPH, htp_sslutil_cipher_tostr }, { "X-SSL-Certificate", HTP_SSLUTILS_XHDR_CERT, htp_sslutil_cert_tostr }, { "X-SSL-SHA1", HTP_SSLUTILS_XHDR_SHA1, htp_sslutil_sha1_tostr }, { NULL, 0, NULL } }; /* remove all of the current x- headers if present */ evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Subject")); evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Issuer")); evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Notbefore")); evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Notafter")); evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Serial")); evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Cipher")); evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Certificate")); if (flags == 0) { return 0; } /* iterate over our ssl_x_hdrs_ struct array, compare the flags, * and if a xhdr flag is set, run the proper *_tostr function and * append it to the `hdrs` passed to this function. */ for (i = 0; ssl_x_hdrs_[i].hdr_str; i++) { char * o_str = NULL; if (flags & ssl_x_hdrs_[i].flag) { if ((o_str = (ssl_x_hdrs_[i].convfn)(ssl))) { evhtp_headers_add_header( hdrs, evhtp_header_new(ssl_x_hdrs_[i].hdr_str, o_str, 0, 1)); evhtp_safe_free(o_str, free); } } } return 0; } /* htp_sslutil_add_xheaders */ libevhtp-1.2.18/thread.c000066400000000000000000000223161342660753300150500ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #ifndef WIN32 #include #endif #include #include #include #include #include #include "internal.h" #include "evhtp/thread.h" typedef struct evthr_cmd evthr_cmd_t; typedef struct evthr_pool_slist evthr_pool_slist_t; struct evthr_cmd { uint8_t stop; void * args; evthr_cb cb; } __attribute__((packed)); TAILQ_HEAD(evthr_pool_slist, evthr); struct evthr_pool { #ifdef EVTHR_SHARED_PIPE int rdr; int wdr; #endif int nthreads; evthr_pool_slist_t threads; }; struct evthr { int rdr; int wdr; char err; ev_t * event; evbase_t * evbase; pthread_mutex_t lock; pthread_t * thr; evthr_init_cb init_cb; evthr_exit_cb exit_cb; void * arg; void * aux; #ifdef EVTHR_SHARED_PIPE int pool_rdr; struct event * shared_pool_ev; #endif TAILQ_ENTRY(evthr) next; }; #define _evthr_read(thr, cmd, sock) \ (recv(sock, cmd, sizeof(evthr_cmd_t), 0) == sizeof(evthr_cmd_t)) ? 1 : 0 static void _evthr_read_cmd(evutil_socket_t sock, short which, void * args) { evthr_t * thread; evthr_cmd_t cmd; int stopped; if (!(thread = (evthr_t *)args)) { return; } stopped = 0; if (evhtp_likely(_evthr_read(thread, &cmd, sock) == 1)) { stopped = cmd.stop; if (evhtp_likely(cmd.cb != NULL)) { (cmd.cb)(thread, cmd.args, thread->arg); } } if (evhtp_unlikely(stopped == 1)) { event_base_loopbreak(thread->evbase); } return; } /* _evthr_read_cmd */ static void * _evthr_loop(void * args) { evthr_t * thread; if (!(thread = (evthr_t *)args)) { return NULL; } if (thread == NULL || thread->thr == NULL) { pthread_exit(NULL); } thread->evbase = event_base_new(); thread->event = event_new(thread->evbase, thread->rdr, EV_READ | EV_PERSIST, _evthr_read_cmd, args); event_add(thread->event, NULL); #ifdef EVTHR_SHARED_PIPE if (thread->pool_rdr > 0) { thread->shared_pool_ev = event_new(thread->evbase, thread->pool_rdr, EV_READ | EV_PERSIST, _evthr_read_cmd, args); event_add(thread->shared_pool_ev, NULL); } #endif 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); pthread_mutex_lock(&thread->lock); if (thread->exit_cb != NULL) { (thread->exit_cb)(thread, thread->arg); } pthread_mutex_unlock(&thread->lock); if (thread->err == 1) { fprintf(stderr, "FATAL ERROR!\n"); } pthread_exit(NULL); } /* _evthr_loop */ evthr_res evthr_defer(evthr_t * thread, evthr_cb cb, void * arg) { evthr_cmd_t cmd = { .cb = cb, .args = arg, .stop = 0 }; if (send(thread->wdr, &cmd, sizeof(cmd), 0) <= 0) { return EVTHR_RES_RETRY; } return EVTHR_RES_OK; } evthr_res evthr_stop(evthr_t * thread) { evthr_cmd_t cmd = { .cb = NULL, .args = NULL, .stop = 1 }; if (send(thread->wdr, &cmd, sizeof(evthr_cmd_t), 0) < 0) { return EVTHR_RES_RETRY; } pthread_join(*thread->thr, NULL); return EVTHR_RES_OK; } evbase_t * evthr_get_base(evthr_t * thr) { return thr ? thr->evbase : NULL; } void evthr_set_aux(evthr_t * thr, void * aux) { if (thr) { thr->aux = aux; } } void * evthr_get_aux(evthr_t * thr) { return thr ? thr->aux : NULL; } int evthr_set_initcb(evthr_t * thr, evthr_init_cb cb) { if (thr == NULL) { return -1; } thr->init_cb = cb; return 0; } int evthr_set_exitcb(evthr_t * thr, evthr_exit_cb cb) { if (thr == NULL) { return -1; } thr->exit_cb = cb; return 0; } static evthr_t * _evthr_new(evthr_init_cb init_cb, evthr_exit_cb exit_cb, void * args) { evthr_t * thread; int fds[2]; if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) { return NULL; } evutil_make_socket_nonblocking(fds[0]); evutil_make_socket_nonblocking(fds[1]); if (!(thread = calloc(sizeof(evthr_t), 1))) { return NULL; } thread->thr = malloc(sizeof(pthread_t)); thread->arg = args; thread->rdr = fds[0]; thread->wdr = fds[1]; thread->init_cb = init_cb; thread->exit_cb = exit_cb; if (pthread_mutex_init(&thread->lock, NULL)) { evthr_free(thread); return NULL; } return thread; } /* evthr_new */ evthr_t * evthr_new(evthr_init_cb init_cb, void * args) { return _evthr_new(init_cb, NULL, args); } evthr_t * evthr_wexit_new(evthr_init_cb init_cb, evthr_exit_cb exit_cb, void * args) { return _evthr_new(init_cb, exit_cb, args); } int evthr_start(evthr_t * thread) { if (thread == NULL || thread->thr == NULL) { return -1; } if (pthread_create(thread->thr, NULL, _evthr_loop, (void *)thread)) { return -1; } return 0; } void evthr_free(evthr_t * thread) { if (thread == NULL) { return; } if (thread->rdr > 0) { close(thread->rdr); } if (thread->wdr > 0) { close(thread->wdr); } if (thread->thr) { free(thread->thr); } if (thread->event) { event_free(thread->event); } #ifdef EVTHR_SHARED_PIPE if (thread->shared_pool_ev) { event_free(thread->shared_pool_ev); } #endif if (thread->evbase) { event_base_free(thread->evbase); } free(thread); } /* evthr_free */ void evthr_pool_free(evthr_pool_t * pool) { evthr_t * thread; evthr_t * save; if (pool == NULL) { return; } TAILQ_FOREACH_SAFE(thread, &pool->threads, next, save) { TAILQ_REMOVE(&pool->threads, thread, next); evthr_free(thread); } free(pool); } evthr_res evthr_pool_stop(evthr_pool_t * pool) { evthr_t * thr; evthr_t * save; if (pool == NULL) { return EVTHR_RES_FATAL; } TAILQ_FOREACH_SAFE(thr, &pool->threads, next, save) { evthr_stop(thr); } return EVTHR_RES_OK; } static inline int get_backlog_(evthr_t * thread) { int backlog = 0; ioctl(thread->rdr, FIONREAD, &backlog); return (int)(backlog / sizeof(evthr_cmd_t)); } evthr_res evthr_pool_defer(evthr_pool_t * pool, evthr_cb cb, void * arg) { #ifdef EVTHR_SHARED_PIPE evthr_cmd_t cmd = { .cb = cb, .args = arg, .stop = 0 }; if (evhtp_unlikely(send(pool->wdr, &cmd, sizeof(cmd), 0) == -1)) { return EVTHR_RES_RETRY; } return EVTHR_RES_OK; #endif evthr_t * thread = NULL; evthr_t * min_thread = NULL; int min_backlog = 0; if (pool == NULL) { return EVTHR_RES_FATAL; } if (cb == NULL) { return EVTHR_RES_NOCB; } TAILQ_FOREACH(thread, &pool->threads, next) { int backlog = get_backlog_(thread); if (backlog == 0) { min_thread = thread; break; } if (min_thread == NULL || backlog < min_backlog) { min_thread = thread; min_backlog = backlog; } } return evthr_defer(min_thread, cb, arg); } /* evthr_pool_defer */ static evthr_pool_t * _evthr_pool_new(int nthreads, evthr_init_cb init_cb, evthr_exit_cb exit_cb, void * shared) { evthr_pool_t * pool; int i; #ifdef EVTHR_SHARED_PIPE int fds[2]; #endif if (nthreads == 0) { return NULL; } if (!(pool = calloc(sizeof(evthr_pool_t), 1))) { return NULL; } pool->nthreads = nthreads; TAILQ_INIT(&pool->threads); #ifdef EVTHR_SHARED_PIPE if (evutil_socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) == -1) { return NULL; } evutil_make_socket_nonblocking(fds[0]); evutil_make_socket_nonblocking(fds[1]); pool->rdr = fds[0]; pool->wdr = fds[1]; #endif for (i = 0; i < nthreads; i++) { evthr_t * thread; if (!(thread = evthr_wexit_new(init_cb, exit_cb, shared))) { evthr_pool_free(pool); return NULL; } #ifdef EVTHR_SHARED_PIPE thread->pool_rdr = fds[0]; #endif TAILQ_INSERT_TAIL(&pool->threads, thread, next); } return pool; } /* _evthr_pool_new */ evthr_pool_t * evthr_pool_new(int nthreads, evthr_init_cb init_cb, void * shared) { return _evthr_pool_new(nthreads, init_cb, NULL, shared); } evthr_pool_t * evthr_pool_wexit_new(int nthreads, evthr_init_cb init_cb, evthr_exit_cb exit_cb, void * shared) { return _evthr_pool_new(nthreads, init_cb, exit_cb, shared); } int evthr_pool_start(evthr_pool_t * pool) { evthr_t * evthr = NULL; if (pool == NULL) { return -1; } TAILQ_FOREACH(evthr, &pool->threads, next) { if (evthr_start(evthr) < 0) { return -1; } usleep(5000); } return 0; } libevhtp-1.2.18/zimg_vs_nginx.png000066400000000000000000002106231342660753300170240ustar00rootroot00000000000000PNG  IHDR[$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYsgR@IDATxTҗ; %hlMD]1kTT+v]Ƃ!Q# t{ew333ΛsY6t{ih @ @%BB 9ٟg=lٲ"Y%rgn@ @#*ڵDy=C @ @ lݺY]߫Aֶm[w@ @MB-Wq@ @@@8rn@ @KaG @ '0/q @ |/ @ @%Na^ȹ! @ @`/^A @J¼đsC@ @^,8 @ 8y# @ {Yp@ @(qG !@ @{  @ P%B @@e @ @ K97 @ %0˂#@ @@@8rn@ @KaG @ '0/q @ |/ @ @%Na^ȹ! @ @`/^A @J¼đsC@H7K^a+=rrr [\{myh@(oyb@eիmv '~ZhaOy= BS-\ѣ}G "RBkcu%T/^! X^%K^kW\qE{b o{mٲ%ޭ?r1oɆ;ZΝū]R%U]ƏoW^O_7]vO?4OSLڵk_|W_E^yȑv뭷Zvns-?\ժU.r#ǎ=X:uj*nA "YÆ k/߿:E  @ ȋz˝Osb+ލ[nmYYYkxU^;w\ 6tHFȫ?bꫯx/iժUu7l/;c't5k$SDׯwjҤ}ֳgϤMbŊn4RfxU&On_|URōX4rYgY5}_|ю<Ȩ}?]W_u}Q{G}t" O5kքhzgSԀ"H0@(ydJ%{@H ƍ;y0+҉Ы* >P8q 7,򤴼 mܸq?{lw,צM<\g'o?۶mK?_PPc T^ׯo˖-38ò[W8&6dիM4< $xG5YejWĎB>w/bMr(:#) ^agĉxF}w@KӚHQ{~=裑%Oj"~_&}]{뭷L!a PzǞ;C@ H(Hzɀ.,N.i]B c庿$(5R/}4٠Ȇsj7on;wt+xE/7qDgu4(i} ~M^uo 賎g̘ߺA@(%7@([nuBd̙N܄W6m}7{|))|ݷ+O_,PVZNegNB"Z!| V豼 oShWub}Pu4! YvinA vRq]}%f9mG%(j!ӬICf 6PprDB./@Nn]߅x4*RGxpv(W&dtM JJ=w PhZ;J_{BU*wq!a!Q'iwqQCE^.g%YY7yԋ2]K4yqx%…XAu]"P VeiֻCɓgI@+*{, / ֭ U /:@ fM(Bp|o.&k4i~u-{xC@ ǍZJkZ/ƎERX)\ׯU(ۼy7ZbGb:ښkWȴĊ%bf*#=Z/yy}8)W>xS@yUGKIv[$Q" Y~k#QSBMQ7%I}yx9y•o֬Y+i֝+A= 9?1@Niu4YFJyC@ ʞB4@$HIVq4ZY8 >Bsf\#R7Kh-^&~Xj1c K/c% YmI6eJIk|f,a ノ4To4iM&U?%0xH&bdwZ3=xUve?C\ߑxںM\|L42M&{:lʃtjSx=ɓcUw^V}W _PNQeW"T]@@'Ί҂ħSJLP@/ sC5s\ 5о}?~u=b]s@%Ba^"  Pr.ʋIjs,2dKZk>d%Һ|c9[|p Sdy}(A[I`J)z r=o:+Q<.Aۻwo*]^kPb[뮻Ej+$\{=}(ᥙi?R@y&0/ϣϳCF`ҥ6tPn(T{+ڛ .VdH,kͭz뭡DO/vqUH+ۻ ^=.Vo^sqI|˫8b)U&ky%NV-`7UʲbJhY>}~,1otTA5 ^TJu%bZV<^Fy YWR~^(!Z8wxXh*9k=%LmE@IaB 2N@8 DMI۽ޛǣ')}dL=K+sT9y5I.J'r{kz}WԶ2ʴ6/۷oOxYs֚{Ąk8g8jIF*?n7-^c/.ӽ+-[QS}L-諎ړ8WyR<@@'όL@poeSqGDm:Ua W讶7)UYrEI^Uo O2vMo3Yke WRBM c/bhj )yǍ¤ZMaɚZ.A W^yy.a.ŬޭO4cAO rF ?O=<\d?)##2 oe&/B߽iK2Ky]K, c4i}_6o B y"V1$L:+7\\I5MVI =DXҩ[9_­V5x$_'hs~p O&l,SR> s4gBU,MH dQGp+>R#ܫHZ¡NU!b[dNv* zWԅ~/Z#~d`zѿ5#! yz1ygcg $ID?>0@&O\0ug`*rz$ $.$E X +1Wr*==nzFXbZKًwy?|>;Ex׿BwʳqKëc=&,t_"0&jO9h{b$+O)*ÇsTxdgq`XE\#_>&Ҷ@ LCpM}@@ ̋ΐ ynӎq+qSL(<-[&P#yVC9Mr-y7tZ/o24/o&Q4Y#?S4Q !o7Ѿ\IkAb~& J_ @e R#0pP]qF%jԩ>[n+10ydN/ItM.X _G:\¿n֖Sze>T ׺`ev_ي4%#NJ_@e 'l2WYTL9$ZPXuWhBt%=e|2״vWe+IHw%ܓ'U}n+3y]5Ql>}dD?kk4&uh$%.%䨕#oj?AQPҸgX|׾jh3rׅP"(,]{채ZRrȔ!rL'g J¼dys7@y\|N7/~=Ͻ-ǴA] CN bX[3o\Q޵'D駟> %>zW_}e\ >"Dދ~*SHyoDAwOW"j&23-!@ yQ@JHDQ-o)Kwy'$Zte>k,E }e9%7gr]rS΀X&a=*:bqWz]vh\~A3|h9e^kBJW -1-[ {v CMhHBo7_^}]y|}I9|xVPTM$wwA ?M:ﺠ߅)S_\ @!-@H<\rIzYL6qnnرNxy~0+YȮ<{vGk{:u-UkT(Զn|a޵<@KSO=0MD#á5E-I{ `?.a|aHWD~r,mOnJ2N!~nDyn[2o}U-{dD?3܊^Ph*-DSGiJn׿EHq;649D @ 5H+"0` p r[ji[Wg=^:_7]´>YKImyБ-ik-yeb d^=U 4faz{p_|1?!.Jȥ2ԏO>UW?%Mi]L,a"W[aHkCY_~e'YF>"l 5[=֬Yc\$sh~;ϑ뼕x.lRpBs9ZۊZQJ* j(=;묳B^s]NH#>C@|lW! 'kWb/ lB%uE  @kol>\!#Fp%d/򐨗ؕ/ȼx' ĻT.Qw1ǸnQ-!K/uM\Iь5WZ|]5=<0+c+@}wyM6- xy奎J6t2yN&Lj+AgEd(塇r^gҥ[+W@*M>Br䝲?{v &.};M-gMiY&% J¼tsW@HRM枅) ZWKGkSBG-Nd}D>Z^ yO|ČԋVF! T!LaքIJ)IB["P< Ѥv%%[,ry\sM(_^u J¼tsW@x#2_E~s}]_Vfe>>ʮ?}iE&"O7 P  V^rX<)[ϡlZP]y5&ދ2!qO|'N+IC.ODĥ?\u$$4qRy K@),Y`bΝ;wHV8Zzނ&"tO୾2UFB[\E3K'O&S$0YgD~oƌ㼻>Dc㯇+L=;_e4QmvI:V41>軭DkJܨXkYĺil4艈t>(Abāķ9iG&/8jDLW_=?>c PJVC  @ S07C:7p1H_9*@SQ1w/{ Yn |C _@8ʩNX,O:@.7 F)^xj ,@77p rcn  Žs@$ Vn0 D[D rh׾3=s㏾Zn, 7h>ո;ty[nL+آ, ) B؉ 7^& cLc#>OD%`čM s D+ Z#7v~߻["ٝ~$Dn>.YI/׶Lܝ&B=@l+e -75/9MP> "\פMgD,UHZN&=MF QpB X)^!Q 2]+$W6H`Dړ7HCb8!b[1J>-QcnFrq]Zb*؟PoWI^[ydGĽ$ A7aQg`-}n|@=E $jA&AȽ/|)EQd&T^  I(@y ,ỹ݅ڛ4rh  &JkW뎵OW}MkǏgVV28m*Ւ>V_8NV+&Z_nkJz~ek,mZD.rsՖxJW^y}ߴ;5YහKEjCY}^єXN)2,>AHGU3|pum0<G>A@ DgXvb=ZFv% Bi.nY%6S"5dhm%' @O \W3 nOdm_$+yPݖ&@*ECBXs'@l 2{= "um!dv[)9He:\%9KیҪt yiP8 tΔe|8x<@Ҝi>@t @6y_ @Ҝ<A @@&0/A @@@=@ @(e{|y:@ @Hs4 @ @e¼l/O@ @iNaD @ @l@ @ @ | @ m=< @ 9y݃ @ Ma^Ǘ @ 4'0O{ @ P t @ i>@t @6y_ @Ҝ<A @@&0/A @@@=@ @(e{|y:@ @Hs4 @ @e¼l/O@ @iNRqo˖-6}tYu)-6mdǏjժYϞ=-;;;j95k{uZn\qf\ @ @x̏>hӧuVZws=֭kGqr!Vvm6lXr0vXkڴu;8kӦu͖,Ylq| @  _~>s'<]8p=s6x`:uM8>`6myVN{m̙3_~*[m @ PR*7ol_G{ٲe /)bC5+}ȑVjU{'BUyS{v' ..2={3ƕ-6C @ PR[o 6=crKk}ְaC;O>+WZ LetB K+]^tGKXkWXk @җ@]!=+R&b~}7lZlƌ|^]WѣG{챵kڱN.^]+6ݴ-rъ5k @ %pYg1kzVVL+|}ukc^e%~Sk֬ ~VNt]dѦk88՘3g[Cdxjw @@zhԨQzt^K) +>pkc\~䑦md \;VNtއGIQm6_ $CB @ rM߶mfW]uLx 벍7+sNwNS!U9Ym5@ @@ (\[-X `wuWSLq=&wy$o2y?yƍ<2 /vmrh3~C @@֭[]O?~;mu&a~2&L;)?i$nݺeee\G8t  @ Pʮ0vG$eK,qSO՛Kcԩ6n8@MǫTb'p͛7Ǝ*uogy;_m! @ @H|؎-m6mf= @2:@ @Na#H!@ @ 3z< @ d:y  @ &0 @ > @2:@ @Na#H!@ @ 3z< @ d:y  @ &0 @ > @2:@ @Na#H!@ @ 3z< @ d:y  @ &0 @ > @2:@ @Na#H!@ @ 3z< @ d:y  @ &0 @ > @2:@ @Na#H!@ @ 3z< @ d:y  @ &0 @ > @2J*Ν;;vtIb _$Ge-Z۽{ /؞={ywwCthy* @ PLR㏛oisεO?ڵkg>}ߺuu͝/ ӦM#<ԩco͟?t3gZ~,'''s"|ԩ6qD;ꫯ>T.6C8 @ #xoFkРAm۶Cډ'hs ]Af쬳s.#c ޫWPG%K牶'.ᭉXƺy@ @ (7Z4](R&Ǎ璿ITGnwmGuT}UXѭKWC=n&g}\Ynݺy)ܚ5k ,[~}WFeUGH`? |i嫡,Z~G[Z] @ 5j@ʄ{gÇ7%wShyǎm.ӺO.\h u/gyyܺp]jU_-ϻk,^Ym&SYRHR?jԨaZWi6lp$$Ad>C @@&B8d۸q۷n!o+M5Hh(LE3m׵+r2WNeg  @ PR&}gmZ]6ukY]d>Ca7n؝Wևīlrmߏc@ @@qH0Uz.]GeEg;vpxV U4i;ףG~Xe)A2m)' @ @D %||K<۳gOr:K//ݻD '`ͳcǺs%W"3<ӝ:SݻubԩtJʦuɴ @ @$*ٳ|;tR{Jwo:ܮ];=λ.WVm^#Gڥ^ĩіlJwg{[zr-nmUW]围D U @ PR":t]tE6kxCgKh~7l١tio)!.ox-H[Ct6 do;޻wo1bu!T66C8 @ #aÆ x/߿ӧHR5%\kҤI(kz7md/ڵk[M{3ūN˖-s}N7h|a'OlFrlVԁ @ P6 Hjc1i[2?VZ֩ShS(ZV'6 @ @ UR-U@ @@y#0/o#B @@Z@p@ @(omy^@ @H+)MVOV:i۲`U[aY* @ @ ~Nbڊ|͵,~GYoflٹ1_N~_?fm}엶ZĽ>& vTvzURqq @@TTbvٍ͍=Yljî6A'zj п6,BZ?Xu3m\ 7T){Ϟ\`b*(Ec9@ @ c)Mj<8Wr*ڷz"X|eޚT+ 4θf}msI^x&Å @@"P*2ukTqۑ}%=Z7$'6eSu+'/Oj9ۣybnXu_3;Vi?*YŊl7Kl֝1G^ 9\$>C @MaM]ζ5.ZgY^b +[jlk6Bk3xVagržݶϬ/o'>'*լ2?$zth݈zpQp @@>|HJDvvY=lβM`ke5{qDk}m7l tl[NV'hWhf5Öޒ0$% @@9%Pqurrrl۶hѢb(^|͙3zmIߤ~ֶY-kӤhPשfukVq¼r q۫Pq5?w{m7rĭyqϮ]~[RՎU -x)["\vlϾ]Hbƍ6lUmg ަ~R ק/}k+ @ !ɷjŗZGX#~kY+&;֭o.Ī/ewӮ<ծ^%&Zmyx'u C @  TtZ&񊫬#[_fKٷM9#lT3>9e7&U @yY$%;'kF2{MB[+^֢ar}rG?d߽q @ r2бƾ7Zz*[l?Yc zۙZAT/A/_~c@ @e¼qbX]{~˭aV{*ڵyyIx me$ c! @ xku:uCau:vJlڐlЛCj.iT @yy==Z뮷f%v-?&^y%{$K; @ Ga^<'n[?5k&U777ז̦_;q76.$K !@ @a^FϘU:d;ɧq[$@ٱmjq vm cE4T @ ^5iݛJjYA> M;֭=`߾>uoS?6la=7\T= C @Hgt4@v.A\Ͷmyl[쌅ٕԷի$]96)6z҄P @@:@yߔ  _aU%֣U~]_cS݄6'w_,H!@ +yL++˚v})k}bժI|ON]v>˭Uj 9G.OA@ @Ha}X9kگe=۵ym}%O۟[lL89ܻБL^t  @@&@g⨥q6hh_m{vtO/_f{Ү]9nz[۾3' @ N4e/ڵۺ^we7km5cM֟خj{CeMfGyթS~m?>3gδ~YN^aUm:AD^tFkvoCX㖟4gIq뮸@ @$dV;vXؿӧOfkРAxvyO?-[̅ȑ#CW^m-[޽{ۗ_~.a?e%%]Oǽ8J}˖-bŊ|گ>j{xc'ݷmg&5mdSluvøծlH銎" @@zh޼[fWeb>\68%$R5k*W޻^sO?]?;0'W\DjBSNq\^tGKxѢEΫ~ Ь~֨kqVlI1˯ڸnsu2˪g @:+n2wCYƍܡK,lΞ\{ " @ TH0ҶiݻwwY[n풢͚5]V7r?!BصU-l?w4'P=aVc}iϕyOWk @RA wJ˔i]~?%0aC~KKt+{rףGW8 Mj{N uӺv򜷭{;n4 @ PR",XJ|7߸Dm~˳SO=Օ{饗L۽M:Ք=J$Nﰆ}&i^HϔvK9 K!@ @!Pqu+UE{>k\^O?ԶlbW?.B'Z;c\ue&>v81OBӛ7onegg;]w]r%ɊxϚ̵˗ݻ{dR69Y+ZCm;]6::o~ RQ12B_muNj4 @D@9ƏwIGg 6Վ;Sy:t=ՈK_|V1MtA[o;.:b{cƌ.fΜ<88[oE{q0yd5j{^K+$BTS͑ \lΎMZ5su\eD)15i$5_|W(>iN }ųh3 0/P]_6',u7yjw-?bAvھkj @".SU[uܹ@QYYYD*)\ATXhuIiV!% 0 k~nܢk6`M_>nY.B @H@%і(4#Xulڐ,gc{>ڠ[̲J%ռAuߣ?5"=&3.@ @ cV\@߳rg kxso>`h%`(5[ڟI6&cW= l @@#Ǽ y{wCw7ö\Y C|a5n˪`ou*١]'>uO!@ @|@/7O_=Hnmˏ?=WN]6GٞKnM{t++7l;v*Vf4 @Ҕv֡U~jYYQr @LaΣCRNR͚;;_~ YۛNm ,@yuvEv,-?py czJluw0l@ P%˛ UXoo>5ݲ_[[ P9JBۉ{uIkx{ ԫYy;7mժT7D\ @@@8rn*TW\eUֵGQ`nXe:Jdi`KV`8ChSmNjuWuw-;5|n}2.)}5峺6X)6m m'Q-a-ym۱;շ/ror',v]:XТR,:@,GͣF'ēyq}gw|1kfr kv۸6'߯0"Cݏ~s@SkT'Zp϶LlnMƽ9++Z4ɳ޺IM  @¼Xhhror:6mm}{;q~ˍLkQVڧHOUͱ5V]6=q/*4-i]֧˫ k8Nb  @¼ 4Y0={Y!wmƍq+l]Ԧ~p˪S21 ׽{)!ZqTXZ%kl8P+]7e{/)SA^U\-:nxԮ@e%Ǖn*#D! @u,wCw7ö\9Mb3~:^vEr =Ouw ??dɶ&n7ի'J3o,'l)3{Ir,_ͽ+B] tsd O2`ٚ&?f- DSX)!$L _ 7AA-[wK_o[~1-hZ|Jrkz:= 2s,ĶՊY^viX[)Lb]¹4L6Wתg]b݉ޠ5:HX| FŴ#mvs߶Oe>#Kȅ[\mb&MC .y\<\,6hh=&^}m]8.>m-ZXþ-W\qoZT脱n+9(ɄiZ]]/=-Pwb= .l kyFGq"?/~ J.', &[/sSSϲY$.Š@e INv\);*¼xj noMr۵iS'ݳǦ}l6f QC݃iEYn*nYj/5knujT%ԽX_zk[s* -yZnkص[iYEB(!aR0p_@ ,Xy|P7kڏ7ݚXjh'ҷeA Bok,͚Ynn.Vj>}Xzɭ-+Pp]/X׭^l\$˪Ƀ]1VPN^K6ئ `K7+]LuofmLn~ҕيm?l2AR+Q wE|o?6.vPFEn<7דcÖ)>-36= 3lnۭ%6|U6[;Z*{ uGl/J`_}򕖮 h-)r /rMt֭)0h{E W|E'AB\f 9 ŽEKy@+ۧ.M^bo?SX6[A  3ikhub[ȸ}8kp=+bxߊj{լj5-ca+P5!2r?KlUvE|a3Ufz=9jwZ,< D莑_.Ў +h[?JINAVGw9?˥877^|t_Wcy[0dϤѢJ`d[.//>om:'nD/%Zr)'huҵ[mB]ˣ\LI>Ľ\@]e_qDCھo܌UժQ ;O+ߣHrC@(N}2i[Rv_?N?%@ Uc a]; 3y:}K/AYfl͋۷bՃmԚ/n9.f6 6+mx/? nI8Ӟdܹ uFSK$Լ4wQpfسͱ#z4c5AP VkۤkLYEʳԎ2b;=j6W`),±lgBOL!0ϔiAbj[l╗ێuy֤g7mf;vr@@I fꋃ?@̗5۹+|̽$EXTL0y۴$zL0m}oB׋gGAa75T$- (%g7vNxxȕ;ֶi-;:px ʳ M 0i\n{;SlGoȰoKroт:WCէ,XIuBPD i2X[b4L1[6lhn&ٱ#fiSozګ6j&r 7KpxezC.Mgsgod CS2$@O[:lS'։sָW]lQK lagIsL6;D33D }ncD|4tekwK%Mڵx;?c5A}7gJ&hm}g˲:TݳR>MjNUi! ٻ𨊵ozo!@%4. W(b ( bPl+^ैTZi Cz/߼M{v7nrM}UDFΟ= UkU ePv PӥEbJ?{U `=T pe#8&s^IYa+=QPx{mU# Ԇ\D= ܽ U;ȯn`l g9? l $[$|Y(K[._9Ae|nOfn e7(ګNn~t:,2%%Z\l yկOAwtT7)՘ݕDDپ%nlW>*)V_5xn r=(j)vdt;v9/cVʐj%ppkծ,' pF槰ܕoG -y^??D.˦ww0{_w oԼq U7f@`^{=BCV=íjk$b϶a򕔖GoU5QmC /q8_#|镇гˌR_ݥ 7_tKq A)#q]5T `] iδ!ڲM9k7yVO7#(M׆^Qaf- /=o/CИ 0w۫/Dt1 ߾|"RGN8/ =Mba1o61F<5J Vzyz9|EF=ӣw7?'?d=Xi'лI9Dqcjs\ZuB9@S( ;/\PTlwR_y7]sZe$ 4ͽG^ 33Uӓ:|4UMs9H1ԯ]7stksmP'7v6#:kC;^MNYyhO*:2C5W zt;fn/e/qsopķżm[j8Y}i_O~z! T@J$zN=Iq^eGv@6 t87ˌb~8;>?$-Q?c4^4 >5q@xx4U)܂bi]6vU|y '<֟剡G)܃S  8m32$f]UhJF}i}x4GPtzK:&FYbq/L$'`H_$@@`n^O@9qtmcE5e 44lS:>Ayd?InbH.#G7?_xO>Yn[8Ț5kd nj#sO]dx|| J .4qeuc4blJL<999YKib+LC[תV!ߚJu_yUb' @ap9%pMԀ7jԨҕ|p.W(MP8܋ֹ>|REP>}hǎ${m\CF7_?ۅ)Фn}s1U:1w6ŴnKb @UÆ VH %0u\ UN: iy& 4ҡmڴS\Ν;'߻t"ߵ۷osKJJnS c>RՋ/I9*a^R5Y$zˍ)bTՙ *@ `@hhPX`W_+1bD+@|ΝB)sӧ dn osspSSS.uԑu.Ř6eE~MV:B-r-*a4~o6:eHTe+j#zZf@ @,[NT?2Q6ԩSڵk+{9Yʕ+iÆ t 9/\ˮ*sQ˦qwcڔ԰§YtdkT T+}֥V} @ %+}i>dk e8)Sh4|ϙ3GVL\222{_ qޮVq jv3 lA3,:;\nɤxR}U @Q r^8WKuE1駟Վ?.9q7\pDE2̝իS 6!ע%<2j=s?( @-0dmwqpi&jVd6䋀C @6"y[hAD17M626sۜPP-o((7M[p&:̘En~/O+ @ `u ̭~8!P[ iλzw%byP˪ @5㎳B,QkRC'gLT@ @@`n>K䩧 ;/%N|ss E@ @z̭g3Abxi-@IDAT8hKİTZRb.*@ @@`ngh9eu #%a? @ `%Vi `i'ggjnl`=T @,/8&MfGy雯) C@ @-!`u{zR:~j= @,+ܲh5"ۤ)E9gͤUa' @ `9斳EQ:]R_2x tbTn.*@ @@`n~Sx?5wדߛI% @ `^Dk9cϝ7X @  07'ZM y}"2xmI{__0X @  07%Z 8Qwwںt}fP @0s8ؼXFm >C C@ @̫o `7^닞zͥtz<ʼxQvB @@`^}CoݚLLNNN]G3S~rj= @'z~8v)ڳ5{9מ&  @@W GA>2<8}d_JsfoG @/hv#rrg7{q#P @0]f8#LQoE~͛_vЕ2X @ p8g0= 3xoW}O ;1X @ opX dzgFҩ5MŹC@ @@ 6ƍ[wHjJ޷WsC6$Vɿe+ފ|GoG @`/j@˯RJKJ+~6y"׬9q޲ =5Yԅ @@@`^+3n w/%$j&z+ϧ3K9{@@@ݿEKrSv @Ry|i Dxr(םM[NʗrWp9]͚ @pxq@W^dud0įv#]]'Vޒ#99"A @BjN"@n?m:^0Ry9\ u}fy^WokB& h݆:wF՞N@ `iFcoo0s6e^8/Eˈ9GcdFvkVQN6:q\!!n25 @%[BmBD6~) eP=CEn秤P{3+ @ `IEpP C[̉_.+PmwN$c5|dF @skj\p`-OeiQ!e^T.XϹ~ݬI{v#07( @ 5sB8dm ZAe7\%樟/ yxUY#Sy9  @U>9\7PrzŋTV}/--{(bê @,ܖ @3,ګwݖPի7˥&!ko" @ppq{;ggiDHyQzo#Y*u܃@ ز-_ gݺ׼^RÞo@ [@`nOPX^ < @*^H gOջM;} 32T`' @lU>\  o@Mj~R﫸?C @.cEBz͓0I @T>8\6jyi'OPQVVmcB 8sx;"BﭖQʟ@ تs[}2.@@XޕioH޳[G| @]50r O}vW̤;w޽{)77WiKv>+j`ٴǏSO{8 @ %0?{,ݛ4iB= 0"Đ &Piii(MGEtR^ߟ-ZT~WjՊ,o߾=]~\]KY Uk)),pv:@ `f ̗.]J)))dڽ{7}L~}Go&NHt)(4i}NS@@_._L_|P߾}X,Kw@ g߷׶.W@ f 'OL'Oqg}VqF%J .vQΝi͚5A˖-`?KdX}?0EFFҘ1cW^ .Ю]d]K|lNвi7|n\ @ aFUA7t;իW˞Çk񇐐ӧرdo;׍"]8a܋C-ѦM&''mVi?6@Upq%נ *uK"(?'Y~l @S.lJNƥe˖Νt٦|hӦLKJJ͛4h eqhS6l/x?oCUhCܹ6х2 @ <԰azX`W_[1b^eNVԩSGnJMM%g6XMٰ /^sx _D +'TZ ̠NbtSNw@D 44Aa_ud>*)0pnO^(Fx^ܭ\%ڔ 5mڴ0~ܕ/*U@t7+)_UxyT q@ P+WkJlfy4N9v \222[{ȟ9һtXM]6@xM81d]_I޻>l @K{RlJ6_ږ.ɉ^ ڀ3¨K36o\&6 dvQ@sQzõCҸK}>|lS nGm)ϥRfv]JȤѵlzV;Y?sג2ri'kM6Q۶mNSYfáC*;ze8%};6,f@Ū U(Z$VKy#!@]vt%lʱ!cB)0K`)✝w:}{1}ժUTZt@珻Ӑ!CҥKrmZMI*g6˝?@) nk( @eP^A=]rk=y/4lذJNSٳgk~}ײw3{zKjܧO.wiӚΝvAN-j_~ԭ[*]V^4bj~|r9sb R(W7n8=5kRE[r'ǎ#UDaP(-)#St5:uַ!@M E)+Ц˘qvv/E}r,wҶjU6xy睲w]iqkŋ/RoeW_>LN"YWV8{m*(~)hCm>#1ЄA9۷/q[nQll,U -&ضuchIClNpu @L͙=J.OiR)(Y~M6M=k֕\86ν\Ο?/ZݢE MP^s`nŬYMQZko]m7'ʲh+,Ѧ6!˦خRV/'1]o @$Ч}= Qtn˥abri=ۖXrg,OCn׮M2E٬_nPPX1\Y)-[_UNqoiŜdܖK涌kG c'rl] AHC۷׹!@- D5 $~siAwF"9HIaroPKۤ@ `/ ..%MtZI޳Gm7A @̭NB50癣@ @@ރ+,hvl,\ !@ Pk'-*֦ Q٤Uc' @)ܚ8 `лճ'#;UN@ `sP #"'YW.SvtGT@ G@`OZ*@QT>y/a' @[')K}8;[i\ @jt[лzϼx@ XKq@AA6Jήꃝ @@`ngОّ N @ PIy%lE sSST`' @,-h1 hZKKK zy @J̭@5#`p8>,V3Og @P+xRp3g C;n  @>sUBUW5{tiI ۧw?v@ @--!04=i/C@ Z,?|:j@eNESaFFm}B ؘs{ @^ odކK)~ @@`nI] ،{w̵B @jqwR['NPQvj @%[BmB6'Ӹ1y7{]%EEήW; @,)ܒh)^b @ dgϞ,O:E%bZSʕ+i֭f]mpq(̯Ao7\r[; @JK =8Q''gr  'rSOnݺtI +?ɓ'ӑ#GsJ>,5oޜ.\Vծ!0Dž#۴yիG :)),C)o}tF@ `_Ϗ~-ffZ9'տ?5{9rvwMݼy\]])11y/ʝTcUJpp0qP߰aên`(>\ ` Cٓ ;%& @ Kv]QO9n2<;o+hٲer=[SyM@ ΞwO]N @@pkW MV&8@ -\>#OiݺuZϞ=饗^xHxet?d{JgyFd9o/{9['jZ  J U`' @֪,qNs"w^VZј1cX￯7(yRÃΟ?lR}%K`+FEEQ۶m]8kQNN1 y!hӖ<{"1皣@ k cm'A=־Y\w_% Ϙ1iڴi'V,-tC.< ])|M6&wnb SoݺUn3Q~NJ[.W?Z8 ߆ՓRx8{Hzc @@@T;s-ksΝȫn=\Od$ s0[oV޽;EDDa<~"##ƮSYw_4gW=N Ԥ@JE&Q'&  @deZ|s~5zGi۶m\TÊmv~q)..Cy:WOuDb`juj}(;K MV'Έs*OOOjDwNϯvک C%cwr.ƶY!#~ΦJ5JC2*U@<ԚÇ#UG( 8@xxUwhwƁ8jc1K`c'-'4hI-P̟;wN ݾ}mb;~[-?'>@lWNV6T?ӎڙI^XHnU@ETAib R  @QxD//υ0K`nʅs sN'GTvөQF)SW,Yg055`]Nyυ1\iSV4W:|x*@6JE; *➟O55=&,*YWI>󶢌t*+]eKtw_~o䬕PEW]l ؃*z`>uTxx޽{\nÆ x^2^_/{6m];)b1!r@ .`k)SȤm>gd&v!zmtѫ,DY۹p]oorչXfF P+8;Z`w/5{ntZdu73/EKM \ K u[ E~(V$ji 5|r/{ْE @{駉Ǐݜ. Kdo<vQs]bLm3 P;F.bb=)kª / 졼V#OY6mP=(ȣQ  nM.J́4u?trd^Kǎ{f;mݺuGvcT; P{\ԗ:]R}zock*ˣ Tj<1-"UW}l @ jJ]ΌZJn뮻任2d\mu9܆7DGGӁdR6WnJA:О=U9yhtd"]o=sp~`stŔo @V,=999_ ~!]V͛7'^{۶m_5'LyǏu}Q6m5nX.Ifze8@B ]B%b]%;列wFvkY3ln7g[O="-Sq>F)RHß @.P#!p+Bvn+[Bם&5[:w,9s|HVWͽ5Q[Π˗d=4]߭lj_mOӹZu @')n@ή'#'iwB{nYV|4oˑySjמ~ xw\{y-\ON\}[&F po9F!}Z @s d?[7O>GXdI3`\#m&Ns🤫?(2BԒyE~|E";gsHLm"yh^R{?D0.rT,!!N|)ߪu] -h*Tjg#['O۶WWz+aobz%Kqz|{?NuC-JE$˿Zxkۚ[yy&7/<48w7@s6 C ^CLH /c׭?oRs=.^DW=5w}Z>]̹;_ z9Jョ('[}I+!@ 0@:BIJt4F>͞}^q7PMTUø&OAZfy]sƝp-|[yYP h 0G@{:b}6 ɪ[jc0ߟ<5>:b7m hM-iY~~jΧR煋@ c+z@իʁ9q]xy7j#tC o~-q#܄siB/"P @1̍QB@V <+=DZ 9xlri3[z."AVfE[]s;͛O.^>=@ 0ÇK#8udN =DH 7`FuRի|;9s:C @jt^~E$*ˢTfR`Dg_@QoIT5ث|8 {@`n/O Ԩ_/+ 89JWe2T1T܃+=z=C{|.feV& I{v7~5 @45@*ܽ;K㔡<&OH,:T*UU͟Z;!@ks]CK[߲C~rƻT\Pja}(#Ta' @O2 `>wPԛo .{۸|'GK 8sx @I`듈*W, U~@jA" * @a}ZoRcJIP @ 0@O͞y`k%%t情tauQ @`w@Vh4tE>>Kٔ~@c 0w狻 h1h3ə32p+ @+a-n jR ^{ ^BQN6xw^3X @pL\qW `m^@w4x%t|[`.*@ x @ViQoL::TO7 E@Kc=O  ؘ.ikrE9s:  @GyyָS@!g0=kd_*  8sx @qWc0x/lө`]T @3@P3+N;uJs^Z\l.*@ `!@ )ߩ0=`}T @\mpu %h0*ɦ+P":p<3xWhDF W=!vB `1E 4yi*Φ?@qAɗDb9x0rvsu(A `V@) @ xaP¯;M=RbKJJ;oop * E^]Ļx{;!@jp@ڼ:R}:݂[_11m\}|*̻Oٻoٻ @ Pcb^xԔ7t >1Ϻ|I ]ͷr.ہ<{C@.џ0ZՍO{OJgŽ%T(^U)Q1?*8lJM=\  @1s6?n=Rm1% ֯FA:;njq `|fS!^II(R^x^J2xw5|t(598=  @B@kђUAzY$O?s6vSS>w_H\[Ң))Y#& X?\ ._:Zs^qOeD0/<ƌ1trKTuB9k}A M%̔<Œ*;w&///uϟ?Oj׮EFFg6 ; @$D!!9Yz\{^YYQ_",f3@*f /^HG~yZn]Sat?<}wTRR"iĉ߿_()[R Md65 @yǼzYSR(+K.V}]l/ te ;gS4> @Zf 9~iƌzA74yd9r$_u4iem\N:Ep1,?;iԷo_:+qmvBjӸ|{MAasWUJZP>(_ QRL!O"w1$ɫQ PSf ̍xew cPԩ#\g]xz\,Ѧl؄_|||tfOOOpܟ ͢* @͞KbXe 'asڻSN/(B11ExX ow: EY'eue>+Ws=.juڦl@ `~L*˴Ve~ ?We3EyϨ\9DL]{|/W$C uj2: ggqխja@xG4"?d5+k]Ȥ8@ſĨD{n7#&/6@UsNEV.ߕ!]arJwUmS pY[}:;CnUE)rà p6ҵukMR,r,NΜNi V ̛5k& :Tɒ\Htz|0kmʆ  @v)ܭ;ud)u`+knпʊэyE^_oQҞfCK/R}fmAp= L@IDAT4[jj29www!C%߯xsNsDa@ )o̧#&g#kXF'C-@/HҠA*V~~>\>,{mFxI.9:g&?qmٲx)1Ccƌ5É7\|r5˓Y*!8(AٵkWy:` @ jGu:u[ǏSQvQgʍA v_&UQ8B EO'SBgRBʈjuu/J;5oAajU pNmwZhQCX_ׯu֭J7}\v/vC_5=r|رn:$p;΁+2.vqQHm< ͚5KLg6˝?;vn*˥UA Ay'4<;>:Tr5rh7: ^ă|HhNکh:E"Ol3Rk41j2)76d@̤O?o|Yr}elZ>MM^/:""B{sϖhIL؀,T XA ~V P$zs^yh:Yj4tΧU#/_wܯo_vdnH_l pځmʿyCA9_'3s=K@  ,[܄yю)ˡ9S7/oj$j7m2gu2mpLDu@ `ڽ\m.]ϕt >ޢCE@ " jvaWzR@TQJAAa,j8r[n?&UGIn!@# @ PUy~Mh<_clbUCb6G Lw~xi5cKaV&^8μlƶzlQ->\ @V([|k\y>n<<^Qg-/iBrKVxu:/ZLOä뉿&z_['Wp< @`nӏ@UDN|@^u}#Ghs9g7}5+rhޚBiiFCk;ltC h,Zwxy)r˨P 8ˀ'4yp $$$ŋ:^^^k-A `_g_j9J(;\#=nzʺ\)ƫn=rru5 KV.^dTP+4{99z@܄E.Œ ʼw("]#`AhԠAEaƊ"N& (*4Rwm7og{}og?w̹\8W"հ5pl_L}7{k}_ICJ5*锓ee\iҤLvʙ%A -WQ+4@DJv2v<^sqJYf Mr?WyZJ}:Rv%qchg\Ӛ5IRACed*e瞑uN5ַJ$n @%@1/O`gQCIB Tʶ3=Ne36Vh&==Mm{_ԅYYN_$1P_Ȗ d5'ʀuӦʶŋrW֬d v&nmϏ?=^cCQ??;ѼtR}{iTg n%^ ƍu[>H+D@'*IB ?G0>Զ+dP?N)y0Avm[oOӬ"Z-7Sv_eBà+$;']+3?4JZ^- ?xߺT[,rTJ>JL\MkMvוּ]!:R5u;vq:?=?Tb\=n#Xk7 "֧ZN ybD$ @t;[s]n>ZFrȟIeUS^nY@ZW4w Lk]U[7+or]v?RN_ei4.@9ߺ]K%b^AC1 d@NI?:S\YEN\*94 ѭfVҠ{2oܸwa+ʵ*ju}U~K[Jc]ͩSvҴ {V<,\J|.o"A  WP+4@2Kծh"ucgT6ZVss?U7{_ڏHJN|;/&iNܰ{wzRsT8j]U~bEe{IzKy$xżlC^)Y] @ %P);[=9lXgKmRZmGE'vpY_4?orwI} ?nGd @*JrA <uuĽԪ>kO/cg $f}UT|J~g'jFz<4:ҿN_+?YSl'byQ@@ T'>rOHǕ*εڈd]w\תoYw.;7m}cI]>:&mϿ-}']u[ 2D@ @1O!aQT@ %K;x2iܸLY;m|z%|⛥^n>jYƿJEP]w%.*"R.w m}ז-2?^#T( Mf @Nac{X޾My-޾dI7gW y꛼ǨѲ,tRv6Q~@ޢ/w ޷%l$$b^  @Jo^Zmf|>.^]ޑPòRusM۶muA҂cIJ @  Zum]dW&wlb|'CG%gwO޺պTF jpj @iK ϵr>zo>GLj.#Zh>v}_E۳c|2cJHKCN @(H ZV+NqgUb})Pۻ}:7,7dG:"b)qP@ L'qV7>M~FI"srrå9 'l;G>rl?Ot@ RP#% @ D\oOEwϷKZ|u禍ysRy*IB dϵZSZM!|_{Avn$?x?UK3y jC u~`\o 7y24m&ݭ[zJU+}#B<* @(@HQJ/.Ҫ&?l:jդeKV%V-'D%(y1`8 @ ZK|iXyE5M_v}Ra"Xn]]4C=TW^l\i' @ @pSO)S*zʕeΝRRعSJ~dձcҢEرݻw_,/ݻ2dH,f @ $}ҹsgD"}^}|JٳOu믿.-qɼyW^gϞ?Os9%믿ϭ/c9F:ycʒf"~@ @Hg̵.͚5;j3Fn*S5ݒx@&O,'p\)gy/׵lRx 9qi @ @ BQKΆO0A:uSʽkTW\gU1xsF/~ ywe͚5ҰaCiK?۶m˷޻dڵҥKeƍa!@ h޼UbQ !bnq{U]~;رl2=w\ݽ{XƝ4i,_=woUu0qNq @]۪U"d" WU+٢zϞ=eذaҺuk'[z DeeeInnnq4hh\F4]2.tw}|RvB9@ D@ƍQJ*\1eɒ%RV-7+G3<#?嫯r½eժU+R(z\k()MӠqT~IE,ß5kJ۶m ]i&wLwoP$@ @@Fp\LJom[Ǝ,k͛7GVjg\ZmנqkԨ~{4XIӻo@ @@ T*t_~Vmpt5֤Iwޒx[Rio@ @@2 DB1߱c<"Kg̘Q3gtǺv۵k羋3꺏,iʔ @ $P|޽EVo;~QGUi& .SF!s=o5 һeڴi(+/K.A@ @*@1W_urE;:\sMW\q{gСCe}u.^y3vZ?wzCG- =!ih!r~̓ôi/N: < +@(\CjՊDǵc*ORԯ_Х+QF ]P~þ? my1laO[F;@ѐcrrr~!oj]͛DsNiذa8@ @HQ5jz3@u kҤIa~C @N csU3 A9s;ֵkB8@ @H&>}Ե״mVZh̼BM;;;[[YZj+M7$sΕ~Zԩ;vZnh~vZNwlCG- =!ih!r~Yj3Jە A@SO>]Tncǎ%{?"-ثW/Q+C}.W|#V62 @ !e=zVxHXey2qD4hSč1n*Ç(@ @@(\9sdӦM#-[Pd@ @'qWyuP @ @hc @ @s?@ @H$%Y@ @(~( @ $k-I<IvϞ=2ydc}ٲe+[n J&Mk׮r&բ+d„ u\Vo޼vir'Jvvv u. D e- r-곊~QihłrxZ1|֭7!lV7￿}LՍU16lW@;{m&MdC;?/K[nf7v%sN3w\3j(>> 00&l*ϰ@[4y= m1 }R@@+'D~d7!b$^h"7W. 0-Zo .m㏥^2|7OoQ(mD~@E}/ *퀶ݖx(@broٳgDuﶟЯ_?Y|k?}2SrrrJo܋ɒiӦoDʌ((~gCTf"mw%}6 $ i&V\ _~~TԮ]W|? ~,Y"u񓴯80 ߶@[Ay6/=ByF=#>Z//ݕ@c^₊?̚5uyӦḾXb;vllzl۶\ve./XtBa rAA@h %'5П!:r+ !P~K+bcǎ%{?<@C`ԩnGԩt]ի'"XƍeƌpB|ĉҫWdU5zKn&cVZlڴI.]*SLno߾k5XWdz9.m1 Lߏż^1>H[#@[T<7#e=z&2HڕLg4hf}תUX?ƾ% :S㏛>:_ҥ{|+O!a`+0ہ 9 e_4CG?hJ~1 PE`\$R hڵkennݺҨQ#iҤXR6l` *$JC4~ 9/"bL&iG ~Ɯi'^*+VȄ dbKnnс͛idggeto;k׮bWNH˖-]?Q(rAދgCr&-@'b J,P/vz뭲wޘtн^PrncA|<`XrYYYր6A]~## e@"02!l9}ͳ! C4@YAʀz"L%A%1bOKhtEPCb5f*1;5ִiSs'ǎ3&k׮b/߶m\_|x96:խN:O6\rUW]e.Bӻw1}5dP֪U˕SN7W^y+vmg`rrr{d.ˀA^-ghgCGma?xF~' B 1Kc0G .MRV!TիdYƍeƌpB|ĉέV9祠7~K/g{ҪU+[tmڴI^f2esfRyZ6Δ?SϋU [.]wXe").3f~B1|_qrG:āˀh^;vȳ!%" M[-zυG|C KKl`#Wn6An ;߷Τ{vHI)Δ?裏Ηo|9Rj___SȰė03s]6o<%utʕ.3/zԓo|Q(r0y¾’C"φ^9*UWmĿ+!y"x ]VV^fuqFI&bWHIt^OJaÆ a\!GATL $~gr޲QhQ(}h(@ѣodggG>EkbgPy.t t~ڵkƣtp}\Zև[HEHdŊneݺubgֽ݀ynn;2&#v믋Z#Z]]9!-[LF.(9 79c_ATm1mѽ0A[yD]nn@Q"{͝wi쬽ηlM .>|޽{ 7`}lyufh9r]3*n)Y`/!._Zի;;t.(9ch0N) eO_@[tK@B◲ yB,C8=mڴq{u/*z?oѢnߺ_x|i?WEK/5 0͚5s狛?/K[nFMz>w\3j(gW-Z0֠2d׾aÆ||3i$ӿW֡C;?a9L[43ֈ`v2|R) 8&%Uc2;,\|.M͸q]:mn7SGŲ{g\\T(N1?ꨣ{xk!T. ."KMwn',5bADrA  2D z&9_ATm1m1.\yr7۷)jMeZuv1#tnì{9S9/)ۮd y'FtA2ӗ 4;0O6-51ݿ'Y|ۇ'8@Ce@Eڢ À~gCTGbmQe@@P̃`Z~Ψښ5kD}Wy̱|w⤓NrFtASCl v3V\cl?V:HY=,YU_2hbsT$*e@+rA4@H(<&*@{~d'_˚h`yNؙaczR =s΅Wmq_}W:^R?no'|+WlƌS(_|-B{`ر.Mݷ`z۶ms iAjf֬Y%&|~a9VM[4ΞE}m=cÿU!|=7!*;vXһwo[Bt#d…5Vq%K.Q*X3ְW%{-Gyd얶[T>D~uo4`:tM[ͨqAݏXK/p]ߪU+[WlڴI.]*v{G+XcmAdҘ:u[F:u$ݻw;81ck#v G&N((_`kBa9乁-󺶰$ 9l@y6xcUD8t}t~ :xAEѻtbI+?1ji=od>%I_wuΔ늄>͞з{ ^}7#_ ]we@ƭڠ-.1H@[-z,"A@ |C1OdXk3Z>z謼kԨQ>CtɄjP߸qԭ[ߤI뒙u,(9 oAr-jHb4b쥉(sQ!P-7?蠃:c͞|gm'[`*NHI ^d<^ nLWZV Xn Ɏ!>Y yv U8%[MvmҶm[9~&YuȼyD]X:w@tkM6m\9;v;;`].V;my 2?҂ @ +ӻ'.~6lX`SOɿ/{k_P?>/- ?;sqn4nVVx2|p.O[]YRPKjA4jjժtsQ5ʹS Emw}#W=m O<_Z{|yG}ٝw" #00ooegcU5)o d-ǎT^ *GusQgg+cY[ν( 4hP$u`D_< 6}^C { _2nr|q>U2 \HヶAMݧ*U^ P4nN[ Xea](ѕ %LL/=@Pwqۥ셒|Wr~UIҐ \G^{Ag$=Pw*A)83C:)skHO3ZAR7v)yo48b'c)^z:;}W5k:L5\c/J`C\NpǵAgfT]"n.BUvI1nTP_4n8_I*}20`i x}B~K,,/zuW̳ÞcǎL>wu?LK(gq/n]I>}s,#װ~NZe1}7oSAwؗgWcO/*rc>%Ծ}{\t.׍%.],Bj*b|NvڹZ#G\ f IDATCP{;m49s[!jKw -d=hشiS *#5TT={KʮЃ }~̾XTJg:v S쀥/ZdRɕa̘1riV!vKYb2ޅ$+'-d\z-_h]Y!ۥAe_l:j{A˦&/eS!A5FFރv5YwN g^w-CV 5j,ΉNl=\{JyA"5mRm؁A; Z_m[UuP#^+dpwǂ~1z 7,}%%K_puIUXTP+ZtZ֗SO=U}݂QS bԉG/ŋUA~}=eGbv ))-}{O?uu/JޞrfgKJL.rr쌥{WUv}Ѡʊ]=!v{3fg˔G"PCxv KW^q/`mNDڕ!e\teVctHנJ8R ŧUߪ曱/UTTA)ڥKTQ yKy2`UȀgdb,/jFa g"\ "7u Zj`R%6n*^{u?ܧ؈v`Хk=8ޖT/[d}㷌jBA;[nOzY%.9`  h~^W߂zר͘ ]die&6@}Pk~qcH}ۙ@SˌuY!NTsv)m߸0B*"Xh*g❬=Zc?j`N} T+֧z~崫%ZKHe/{smg%loWQemA O-{ 5/{ٵ?P0$Z U~T|*cwSmDc$=0`i  XUy,3`~1^@y+t܎-})v> OZwu -"u {˷*.ۺ\uTd=y„ EnнYZyI)]>ߒo׭AnP{^~₝:-)VV)ZG쌱n#`gݶmЭ/rKRU }ΤKw_b]m^,vmQ#hr:luߺnm5ad]Jgw@nϿnòn5 փvP}B)o@r 2D_ >y5zh̓?5 &/EW}TZ5kF%eL$F;+K5Q^@h!5Rg$SuF /,:V0ʠ:"B"@tQۢ=jUDɖCizFDyC @e$J+btIR7Y"V(9sF[L>A3[@f >_,־F?ȓ@DyBQ @ @+BG}$O=OƎlN)@<D!@ s !'k7kMw\[UFԏfQ(rA4`#u*7VSbUui(CX/z-9蠃|[D@PÖCJ E3̠3QovL Q`2 3}_-j{u5zoˀcqV"ҁ@R 2i$4f#- @N@}D?ҹsg&W_}uHJ\%T_]tu]?L/)hT(9@ܽ}f͚%{lcǎ˯~+o#(f̣&@(@@gov6l8߬@b/_W.--$ Q`2 [A}B6mD?裲k׮"$u+8ג~aq8(d &A[t 3gt3ժU+S #kam۶Ĥ-[&ח5k' e@"0:9sc=7nݺd˖-2p@YtL2x@}=Zb @2@bRn T @&b @2yFC @@PÖC @@F@1hSy@ @y @ @h(-~*@ @a@1[@ @M