diff options
Diffstat (limited to 'libs/libevent/docs')
117 files changed, 41910 insertions, 0 deletions
diff --git a/libs/libevent/docs/.gitignore b/libs/libevent/docs/.gitignore new file mode 100644 index 0000000000..01821fe1c5 --- /dev/null +++ b/libs/libevent/docs/.gitignore @@ -0,0 +1,135 @@ +### These files should get ignored no matter where they appear. + +# Editors leave these lying around +\#*\# +.#* +*~ +*.swp + +# C stuff +*.o + +# Windows stuff +*.obj +*.exe +*.lib + +# Patch leaves these lying arround +*.orig +*.rej + +# gcov stuff +*.gcno +*.gcov +*.gcda + +# gdb stuff +.gdb_history + +# Autotools stuff +.deps +.dirstamp +Makefile +Makefile.in + +# Libtool stuff +.libs +*.lo +*.la + +# ctags stuff +TAGS +tags + +# Stuff made by our makefiles +libevent.pc +libevent_pthreads.pc +libevent_openssl.pc +*.log +*.trs + +## The initial / makes these files only get ignored in particular directories. +/autom4te.cache + +# Libtool adds these, at least sometimes +/m4/libtool.m4 +/m4/ltoptions.m4 +/m4/ltsugar.m4 +/m4/ltversion.m4 +/m4/lt~obsolete.m4 + +/aclocal.m4 +/compile +/doxygen +/config.cache +/config.guess +/config.log +/config.status +/config.sub +/configure +/configure.lineno +/depcomp +/config.h +/config.h.in +/install-sh +/libtool +/ltmain.sh +/missing +/stamp-h1 +/stamp-h2 + +/include/event2/event-config.h +/evconfig-private.h + +/sample/dns-example +/sample/event-read-fifo +/sample/hello-world +/sample/http-server +/sample/http-connect +/sample/le-proxy +/sample/https-client +/sample/signal-test +/sample/time-test +/sample/event-test + +/test-driver +/test/bench +/test/bench_cascade +/test/bench_http +/test/bench_httpclient +/test/regress +/test/regress.gen.c +/test/regress.gen.h +/test/rpcgen-attempted +/test/test-dumpevents +/test/test-eof +/test/test-closed +/test/test-init +/test/test-ratelim +/test/test-script.sh +/test/test-time +/test/test-weof +/test/test-changelist +/test/test-fdleak + + +# Files generated by cmake +/CMakeCache.txt +/CMakeFiles/ +/CTestTestfile.cmake +/DartConfiguration.tcl +/LibeventConfig.cmake +/LibeventConfigVersion.cmake +/LibeventTargets.cmake +/bin/ +/cmake_install.cmake +/include/evconfig-private.h +/lib/ +/tmp/ +/verify_tests.sh +/event.dir +/event_core.dir +/event_extra.dir +*.vcxproj +*.sln +*.filters diff --git a/libs/libevent/docs/.travis.yml b/libs/libevent/docs/.travis.yml new file mode 100644 index 0000000000..f1d8141f5a --- /dev/null +++ b/libs/libevent/docs/.travis.yml @@ -0,0 +1,21 @@ +env: + matrix: + - EVENT_BUILD_METHOD=cmake EVENT_CMAKE_OPTIONS="" + - EVENT_BUILD_METHOD=cmake EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_OPENSSL=ON" + - EVENT_BUILD_METHOD=cmake EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_THREAD_SUPPORT=ON" + - EVENT_BUILD_METHOD=cmake EVENT_CMAKE_OPTIONS="-DEVENT__DISABLE_DEBUG_MODE=ON" + - EVENT_BUILD_METHOD=autotools EVENT_CONFIGURE_OPTIONS="" + - EVENT_BUILD_METHOD=autotools EVENT_CONFIGURE_OPTIONS="--disable-debug-mode" +language: c +compiler: + - gcc + - clang +install: + - sudo apt-get update -qq + - sudo apt-get install -y -qq zlib1g-dev libssl-dev build-essential automake autoconf cmake +script: + - if [ "$EVENT_BUILD_METHOD" = "autotools" ]; then ./autogen.sh && ./configure $EVENT_CONFIGURE_OPTIONS && make && make verify; fi + - if [ "$EVENT_BUILD_METHOD" = "cmake" ]; then mkdir build && cd build && cmake .. $EVENT_CMAKE_OPTIONS && cmake --build . && CTEST_OUTPUT_ON_FAILURE=1 cmake --build . --target verify; fi + +notifications: + irc: "irc.oftc.net#libevent" diff --git a/libs/libevent/docs/CMakeLists.txt b/libs/libevent/docs/CMakeLists.txt new file mode 100644 index 0000000000..708f8a1bfa --- /dev/null +++ b/libs/libevent/docs/CMakeLists.txt @@ -0,0 +1,1428 @@ +# +# Libevent CMake project +# +# Based on initial work by: +# Alexey Ozeritsky +# +# Additional changes: +# Brodie Thiesfield +# Joakim Soderberg +# Trond Norbye +# Sergei Nikulov +# +# Build example: +# +# cd libevent +# md build +# cd build +# cmake -G "Visual Studio 10" .. +# start libevent.sln +# +cmake_minimum_required(VERSION 2.8.0 FATAL_ERROR) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release + CACHE STRING "Set build type to Debug o Release (default Release)" FORCE) +endif() + +# get rid of the extra default configurations +# what? why would you get id of other useful build types? - Ellzey +set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Limited configurations" FORCE) + +project(libevent C) + +set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/") + + +include(CheckTypeSize) +include(CheckFunctionExistsEx) +include(CheckFileOffsetBits) +include(CheckFunctionExists) +include(CheckIncludeFile) +include(CheckIncludeFiles) +include(CheckVariableExists) +include(CheckSymbolExists) +include(CheckStructHasMember) +include(CheckCSourceCompiles) +include(CheckPrototypeDefinition) +include(CheckFunctionKeywords) +include(AddCompilerFlags) +include(VersionViaGit) + +event_fuzzy_version_from_git() + +set(EVENT_VERSION_MAJOR ${EVENT_GIT___VERSION_MAJOR}) +set(EVENT_VERSION_MINOR ${EVENT_GIT___VERSION_MINOR}) +set(EVENT_VERSION_PATCH ${EVENT_GIT___VERSION_PATCH}) +set(EVENT_VERSION_STAGE ${EVENT_GIT___VERSION_STAGE}) + + +set(EVENT_ABI_MAJOR ${EVENT_VERSION_MAJOR}) +set(EVENT_ABI_MINOR ${EVENT_VERSION_MINOR}) +set(EVENT_ABI_PATCH ${EVENT_VERSION_PATCH}) + +set(EVENT_ABI_LIBVERSION + "${EVENT_ABI_MAJOR}.${EVENT_ABI_MINOR}.${EVENT_ABI_PATCH}") + +set(EVENT_PACKAGE_VERSION + "${EVENT_VERSION_MAJOR}.${EVENT_VERSION_MINOR}.${EVENT_VERSION_PATCH}") + +set(EVENT_NUMERIC_VERSION 0x02010500) + +# only a subset of names can be used, defaults to "beta" +set(EVENT_STAGE_NAME ${EVENT_VERSION_STAGE} + CACHE STRING "set the stage name (beta|alpha|release)") + +# a list that defines what can set for EVENT_STAGE_VERSION +set(EVENT__ALLOWED_STAGE_NAMES + rc + beta + alpha + release) + +# attempt to find the EVENT__STAGE_VERSION in the allowed list +# of accepted stage names, the return value is stord in +# EVENT__STAGE_RET + +list(FIND EVENT__ALLOWED_STAGE_NAMES + ${EVENT_STAGE_NAME} + EVENT__STAGE_RET) + +if (EVENT__STAGE_RET EQUAL "-1") + set(EVENT_STAGE_NAME beta) +endif() + +set(EVENT_VERSION + "${EVENT_VERSION_MAJOR}.${EVENT_VERSION_MINOR}.${EVENT_VERSION_PATCH}-${EVENT_STAGE_NAME}") + +option(EVENT__BUILD_SHARED_LIBRARIES + "Define if libevent should be built with shared libraries instead of archives" OFF) + +option(EVENT__DISABLE_DEBUG_MODE + "Define if libevent should build without support for a debug mode" OFF) + +option(EVENT__ENABLE_VERBOSE_DEBUG + "Enables verbose debugging" OFF) + +option(EVENT__DISABLE_MM_REPLACEMENT + "Define if libevent should not allow replacing the mm functions" OFF) + +option(EVENT__DISABLE_THREAD_SUPPORT + "Define if libevent should not be compiled with thread support" OFF) + +option(EVENT__DISABLE_OPENSSL + "Define if libevent should build without support for OpenSSL encrpytion" OFF) + +option(EVENT__DISABLE_BENCHMARK + "Defines if libevent should build without the benchmark exectuables" OFF) + +option(EVENT__DISABLE_TESTS + "If tests should be compiled or not" OFF) + +option(EVENT__DISABLE_REGRESS + "Disable the regress tests" OFF) + +option(EVENT__DISABLE_SAMPLES + "Disable sample files" OFF) + +option(EVENT__FORCE_KQUEUE_CHECK + "When crosscompiling forces running a test program that verifies that Kqueue works with pipes. Note that this requires you to manually run the test program on the the cross compilation target to verify that it works. See cmake documentation for try_run for more details" OFF) + +# TODO: Add --disable-largefile omit support for large files +option(EVENT__COVERAGE +"Enable running gcov to get a test coverage report (only works with GCC/CLang). Make sure to enable -DCMAKE_BUILD_TYPE=Debug as well." OFF) + +# Put the libaries and binaries that get built into directories at the +# top of the build tree rather than in hard-to-find leaf directories. +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) + +if (EVENT__ENABLE_VERBOSE_DEBUG) + add_definitions(-DUSE_DBUG=1) +endif() + +# Setup compiler flags for coverage. +if (EVENT__COVERAGE) + if ((NOT CMAKE_COMPILER_IS_GNUCC) AND (NOT ${CMAKE_CXX_COMPILER_ID} STREQAL "Clang")) + message(FATAL_ERROR"Trying to compile coverage support, but compiler is not GNU gcc! Try CC=/usr/bin/gcc CXX=/usr/bin/g++ cmake <options> ..") + endif() + + if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(FATAL_ERROR "Coverage requires -DCMAKE_BUILD_TYPE=Debug") + endif() + + message(STATUS "Setting coverage compiler flags") + add_compiler_flags(-g -O0 -fprofile-arcs -ftest-coverage) +endif() + +# GCC specific options. +if (CMAKE_COMPILER_IS_GNUCC) + option(EVENT__DISABLE_GCC_WARNINGS "Disable verbose warnings with GCC" OFF) + option(EVENT__ENABLE_GCC_HARDENING "Enable compiler security checks" OFF) + option(EVENT__ENABLE_GCC_FUNCTION_SECTIONS "Enable gcc function sections" OFF) + option(EVENT__ENABLE_GCC_WARNINGS "Make all GCC warnings into errors" OFF) + + list(APPEND __FLAGS -Wall) + + if (EVENT__DISABLE_GCC_WARNINGS) + list(APPEND __FLAGS -w) + endif() + + if (EVENT__ENABLE_GCC_HARDENING) + list(APPEND __FLAGS + -fstack-protector-all + -fwrapv + -fPIE + -Wstack-protector + "--param ssp-buffer-size=1") + + add_definitions(-D_FORTIFY_SOURCE=2) + endif() + + if (EVENT__ENABLE_GCC_FUNCTION_SECTIONS) + list(APPEND __FLAGS -ffunction-sections) + # TODO: Add --gc-sections support. We need some checks for NetBSD to ensure this works. + endif() + + if (EVENT__ENABLE_GCC_WARNINGS) + list(APPEND __FLAGS -Werror) + endif() + + # We need to test for at least gcc 2.95 here, because older versions don't + # have -fno-strict-aliasing + list(APPEND __FLAGS -fno-strict-aliasing) + + add_compiler_flags(__FLAGS) +endif() + +if (APPLE) + # Get rid of deprecated warnings for OpenSSL on OSX 10.7 and greater. + add_compiler_flags( + -Wno-error=deprecated-declarations + -Qunused-arguments) +endif() + +# Winsock. +if(WIN32) + set(CMAKE_EXTRA_INCLUDE_FILES winsock2.h ws2tcpip.h) + set(CMAKE_REQUIRED_LIBRARIES ws2_32.lib) + set(CMAKE_REQUIRED_DEFINITIONS -FIwinsock2.h -FIws2tcpip.h) +endif() + +# Check if _GNU_SOURCE is available. +CHECK_SYMBOL_EXISTS(__GNU_LIBRARY__ "features.h" _GNU_SOURCE) + +if (_GNU_SOURCE) + add_definitions(-D_GNU_SOURCE) +endif() + +CHECK_INCLUDE_FILE(sys/types.h EVENT__HAVE_SYS_TYPES_H) +if(EVENT__HAVE_SYS_TYPES_H) + list(APPEND CMAKE_EXTRA_INCLUDE_FILES sys/types.h) +endif() + +CHECK_INCLUDE_FILE(sys/socket.h EVENT__HAVE_SYS_SOCKET_H) +if(EVENT__HAVE_SYS_SOCKET_H) + list(APPEND CMAKE_EXTRA_INCLUDE_FILES sys/socket.h) +endif() + +CHECK_INCLUDE_FILE(netinet/in.h EVENT__HAVE_NETINET_IN_H) +if(EVENT__HAVE_NETINET_IN_H) + list(APPEND CMAKE_EXTRA_INCLUDE_FILES netinet/in.h) +endif() + +CHECK_INCLUDE_FILE(netinet/in6.h EVENT__HAVE_NETINET_IN6_H) +if(EVENT__HAVE_NETINET_IN6_H) + list(APPEND CMAKE_EXTRA_INCLUDE_FILES netinet/in6.h) +endif() + +CHECK_INCLUDE_FILE(unistd.h EVENT__HAVE_UNISTD_H) +CHECK_INCLUDE_FILE(netdb.h EVENT__HAVE_NETDB_H) +CHECK_INCLUDE_FILE(dlfcn.h EVENT__HAVE_DLFCN_H) +CHECK_INCLUDE_FILE(arpa/inet.h EVENT__HAVE_ARPA_INET_H) +CHECK_INCLUDE_FILE(fcntl.h EVENT__HAVE_FCNTL_H) +if(EVENT__HAVE_FCNTL_H) + list(APPEND CMAKE_EXTRA_INCLUDE_FILES fcntl.h) +endif() +CHECK_INCLUDE_FILE(inttypes.h EVENT__HAVE_INTTYPES_H) +CHECK_INCLUDE_FILE(memory.h EVENT__HAVE_MEMORY_H) +CHECK_INCLUDE_FILE(poll.h EVENT__HAVE_POLL_H) +CHECK_INCLUDE_FILE(port.h EVENT__HAVE_PORT_H) +if(EVENT__HAVE_PORT_H) + list(APPEND CMAKE_EXTRA_INCLUDE_FILES port.h) +endif() +CHECK_INCLUDE_FILE(signal.h EVENT__HAVE_SIGNAL_H) +CHECK_INCLUDE_FILE(stdarg.h EVENT__HAVE_STDARG_H) +CHECK_INCLUDE_FILE(stddef.h EVENT__HAVE_STDDEF_H) +CHECK_INCLUDE_FILE(stdint.h EVENT__HAVE_STDINT_H) +CHECK_INCLUDE_FILE(stdlib.h EVENT__HAVE_STDLIB_H) +CHECK_INCLUDE_FILE(strings.h EVENT__HAVE_STRINGS_H) +CHECK_INCLUDE_FILE(string.h EVENT__HAVE_STRING_H) +CHECK_INCLUDE_FILE(sys/devpoll.h EVENT__HAVE_SYS_DEVPOLL_H) +CHECK_INCLUDE_FILE(sys/epoll.h EVENT__HAVE_SYS_EPOLL_H) +CHECK_INCLUDE_FILE(sys/eventfd.h EVENT__HAVE_SYS_EVENTFD_H) +CHECK_INCLUDE_FILE(sys/event.h EVENT__HAVE_SYS_EVENT_H) +CHECK_INCLUDE_FILE(sys/ioctl.h EVENT__HAVE_SYS_IOCTL_H) +CHECK_INCLUDE_FILE(sys/mman.h EVENT__HAVE_SYS_MMAN_H) +CHECK_INCLUDE_FILE(sys/param.h EVENT__HAVE_SYS_PARAM_H) +CHECK_INCLUDE_FILE(sys/queue.h EVENT__HAVE_SYS_QUEUE_H) +CHECK_INCLUDE_FILE(sys/select.h EVENT__HAVE_SYS_SELECT_H) +CHECK_INCLUDE_FILE(sys/sendfile.h EVENT__HAVE_SYS_SENDFILE_H) +CHECK_INCLUDE_FILE(sys/stat.h EVENT__HAVE_SYS_STAT_H) +CHECK_INCLUDE_FILE(sys/time.h EVENT__HAVE_SYS_TIME_H) +if(EVENT__HAVE_SYS_TIME_H) + list(APPEND CMAKE_EXTRA_INCLUDE_FILES sys/time.h) +endif() +CHECK_INCLUDE_FILE(sys/uio.h EVENT__HAVE_SYS_UIO_H) +CHECK_INCLUDE_FILES("sys/types.h;ifaddrs.h" EVENT__HAVE_IFADDRS_H) +CHECK_INCLUDE_FILE(mach/mach_time.h EVENT__HAVE_MACH_MACH_TIME_H) +CHECK_INCLUDE_FILE(netdb.h EVENT__HAVE_NETDB_H) +CHECK_INCLUDE_FILE(netinet/tcp.h EVENT__HAVE_NETINET_TCP_H) +CHECK_INCLUDE_FILE(sys/wait.h EVENT__HAVE_SYS_WAIT_H) +CHECK_INCLUDE_FILE(sys/resource.h EVENT__HAVE_SYS_RESOURCE_H) +CHECK_INCLUDE_FILE(sys/sysctl.h EVENT__HAVE_SYS_SYSCTL_H) +CHECK_INCLUDE_FILE(sys/timerfd.h EVENT__HAVE_SYS_TIMERFD_H) + + +CHECK_FUNCTION_EXISTS_EX(epoll_create EVENT__HAVE_EPOLL) +CHECK_FUNCTION_EXISTS_EX(epoll_ctl EVENT__HAVE_EPOLL_CTL) +CHECK_FUNCTION_EXISTS_EX(eventfd EVENT__HAVE_EVENTFD) +CHECK_FUNCTION_EXISTS_EX(clock_gettime EVENT__HAVE_CLOCK_GETTIME) +CHECK_FUNCTION_EXISTS_EX(fcntl EVENT__HAVE_FCNTL) +CHECK_FUNCTION_EXISTS_EX(getaddrinfo EVENT__HAVE_GETADDRINFO) +CHECK_FUNCTION_EXISTS_EX(getnameinfo EVENT__HAVE_GETNAMEINFO) +CHECK_FUNCTION_EXISTS_EX(gettimeofday EVENT__HAVE_GETTIMEOFDAY) +CHECK_FUNCTION_EXISTS_EX(getprotobynumber EVENT__HAVE_GETPROTOBYNUMBER) +CHECK_FUNCTION_EXISTS_EX(getservbyname EVENT__HAVE_GETSERVBYNAME) +CHECK_FUNCTION_EXISTS_EX(inet_ntop EVENT__HAVE_INET_NTOP) +CHECK_FUNCTION_EXISTS_EX(inet_pton EVENT__HAVE_INET_PTON) +CHECK_FUNCTION_EXISTS_EX(kqueue EVENT__HAVE_KQUEUE) +CHECK_FUNCTION_EXISTS_EX(mmap EVENT__HAVE_MMAP) +CHECK_FUNCTION_EXISTS_EX(pipe EVENT__HAVE_PIPE) +CHECK_FUNCTION_EXISTS_EX(pipe2 EVENT__HAVE_PIPE2) +CHECK_FUNCTION_EXISTS_EX(poll EVENT__HAVE_POLL) +CHECK_FUNCTION_EXISTS_EX(port_create EVENT__HAVE_PORT_CREATE) +CHECK_FUNCTION_EXISTS_EX(sendfile EVENT__HAVE_SENDFILE) +CHECK_FUNCTION_EXISTS_EX(sigaction EVENT__HAVE_SIGACTION) +CHECK_FUNCTION_EXISTS_EX(signal EVENT__HAVE_SIGNAL) +CHECK_FUNCTION_EXISTS_EX(splice EVENT__HAVE_SPLICE) +CHECK_FUNCTION_EXISTS_EX(strlcpy EVENT__HAVE_STRLCPY) +CHECK_FUNCTION_EXISTS_EX(strsep EVENT__HAVE_STRSEP) +CHECK_FUNCTION_EXISTS_EX(strtok_r EVENT__HAVE_STRTOK_R) +CHECK_FUNCTION_EXISTS_EX(strtoll EVENT__HAVE_STRTOLL) +CHECK_FUNCTION_EXISTS_EX(vasprintf EVENT__HAVE_VASPRINTF) +CHECK_FUNCTION_EXISTS_EX(sysctl EVENT__HAVE_SYSCTL) +CHECK_FUNCTION_EXISTS_EX(accept4 EVENT__HAVE_ACCEPT4) +CHECK_FUNCTION_EXISTS_EX(arc4random EVENT__HAVE_ARC4RANDOM) +CHECK_FUNCTION_EXISTS_EX(arc4random_buf EVENT__HAVE_ARC4RANDOM_BUF) +CHECK_FUNCTION_EXISTS_EX(epoll_create1 EVENT__HAVE_EPOLL_CREATE1) +CHECK_FUNCTION_EXISTS_EX(getegid EVENT__HAVE_GETEGID) +CHECK_FUNCTION_EXISTS_EX(geteuid EVENT__HAVE_GETEUID) +CHECK_FUNCTION_EXISTS_EX(getifaddrs EVENT__HAVE_GETIFADDRS) +CHECK_FUNCTION_EXISTS_EX(issetugid EVENT__HAVE_ISSETUGID) +CHECK_FUNCTION_EXISTS_EX(mach_absolute_time EVENT__HAVE_MACH_ABSOLUTE_TIME) +CHECK_FUNCTION_EXISTS_EX(nanosleep EVENT__HAVE_NANOSLEEP) +CHECK_FUNCTION_EXISTS_EX(usleep EVENT__HAVE_USLEEP) +CHECK_FUNCTION_EXISTS_EX(timeradd EVENT__HAVE_TIMERADD) +CHECK_FUNCTION_EXISTS_EX(timerclear EVENT__HAVE_TIMERCLEAR) +CHECK_FUNCTION_EXISTS_EX(timercmp EVENT__HAVE_TIMERCMP) +CHECK_FUNCTION_EXISTS_EX(timerfd_create HAVE_TIMERFD_CREATE) +CHECK_FUNCTION_EXISTS_EX(timerisset EVENT__HAVE_TIMERISSET) +CHECK_FUNCTION_EXISTS_EX(putenv EVENT__HAVE_PUTENV) +CHECK_FUNCTION_EXISTS_EX(setenv EVENT__HAVE_SETENV) +CHECK_FUNCTION_EXISTS_EX(setrlimit EVENT__HAVE_SETRLIMIT) +CHECK_FUNCTION_EXISTS_EX(umask EVENT__HAVE_UMASK) +CHECK_FUNCTION_EXISTS_EX(unsetenv EVENT__HAVE_UNSETENV) + +# Get the gethostbyname_r prototype. +CHECK_FUNCTION_EXISTS_EX(gethostbyname_r EVENT__HAVE_GETHOSTBYNAME_R) + +if(EVENT__HAVE_GETHOSTBYNAME_R) + CHECK_PROTOTYPE_DEFINITION(gethostbyname_r + "int gethostbyname_r(const char *name, struct hostent *hp, struct hostent_data *hdata)" + "0" + "netdb.h" + EVENT__HAVE_GETHOSTBYNAME_R_3_ARG) + + CHECK_PROTOTYPE_DEFINITION(gethostbyname_r + "struct hostent *gethostbyname_r(const char *name, struct hostent *hp, char *buf, size_t buflen, int *herr)" + "NULL" + "netdb.h" + EVENT__HAVE_GETHOSTBYNAME_R_5_ARG) + + CHECK_PROTOTYPE_DEFINITION(gethostbyname_r + "int gethostbyname_r(const char *name, struct hostent *hp, char *buf, size_t buflen, struct hostent **result, int *herr)" + "0" + "netdb.h" + EVENT__HAVE_GETHOSTBYNAME_R_6_ARG) +endif() + +if(HAVE_PORT_H AND HAVE_PORT_CREATE) + set(EVENT__HAVE_EVENT_PORTS 1) +endif() + +if(NOT WIN32) + CHECK_FUNCTION_EXISTS_EX(select EVENT__HAVE_SELECT) +endif() + +CHECK_TYPE_SIZE("uint8_t" EVENT__HAVE_UINT8_T) +CHECK_TYPE_SIZE("uint16_t" EVENT__HAVE_UINT16_T) +CHECK_TYPE_SIZE("uint32_t" EVENT__HAVE_UINT32_T) +CHECK_TYPE_SIZE("uint64_t" EVENT__HAVE_UINT64_T) +CHECK_TYPE_SIZE("short" EVENT__SIZEOF_SHORT BUILTIN_TYPES_ONLY) +CHECK_TYPE_SIZE("int" EVENT__SIZEOF_INT BUILTIN_TYPES_ONLY) +CHECK_TYPE_SIZE("unsigned" EVENT__SIZEOF_UNSIGNED BUILTIN_TYPES_ONLY) +CHECK_TYPE_SIZE("unsigned int" EVENT__SIZEOF_UNSIGNED_INT BUILTIN_TYPES_ONLY) +CHECK_TYPE_SIZE("long" EVENT__SIZEOF_LONG BUILTIN_TYPES_ONLY) +CHECK_TYPE_SIZE("long long" EVENT__SIZEOF_LONG_LONG BUILTIN_TYPES_ONLY) + +if(WIN32) + # These aren't available until Windows Vista. + # But you can still link them. They just won't be found when running the exe. + set(EVENT__HAVE_INET_NTOP 0) + set(EVENT__HAVE_INET_PTON 0) +endif() + +# Check for different inline keyword versions. +check_function_keywords("inline" "__inline" "__inline__") + +if (HAVE_INLINE) + set (EVENT__inline inline) +elseif (HAVE___INLINE) + set(EVENT__inline __inline) +elseif(HAVE___INLINE__) + set(EVENT__inline __inline__) +else() + set(EVENT__inline) +endif() + +CHECK_SYMBOL_EXISTS(TAILQ_FOREACH sys/queue.h EVENT__HAVE_TAILQFOREACH) +CHECK_SYMBOL_EXISTS(CTL_KERN sys/sysctl.h EVENT__HAVE_DECL_CTL_KERN) +CHECK_SYMBOL_EXISTS(KERN_ARND sys/sysctl.h EVENT__HAVE_DECL_KERN_ARND) +CHECK_SYMBOL_EXISTS(KERN_RANDOM sys/sysctl.h EVENT__HAVE_DECL_KERN_RANDOM) +CHECK_SYMBOL_EXISTS(RANDOM_UUID sys/sysctl.h EVENT__HAVE_DECL_RANDOM_UUID) +CHECK_SYMBOL_EXISTS(F_SETFD fcntl.h EVENT__HAVE_SETFD) + +CHECK_TYPE_SIZE(fd_mask EVENT__HAVE_FD_MASK) + +CHECK_TYPE_SIZE(size_t EVENT__SIZEOF_SIZEE_T) +if(NOT EVENT__SIZEOF_SIZE_T) + set(EVENT__size_t "unsigned") + set(EVENT__SIZEOF_SIZE_T ${EVENT__SIZEOF_UNSIGNED}) +else() + set(EVENT__size_t size_t) +endif() + +CHECK_TYPE_SIZE("off_t" EVENT__SIZEOF_OFF_T LANGUAGE C) + + +# XXX we should functionalize these size and type sets. --elley + +# Winssck. +if (_MSC_VER) + list(APPEND CMAKE_EXTRA_INCLUDE_FILES BaseTsd.h) +endif() +CHECK_TYPE_SIZE("ssize_t" EVENT__SIZEOF_SSIZE_T_LOWER LANGUAGE C) +CHECK_TYPE_SIZE("SSIZE_T" EVENT__SIZEOF_SSIZE_T_UPPER LANGUAGE C) + +if (EVENT__SIZEOF_SSIZE_T_LOWER) + set(EVENT__ssize_t "ssize_t") +elseif (EVENT__SIZEOF_SSIZE_T_UPPER) + set(EVENT__ssize_t "SSIZE_T") +else() + set(EVENT__ssize_t "int") + set(EVENT__SIZEOF_SSIZE_T ${EVENT__SIZEOF_INT}) +endif() + + +CHECK_TYPE_SIZE(socklen_t EVENT__SIZEOF_SOCKLEN_T) +if(NOT EVENT__SIZEOF_SOCKLEN_T) + set(EVENT__socklen_t "unsigned int") + set(EVENT__SIZEOF_SOCKLEN_T ${EVENT__SIZEOF_UNSIGNED_INT}) +else() + set(EVENT__socklen_t "socklen_t") +endif() + +CHECK_TYPE_SIZE(pid_t EVENT__SIZEOF_PID_T) +if(NOT EVENT__SIZEOF_PID_T) + set(EVENT__pid_t "int") + set(EVENT__SIZEOF_PID_T ${EVENT__SIZEOF_INT}) +else() + set(EVENT__pid_t "pid_t") + set(EVENT__SIZEOF_PID_T EVENT__SIZEOF_PID_T) +endif() + +if (NOT EVENT__DISABLE_THREAD_SUPPORT) + CHECK_TYPE_SIZE(pthread_t EVENT__SIZEOF_PTHREAD_T) +endif() + +if(EVENT__HAVE_CLOCK_GETTIME) + set(EVENT__DNS_USE_CPU_CLOCK_FOR_ID 1) +endif() + +# we're just getting lazy now. +CHECK_TYPE_SIZE("uintptr_t" EVENT__HAVE_UINTPTR_T) +CHECK_TYPE_SIZE("void *" EVENT__SIZEOF_VOID_P) + +# Tests file offset bits. +# TODO: Add AIX test for if -D_LARGE_FILES is needed. + +# XXX: Why is this here? we don't even use it. Well, we don't even use it +# on top of that, why is it set in the config.h?! IT_MAKES_NO_SENSE +# I'm commenting it out for now. +# - ellzey + +#CHECK_FILE_OFFSET_BITS() +#set(EVENT___FILE_OFFSET_BITS _FILE_OFFSET_BITS) + +# Verify kqueue works with pipes. +if (EVENT__HAVE_KQUEUE) + if (CMAKE_CROSSCOMPILING AND NOT EVENT__FORCE_KQUEUE_CHECK) + message(WARNING "Cannot check if kqueue works with pipes when crosscompiling, use EVENT__FORCE_KQUEUE_CHECK to be sure (this requires manually running a test program on the cross compilation target)") + set(EVENT__HAVE_WORKING_KQUEUE 1) + else() + message(STATUS "Checking if kqueue works with pipes...") + include(CheckWorkingKqueue) + endif() +endif() + +CHECK_SYMBOL_EXISTS(_MINIX "stdio.h" EVENT___MINIX) +CHECK_SYMBOL_EXISTS(_POSIX_1_SOURCE "stdio.h" EVENT___POSIX_1_SOURCE) +CHECK_SYMBOL_EXISTS(_POSIX_SOURCE "stdio.h" EVENT___POSIX_SOURCE) + +if(EVENT__HAVE_NETDB_H) + list(APPEND CMAKE_EXTRA_INCLUDE_FILES netdb.h) + CHECK_TYPE_SIZE("struct addrinfo" EVENT__HAVE_STRUCT_ADDRINFO) +elseif(WIN32) + list(APPEND CMAKE_EXTRA_INCLUDE_FILES ws2tcpip.h) + CHECK_TYPE_SIZE("struct addrinfo" EVENT__HAVE_STRUCT_ADDRINFO) +endif() + +# Check for sockaddr structure sizes. +set(SOCKADDR_HEADERS) +if (WIN32) + set(CMAKE_REQUIRED_DEFINITIONS "-DWIN32_LEAN_AND_MEAN") + if (_MSC_VER LESS 1300) + set(SOCKADDR_HEADERS winsock.h) + else() + set(SOCKADDR_HEADERS winsock2.h ws2tcpip.h) + endif() +else() + if (EVENT__HAVE_NETINET_IN_H) + set(SOCKADDR_HEADERS ${SOCKADDR_HEADERS} netinet/in.h) + endif() + + if (EVENT__HAVE_NETINET_IN6_H) + set(SOCKADDR_HEADERS ${SOCKADDR_HEADERS} netinet/in6.h) + endif() + + if (EVENT__HAVE_SYS_SOCKET_H) + set(SOCKADDR_HEADERS ${SOCKADDR_HEADERS} sys/socket.h) + endif() + + if (EVENT__HAVE_NETDB_H) + set(SOCKADDR_HEADERS ${SOCKADDR_HEADERS} netdb.h) + endif() +endif() + +CHECK_TYPE_SIZE("struct in6_addr" EVENT__HAVE_STRUCT_IN6_ADDR) +if(EVENT__HAVE_STRUCT_IN6_ADDR) + CHECK_STRUCT_HAS_MEMBER("struct in6_addr" + s6_addr16 "${SOCKADDR_HEADERS}" + EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR16) + + CHECK_STRUCT_HAS_MEMBER("struct in6_addr" + s6_addr32 "${SOCKADDR_HEADERS}" + EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR32) +endif() + +CHECK_TYPE_SIZE("sa_family_t" EVENT__HAVE_SA_FAMILY_T) +CHECK_TYPE_SIZE("struct sockaddr_in6" EVENT__HAVE_STRUCT_SOCKADDR_IN6) + +if(EVENT__HAVE_STRUCT_SOCKADDR_IN6) + CHECK_STRUCT_HAS_MEMBER("struct sockaddr_in6" + sin6_len "${SOCKADDR_HEADERS}" + EVENT__HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN) + + CHECK_STRUCT_HAS_MEMBER("struct sockaddr_in6" + sin_len "${SOCKADDR_HEADERS}" + EVENT__HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) +endif() + +CHECK_TYPE_SIZE("struct sockaddr_storage" EVENT__HAVE_STRUCT_SOCKADDR_STORAGE) +if(EVENT__HAVE_STRUCT_SOCKADDR_STORAGE) + CHECK_STRUCT_HAS_MEMBER("struct sockaddr_storage" + ss_family "${SOCKADDR_HEADERS}" + EVENT__HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY) + + CHECK_STRUCT_HAS_MEMBER("struct sockaddr_storage" + __ss_family "${SOCKADDR_HEADERS}" EVENT__HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY) +endif() + +# Group the source files. +set(HDR_PRIVATE + bufferevent-internal.h + changelist-internal.h + defer-internal.h + epolltable-internal.h + evbuffer-internal.h + event-internal.h + evmap-internal.h + evrpc-internal.h + evsignal-internal.h + evthread-internal.h + ht-internal.h + http-internal.h + iocp-internal.h + ipv6-internal.h + log-internal.h + minheap-internal.h + mm-internal.h + ratelim-internal.h + strlcpy-internal.h + util-internal.h + evconfig-private.h + compat/sys/queue.h) + +set(HDR_COMPAT + include/evdns.h + include/evrpc.h + include/event.h + include/evhttp.h + include/evutil.h) + +set(HDR_PUBLIC + include/event2/buffer.h + include/event2/bufferevent.h + include/event2/bufferevent_compat.h + include/event2/bufferevent_struct.h + include/event2/buffer_compat.h + include/event2/dns.h + include/event2/dns_compat.h + include/event2/dns_struct.h + include/event2/event.h + include/event2/event_compat.h + include/event2/event_struct.h + include/event2/http.h + include/event2/http_compat.h + include/event2/http_struct.h + include/event2/keyvalq_struct.h + include/event2/listener.h + include/event2/rpc.h + include/event2/rpc_compat.h + include/event2/rpc_struct.h + include/event2/tag.h + include/event2/tag_compat.h + include/event2/thread.h + include/event2/util.h + include/event2/visibility.h + ${PROJECT_BINARY_DIR}/include/event2/event-config.h) + +set(SRC_CORE + buffer.c + bufferevent.c + bufferevent_filter.c + bufferevent_pair.c + bufferevent_ratelim.c + bufferevent_sock.c + event.c + evmap.c + evthread.c + evutil.c + evutil_rand.c + evutil_time.c + listener.c + log.c + signal.c + strlcpy.c) + +if(EVENT__HAVE_SELECT) + list(APPEND SRC_CORE select.c) +endif() + +if(EVENT__HAVE_POLL) + list(APPEND SRC_CORE poll.c) +endif() + +if(EVENT__HAVE_KQUEUE) + list(APPEND SRC_CORE kqueue.c) +endif() + +if(EVENT__HAVE_DEVPOLL) + list(APPEND SRC_CORE devpoll.c) +endif() + +if(EVENT__HAVE_EPOLL) + list(APPEND SRC_CORE epoll_sub.c epoll.c) +endif() + +if(EVENT__HAVE_EVENT_PORTS) + list(APPEND SRC_CORE evport.c) +endif() + +if (NOT EVENT__DISABLE_OPENSSL) + find_package(OpenSSL REQUIRED) + + set(EVENT__HAVE_OPENSSL 1) + + message(STATUS "OpenSSL include: ${OPENSSL_INCLUDE_DIR}") + message(STATUS "OpenSSL lib: ${OPENSSL_LIBRARIES}") + + include_directories(${OPENSSL_INCLUDE_DIR}) + + list(APPEND SRC_CORE bufferevent_openssl.c) + list(APPEND HDR_PUBLIC include/event2/bufferevent_ssl.h) + list(APPEND LIB_APPS ${OPENSSL_LIBRARIES}) +endif() + +if (NOT EVENT__DISABLE_THREAD_SUPPORT) + if (WIN32) + list(APPEND SRC_CORE evthread_win32.c) + else() + find_package(Threads REQUIRED) + if (NOT CMAKE_USE_PTHREADS_INIT) + message(FATAL_ERROR + "Failed to find Pthreads, set EVENT__DISABLE_THREAD_SUPPORT to disable") + endif() + + set(EVENT__HAVE_PTHREADS 1) + list(APPEND SRC_CORE evthread_pthread.c) + list(APPEND LIB_APPS ${CMAKE_THREAD_LIBS_INIT}) + endif() +endif() + +if (NOT EVENT__DISABLE_TESTS) + # Zlib is only used for testing. + find_package(ZLIB) + + if (ZLIB_LIBRARY) + include_directories(${ZLIB_INCLUDE_DIRS}) + + set(EVENT__HAVE_ZLIB 1) + set(EVENT__HAVE_ZLIB_H) + list(APPEND LIB_APPS ${ZLIB_LIBRARIES}) + endif() +endif() + +set(SRC_EXTRA + event_tagging.c + http.c + evdns.c + evrpc.c) + +add_definitions(-DHAVE_CONFIG_H) + +# We use BEFORE here so we don't accidentally look in system directories +# first for some previous versions of the headers that are installed. +include_directories(BEFORE ${PROJECT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/compat + ${PROJECT_SOURCE_DIR}/include) + +if(WIN32) + list(APPEND SRC_CORE + buffer_iocp.c + bufferevent_async.c + event_iocp.c + evthread_win32.c + win32select.c) + + list(APPEND HDR_PRIVATE WIN32-Code/getopt.h) + + set(EVENT__DNS_USE_FTIME_FOR_ID 1) + set(LIB_PLATFORM ws2_32) + add_definitions( + -D_CRT_SECURE_NO_WARNINGS + -D_CRT_NONSTDC_NO_DEPRECATE) + + include_directories(./WIN32-Code) +endif() + +if (UNIX) + list(APPEND LIB_PLATFORM m) +endif() + +source_group("Headers Private" FILES ${HDR_PRIVATE}) +source_group("Header Compat" FILES ${HDR_COMPAT}) +source_group("Headers Public" FILES ${HDR_PUBLIC}) +source_group("Source Core" FILES ${SRC_CORE}) +source_group("Source Extra" FILES ${SRC_EXTRA}) + +# Generate the configure headers. +# (Place them in the build dir so we don't polute the source tree with generated files). +include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/include) + +if (EVENT__BUILD_SHARED_LIBRARIES) + set(EVENT__LIBRARY_TYPE SHARED) + + if ((CMAKE_COMPILER_IS_GNUCC) OR (${CMAKE_C_COMPILER_ID} STREQUAL "Clang")) + add_compiler_flags(-fvisibility=hidden) + elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "SunPro") + add_compiler_flags(-xldscope=hidden) + endif() + + set(EVENT__NEED_DLLIMPORT 1) +else (EVENT__BUILD_SHARED_LIBRARIES) + set(EVENT__LIBRARY_TYPE STATIC) +endif (EVENT__BUILD_SHARED_LIBRARIES) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/event-config.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/include/event2/event-config.h + NEWLINE_STYLE UNIX) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/evconfig-private.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/include/evconfig-private.h) + +# +# Create the libraries. +# + +# TODO: Add dynamic versions of the libraries as well. +add_library(event_core ${EVENT__LIBRARY_TYPE} + ${HDR_PRIVATE} + ${HDR_COMPAT} + ${HDR_PUBLIC} + ${SRC_CORE}) + +add_library(event_extra ${EVENT__LIBRARY_TYPE} + ${HDR_PRIVATE} + ${HDR_COMPAT} + ${HDR_PUBLIC} + ${SRC_CORE} + ${SRC_EXTRA}) + +# library exists for historical reasons; it contains the contents of +# both libevent_core and libevent_extra. You shouldn’t use it; it may +# go away in a future version of Libevent. +add_library(event ${EVENT__LIBRARY_TYPE} + ${HDR_PRIVATE} + ${HDR_COMPAT} + ${HDR_PUBLIC} + ${SRC_CORE} + ${SRC_EXTRA}) + +if (EVENT__BUILD_SHARED_LIBRARIES) + # Prepare static library to be linked to tests that need hidden symbols + add_library(event_extra_static STATIC + ${HDR_PRIVATE} + ${HDR_COMPAT} + ${HDR_PUBLIC} + ${SRC_CORE} + ${SRC_EXTRA}) + + set(EVENT_EXTRA_FOR_TEST event_extra_static) + + target_link_libraries(event_core ${OPENSSL_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${LIB_PLATFORM}) + + target_link_libraries(event ${OPENSSL_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${LIB_PLATFORM}) + + target_link_libraries(event_extra ${OPENSSL_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${LIB_PLATFORM}) + + set_target_properties(event + PROPERTIES SOVERSION + ${EVENT_ABI_LIBVERSION}) + + set_target_properties(event_core + PROPERTIES SOVERSION + ${EVENT_ABI_LIBVERSION}) + + set_target_properties(event_extra + PROPERTIES SOVERSION + ${EVENT_ABI_LIBVERSION}) + +else (EVENT__BUILD_SHARED_LIBRARIES) + set(EVENT_EXTRA_FOR_TEST event_extra) +endif (EVENT__BUILD_SHARED_LIBRARIES) + +# +# Samples. +# + +if (NOT EVENT__DISABLE_SAMPLES) + set(SAMPLES + dns-example + event-read-fifo + hello-world + signal-test + http-server + http-connect + time-test) + + if (NOT EVENT__DISABLE_OPENSSL AND OPENSSL_LIBRARIES) + set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) + CHECK_FUNCTION_EXISTS_EX(ERR_remove_thread_state EVENT__HAVE_ERR_REMOVE_THREAD_STATE) + set(CMAKE_REQUIRED_LIBRARIES "") + + # Special sample with more than one file. + add_executable(https-client + sample/https-client.c + sample/openssl_hostname_validation.c + sample/hostcheck.c) + + target_link_libraries(https-client + event_extra + ${LIB_APPS} + ${LIB_PLATFORM}) + + add_dependencies(https-client event_extra) + + # Requires OpenSSL. + list(APPEND SAMPLES le-proxy) + endif() + + foreach(SAMPLE ${SAMPLES}) + add_executable(${SAMPLE} + sample/${SAMPLE}.c) + + target_link_libraries(${SAMPLE} + event_extra + ${LIB_APPS} + ${LIB_PLATFORM}) + + add_dependencies(${SAMPLE} event_extra) + endforeach() + + if (WIN32) + target_sources(dns-example PUBLIC + WIN32-Code/getopt.c + WIN32-Code/getopt_long.c) + endif() +endif() + +if (NOT EVENT__DISABLE_BENCHMARK) + foreach (BENCHMARK bench bench_cascade bench_http bench_httpclient) + set(BENCH_SRC test/${BENCHMARK}.c) + + if (WIN32) + list(APPEND BENCH_SRC + WIN32-Code/getopt.c + WIN32-Code/getopt_long.c) + endif() + + add_executable(${BENCHMARK} ${BENCH_SRC}) + + target_link_libraries(${BENCHMARK} + event_extra + ${LIB_PLATFORM}) + + add_dependencies(${BENCHMARK} event_extra) + endforeach() +endif() + +if (NOT EVENT__DISABLE_TESTS) + # + # Generate Regress tests. + # + if (NOT EVENT__DISABLE_REGRESS) + + # (We require python to generate the regress tests) + find_package(PythonInterp) + + if (PYTHONINTERP_FOUND AND PYTHON_VERSION_STRING VERSION_LESS "3.0.0") + set(__FOUND_USABLE_PYTHON 1) + endif() + + if (__FOUND_USABLE_PYTHON) + message(STATUS "Generating regress tests...") + + add_definitions(-DTINYTEST_LOCAL) + + add_custom_command( + OUTPUT + ${CMAKE_CURRENT_SOURCE_DIR}/test/regress.gen.c + ${CMAKE_CURRENT_SOURCE_DIR}/test/regress.gen.h + DEPENDS + event_rpcgen.py + test/regress.rpc + COMMAND ${PYTHON_EXECUTABLE} ../event_rpcgen.py regress.rpc + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test) + + list(APPEND SRC_REGRESS + test/regress.c + test/regress.gen.c + test/regress.gen.h + test/regress_buffer.c + test/regress_bufferevent.c + test/regress_dns.c + test/regress_et.c + test/regress_finalize.c + test/regress_http.c + test/regress_listener.c + test/regress_main.c + test/regress_minheap.c + test/regress_rpc.c + test/regress_testutils.c + test/regress_testutils.h + test/regress_util.c + test/tinytest.c + ${SRC_CORE} + ${SRC_EXTRA}) + + if (WIN32) + list(APPEND SRC_REGRESS test/regress_iocp.c) + list(APPEND SRC_REGRESS test/regress_thread.c) + endif() + + if (CMAKE_USE_PTHREADS_INIT) + list(APPEND SRC_REGRESS test/regress_thread.c) + endif() + + if (ZLIB_LIBRARY) + list(APPEND SRC_REGRESS test/regress_zlib.c) + endif() + + if (OPENSSL_LIBRARIES) + list(APPEND SRC_REGRESS test/regress_ssl.c) + endif() + + add_executable(regress ${SRC_REGRESS}) + + # While building the test suite we don't want the visibility + # header trying to "dllimport" the symbols on windows (it + # generates a ton of warnings due to different link + # attributes for all of the symbols) + SET_TARGET_PROPERTIES(regress + PROPERTIES COMPILE_DEFINITIONS + "EVENT_BUILDING_REGRESS_TEST=1") + + target_link_libraries(regress + ${LIB_APPS} + ${LIB_PLATFORM}) + else() + message(WARNING "No suitable Python interpreter found, cannot generate regress tests!") + endif() + endif() + + # + # Test programs. + # + # all of these, including the cmakelists.txt should be moved + # into the dirctory 'tests' first. + # + # doing this, we can remove all the DISABLE_TESTS stuff, and simply + # do something like: + # + # add_custom_targets(tests) + # add_executable(... EXCLUDE_FROM_ALL ...c) + # add_dependencis(tests testa testb testc) + # add_test(....) + # + # then you can just run 'make tests' instead of them all + # auto-compile|running + # - ellzey + set(TESTPROGS test-changelist + test-eof + test-fdleak + test-init + test-time + test-weof) + + set(ALL_TESTPROGS + ${TESTPROGS} + test-dumpevents + test-ratelim) + + # Create test program executables. + foreach (TESTPROG ${ALL_TESTPROGS}) + add_executable(${TESTPROG} + test/${TESTPROG}.c) + + target_link_libraries(${TESTPROG} + ${EVENT_EXTRA_FOR_TEST} + ${LIB_PLATFORM}) + + add_dependencies(${TESTPROG} + ${EVENT_EXTRA_FOR_TEST}) + endforeach() + + # + # We run all tests with the different backends turned on one at a time. + # + + # Add event backends based on system introspection result. + set(BACKENDS "") + + if (EVENT__HAVE_EPOLL) + list(APPEND BACKENDS EPOLL) + endif() + + if (EVENT__HAVE_SELECT) + list(APPEND BACKENDS SELECT) + endif() + + if (EVENT__HAVE_POLL) + list(APPEND BACKENDS POLL) + endif() + + if (EVENT__HAVE_KQUEUE) + list(APPEND BACKENDS KQUEUE) + endif() + + if (EVENT__HAVE_EVENT_PORTS) + list(APPEND BACKENDS EVPORT) + endif() + + if (EVENT__HAVE_DEVPOLL) + list(APPEND BACKENDS DEVPOLL) + endif() + + if (WIN32) + list(APPEND BACKENDS WIN32) + endif() + + + # Default environment variables turns off all event systems, + # then we enable each one, one at a time when creating the tests. + set(DEFAULT_TEST_ENV_VARS "EVENT_SHOW_METHOD=1;") + foreach(BACKEND ${BACKENDS}) + set(BACKEND_ENV_VAR "EVENT_NO${BACKEND}=1") + list(APPEND DEFAULT_TEST_ENV_VARS "${BACKEND_ENV_VAR}") + endforeach() + + # Macro that creates the ctest test for a backend. + macro(add_backend_test BACKEND_TEST_NAME ENV_VARS) + set(TEST_NAMES "") + + foreach (TESTPROG ${TESTPROGS}) + set(TEST_NAME ${TESTPROG}__${BACKEND_TEST_NAME}) + + add_test(${TEST_NAME} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTPROG}) + + list(APPEND TEST_NAMES ${TEST_NAME}) + + set_tests_properties(${TEST_NAME} + PROPERTIES ENVIRONMENT "${ENV_VARS}") + endforeach() + + # Dump events test. + if (__FOUND_USABLE_PYTHON) + set(TEST_NAME test-dumpevents__${BACKEND_TEST_NAME}) + + add_test(${TEST_NAME} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-dumpevents | + ${PYTHON_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/test/check-dumpevents.py) + + set_tests_properties(${TEST_NAME} + PROPERTIES ENVIRONMENT "${ENV_VARS}") + else() + message(WARNING "test-dumpevents will be run without output check since python was not found!") + set(TEST_NAME test-dumpevents__${BACKEND_TEST_NAME}_no_check) + + add_test(${TEST_NAME} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-dumpevents) + + set_tests_properties(${TEST_NAME} + PROPERTIES ENVIRONMENT "${ENV_VARS}") + endif() + + # Regress tests. + if (NOT EVENT__DISABLE_REGRESS AND __FOUND_USABLE_PYTHON) + set(TEST_NAME regress__${BACKEND_TEST_NAME}) + + add_test(${TEST_NAME} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/regress) + + set_tests_properties(${TEST_NAME} + PROPERTIES ENVIRONMENT "${ENV_VARS}") + + add_test(${TEST_NAME}_debug + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/regress) + + set_tests_properties(${TEST_NAME}_debug + PROPERTIES ENVIRONMENT "${ENV_VARS};EVENT_DEBUG_MODE=1") + endif() + endmacro() + + # Add the tests for each backend. + foreach(BACKEND ${BACKENDS}) + # Enable this backend only. + set(BACKEND_ENV_VARS ${DEFAULT_TEST_ENV_VARS}) + list(REMOVE_ITEM BACKEND_ENV_VARS EVENT_NO${BACKEND}=1) + + # Epoll has some extra settings. + if (${BACKEND} STREQUAL "EPOLL") + add_backend_test(timerfd_${BACKEND} + "${BACKEND_ENV_VARS};EVENT_PRECISE_TIMER=1") + + add_backend_test(changelist_${BACKEND} + "${BACKEND_ENV_VARS};EVENT_EPOLL_USE_CHANGELIST=yes") + + add_backend_test(timerfd_changelist_${BACKEND} + "${BACKEND_ENV_VARS};EVENT_EPOLL_USE_CHANGELIST=yes;EVENT_PRECISE_TIMER=1") + else() + add_backend_test(${BACKEND} "${BACKEND_ENV_VARS}") + endif() + endforeach() + + # + # Rate limiter tests. + # + + # Group limits, no connection limit. + set(RL_BIN ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-ratelim) + + add_test(test-ratelim__group_lim + ${RL_BIN} + -g 30000 + -n 30 + -t 100 + --check-grouplimit 1000 + --check-stddev 100) + + # Connection limit, no group limit. + add_test(test-ratelim__con_lim + ${RL_BIN} + -c 1000 + -n 30 + -t 100 + --check-connlimit 50 + --check-stddev 50) + + # Connection limit and group limit. + add_test(test-ratelim__group_con_lim + ${RL_BIN} + -c 1000 + -g 30000 + -n 30 + -t 100 + --check-grouplimit 1000 + --check-connlimit 50 + --check-stddev 50) + + # Connection limit and group limit with independent drain. + add_test(test-ratelim__group_con_lim_drain + ${RL_BIN} + -c 1000 + -g 35000 + -n 30 + -t 100 + -G 500 + --check-grouplimit 1000 + --check-connlimit 50 + --check-stddev 50) + + # Add a "make verify" target, same as for autoconf. + # (Important! This will unset all EVENT_NO* environment variables. + # If they are set in the shell the tests are running using simply "ctest" or "make test" will fail) + if (WIN32) + # Windows doesn't have "unset". But you can use "set VAR=" instead. + # We need to guard against the possibility taht EVENT_NOWIN32 is set, and all test failing + # since no event backend being available. + file(TO_NATIVE_PATH ${CMAKE_CTEST_COMMAND} WINDOWS_CTEST_COMMAND) + + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/tmp/verify_tests.bat + " + set EVENT_NOWIN32= + \"${WINDOWS_CTEST_COMMAND}\" + ") + + message(STATUS "${WINDOWS_CTEST_COMMAND}") + + file(COPY ${CMAKE_CURRENT_BINARY_DIR}/tmp/verify_tests.bat + DESTINATION + ${CMAKE_CURRENT_BINARY_DIR} + FILE_PERMISSIONS + OWNER_READ + OWNER_WRITE + OWNER_EXECUTE + GROUP_READ + GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) + + file(TO_NATIVE_PATH + "${CMAKE_CURRENT_BINARY_DIR}/verify_tests.bat" VERIFY_PATH) + + add_custom_target(verify COMMAND "${VERIFY_PATH}" + DEPENDS event ${ALL_TESTPROGS}) + else() + # On some platforms doing exec(unset) as CMake does won't work, so make sure + # we run the unset command in a shell instead. + # First we write the script contents. + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/tmp/verify_tests.sh + " + #!/bin/bash + unset EVENT_NOEPOLL; unset EVENT_NOPOLL; unset EVENT_NOSELECT; unset EVENT_NOWIN32; unset EVENT_NOEVPORT; unset EVENT_NOKQUEUE; unset EVENT_NODEVPOLL + ${CMAKE_CTEST_COMMAND} + ") + + # Then we copy the file (this allows us to set execute permission on it) + file(COPY ${CMAKE_CURRENT_BINARY_DIR}/tmp/verify_tests.sh + DESTINATION ${CMAKE_CURRENT_BINARY_DIR} + FILE_PERMISSIONS + OWNER_READ + OWNER_WRITE + OWNER_EXECUTE + GROUP_READ + GROUP_EXECUTE + WORLD_READ + WORLD_EXECUTE) + + # Create the target that runs the script. + add_custom_target(verify + COMMAND + ${CMAKE_CURRENT_BINARY_DIR}/verify_tests.sh + DEPENDS + event + ${ALL_TESTPROGS}) + endif() + + if (NOT EVENT__DISABLE_REGRESS AND __FOUND_USABLE_PYTHON) + add_dependencies(verify regress) + endif() + + if (EVENT__COVERAGE) + include(CodeCoverage) + + setup_target_for_coverage( + verify_coverage # Coverage target name "make verify_coverage" + make # Test runner. + coverage # Output directory. + verify) # Arguments passed to test runner. "make verify" + endif() + + enable_testing() + + include(CTest) +endif() + +# +# Installation preparation. +# + +# Allow the user to override installation directories. +set(EVENT_INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries") +set(EVENT_INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables") +set(EVENT_INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files") + +if(WIN32 AND NOT CYGWIN) + set(DEF_INSTALL_CMAKE_DIR cmake) +else() + set(DEF_INSTALL_CMAKE_DIR lib/cmake/libevent) +endif() + +set(EVENT_INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files") + +# Make sure the paths are absolute. +foreach(p LIB BIN INCLUDE CMAKE) + set(var EVENT_INSTALL_${p}_DIR) + if(NOT IS_ABSOLUTE "${${var}}") + set(${var} "${CMAKE_INSTALL_PREFIX}/${${var}}") + endif() +endforeach() + +# Export targets (This is used for other CMake projects to easily find the libraries and include files). +export(TARGETS event event_extra event_core + FILE "${PROJECT_BINARY_DIR}/LibeventTargets.cmake") +export(PACKAGE libevent) + +# Generate the config file for the build-tree. +set(EVENT__INCLUDE_DIRS + "${PROJECT_SOURCE_DIR}/include" + "${PROJECT_BINARY_DIR}/include") + +set(LIBEVENT_INCLUDE_DIRS + ${EVENT__INCLUDE_DIRS} + CACHE PATH "Libevent include directories") + +configure_file(${PROJECT_SOURCE_DIR}/cmake/LibeventConfigBuildTree.cmake.in + ${PROJECT_BINARY_DIR}/LibeventConfig.cmake + @ONLY) + +# Generate the config file for the installation tree. +file(RELATIVE_PATH + REL_INCLUDE_DIR + "${EVENT_INSTALL_CMAKE_DIR}" + "${EVENT_INSTALL_INCLUDE_DIR}") # Calculate the relative directory from the Cmake dir. + +# Note the EVENT_CMAKE_DIR is defined in LibeventConfig.cmake.in, +# we escape it here so it's evaluated when it is included instead +# so that the include dirs are givenrelative to where the +# config file is located. +set(EVENT__INCLUDE_DIRS + "\${EVENT_CMAKE_DIR}/${REL_INCLUDE_DIR}") + +configure_file(${PROJECT_SOURCE_DIR}/cmake/LibeventConfig.cmake.in + ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/LibeventConfig.cmake + @ONLY) + +# Generate version info for both build-tree and install-tree. +configure_file(${PROJECT_SOURCE_DIR}/cmake/LibeventConfigVersion.cmake.in + ${PROJECT_BINARY_DIR}/LibeventConfigVersion.cmake + @ONLY) + +# Define the public headers. +set_target_properties(event event_core event_extra + PROPERTIES PUBLIC_HEADER "${HDR_PUBLIC}") + +# +# Install targets. +# +install(TARGETS event event_core event_extra + EXPORT LibeventTargets + RUNTIME DESTINATION "${EVENT_INSTALL_BIN_DIR}" COMPONENT bin + LIBRARY DESTINATION "${EVENT_INSTALL_LIB_DIR}" COMPONENT lib + ARCHIVE DESTINATION "${EVENT_INSTALL_LIB_DIR}" COMPONENT lib + PUBLIC_HEADER DESTINATION "${EVENT_INSTALL_INCLUDE_DIR}/event2" COMPONENT dev) + +# Install compat headers +install(FILES ${HDR_COMPAT} + DESTINATION + "${EVENT_INSTALL_INCLUDE_DIR}" + COMPONENT dev) + +# Install the configs. +install(FILES + ${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/LibeventConfig.cmake + ${PROJECT_BINARY_DIR}/LibeventConfigVersion.cmake + DESTINATION + "${EVENT_INSTALL_CMAKE_DIR}" + COMPONENT dev) + +# Install exports for the install-tree. +install(EXPORT LibeventTargets + DESTINATION + "${EVENT_INSTALL_CMAKE_DIR}" + COMPONENT dev) + +set(LIBEVENT_LIBRARIES + event + event_core + event_extra + CACHE STRING "Libevent libraries") + +message("") +message(" ---( Libevent " ${EVENT_VERSION} " )---") +message("") +message(STATUS "Available event backends: ${BACKENDS}") +message(STATUS "CMAKE_BINARY_DIR: " ${CMAKE_BINARY_DIR}) +message(STATUS "CMAKE_CURRENT_BINARY_DIR: " ${CMAKE_CURRENT_BINARY_DIR}) +message(STATUS "CMAKE_SOURCE_DIR: " ${CMAKE_SOURCE_DIR}) +message(STATUS "CMAKE_CURRENT_SOURCE_DIR: " ${CMAKE_CURRENT_SOURCE_DIR}) +message(STATUS "PROJECT_BINARY_DIR: " ${PROJECT_BINARY_DIR}) +message(STATUS "PROJECT_SOURCE_DIR: " ${PROJECT_SOURCE_DIR}) +message(STATUS "CMAKE_MODULE_PATH: " ${CMAKE_MODULE_PATH}) +message(STATUS "CMAKE_COMMAND: " ${CMAKE_COMMAND}) +message(STATUS "CMAKE_ROOT: " ${CMAKE_ROOT} ) +message(STATUS "CMAKE_SYSTEM: " ${CMAKE_SYSTEM} ) +message(STATUS "CMAKE_SYSTEM_NAME: " ${CMAKE_SYSTEM_NAME} ) +message(STATUS "CMAKE_SYSTEM_VERSION: " ${CMAKE_SYSTEM_VERSION} ) +message(STATUS "CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR} ) +message(STATUS "CMAKE_SKIP_RPATH: " ${CMAKE_SKIP_RPATH} ) +message(STATUS "CMAKE_VERBOSE_MAKEFILE: " ${CMAKE_VERBOSE_MAKEFILE} ) +message(STATUS "CMAKE_C_FLAGS: " ${CMAKE_C_FLAGS} ) +message(STATUS "CMAKE_BUILD_TYPE: " ${CMAKE_BUILD_TYPE} ) +message(STATUS "CMAKE_C_COMPILER: " ${CMAKE_C_COMPILER} ) +message(STATUS "CMAKE_AR: " ${CMAKE_AR} ) +message(STATUS "CMAKE_RANLIB: " ${CMAKE_RANLIB} ) +message("") + diff --git a/libs/libevent/docs/ChangeLog b/libs/libevent/docs/ChangeLog new file mode 100644 index 0000000000..1499637504 --- /dev/null +++ b/libs/libevent/docs/ChangeLog @@ -0,0 +1,1402 @@ +Changes in version 2.1.5-beta (5 January 2015) + + Security Fixes (evbuffers) + o Avoid integer overflow bugs in evbuffer_add() and related functions. See CVE-2014-6272 advisory for more information. (d49bc0e88b81a5812116074dc007f1db0ca1eecd) + + New APIs (evconnlistener) + o Provide support for SO_REUSEPORT through LEV_OPT_REUSABLE_PORT (b625361 Maciej Soltysiak) + + Bugfixes (core) + o Fix use-after-free error in EV_CLOSURE_EVENT callback (3cc0eac John Ohl) + o Fix race caused by event_active (3c7d6fc vjpai) + + Bugfixes (evbuffer) + o Fix evbuffer_peek() with len==-1 and start_at non-NULL. (ba59923) + o Consistently check for failure from evbuffer_pullup() (60f8f72) + o Fix evbuffer_peek() with len==-1 and start_at non-NULL. (fb7e76a) + + Bugfixes (windows, IOCP) + o be async: avoid double close() (f133b86 Azat Khuzhin) + + Bugfixes (bufferevents) + o Fix issue #127, double free for filterevents that use BEV_OPT_CLOSE_ON_FREE (2c82aa0 John Ohl) + o make bufferevent_getwatermark api more robust (a21e510 ufo2243) + o [Bugfix] fix bufferevent setwatermark suspend_read (b34e4ac ufo2243) + o bufferevent_openssl: reset fd_is_set when setfd with -1 is called (3da84c2 Azat Khuzhin) + o Fix compilation for older OpenSSL versions. (5c7282f Joakim Soderberg) + + New APIs (evhttp) + o Add evhttp_connection_set_family() to set addrinfo->family for DNS requests (12c29b0 Azat Khuzhin) + o Implement interface that provides the ability to have an outbound evhttp_connection free itself once all requests have completed (2b9ec4c,10fe4f John Ohl) + + New APIs (core) + o Implement new/free for struct evutil_monotonic_timer and export monotonic time functions (f2645f8 Andrea Shepard) + + Bugfixes (evdns) + o Load hosts file on Windows. (a0b247c Vilmos Nebehaj) + o Don't truncate hosts file path on Windows. (d0dc861 Vilmos Nebehaj) + o Fix a crash in evdns related to shutting down evdns (9f39c88,e8fe749) + o evdns: avoid read-after-free in evdns_request_timeout_callback() (61262a0 Azat Khuzhin) + o Correctly handle allocation failures in evdns_getaddrinfo (6a53d15) + o evdns: fix EVDNS_BASE_DISABLE_WHEN_INACTIVE in case retransmit/retry (74d0eee Azat Khuzhin) + o evdns: add retry/reissue tests for EVDNS_BASE_DISABLE_WHEN_INACTIVE (3ca9d43 Azat Khuzhin) + o evdns: fail ns after we are failing/retrasmitting request (97c750d Azat Khuzhin) + + Bugfixes (evhttp) + o http: reset connection before installing retry timer (fix http retries handling) (bc79cc5 Azat Khuzhin) + + + Testing + o regress_dns: fix leaks in getaddrinfo_async{,_cancel_stress} tests (2fdc5f2 Azat Khuzhin) + o test: add family argument for http_connection_test_() (177b8a7 Azat Khuzhin) + o test: add regress for evhttp_connection_set_family() with AF_INET and AF_UNSPEC (42aefeb Azat Khuzhin) + o test/http: add regress test for set family to AF_INET6 (3fbf3cc Azat Khuzhin) + o Update to a more recent tinytest_macros. (8da5a18) + o test/regress: add simplestsignal: to track reorder bugs separately (b897bef Azat Khuzhin) + o test/evbuffer_peek: add regress in case we have first buffer greater (e2d139d Azat Khuzhin) + o More evbuffer_peek() test cases (154006a) + o use correct tt macro for pointer compare (08c88ea) + o regress_buffer: fix 'memcmp' compare size (79800df Maks Naumov) + o Fix a use-after-free in unit tests. CID 752027 (3739057) + o Fix a dead-code warning in unit tests. CID 1193548 (c119f24) + o Use evutil_weakrand() in unit tests. (a677b72, 364c110) + o Use a more precise calculation for max in time-ratelim.c (ca5b5c7) + o Make a buffer larger in the tests to avoid a scary evbuffer_copyout_from() (fb57b8b) + o Fix several memory leaks in the unit tests. (89c1a3b) + o Add test for evhttp_connection_free_on_completion (b0e9924 John Ohl) + o Fix annoying heisenbug in test-time.c (cb73704) + + Sample code + o Make http-server.c output into good html5 (6d72bdc) + o Use FindClose for handle from FindFirstFile in http-server.c (6466e88) + o https-client: add -retries argument, for connection retries (d9da844 Azat Khuzhin) + + Bugfixes (build) + o Add missing headerfile for cmake (15d90cc Trond Norbye) + o ignore one more test binary (b6593aa Michael Richardson) + o ignore config.cache/test-driver files (c83f333 Mike Frysinger) + o add a --disable-samples configure flag (0c492b3 Mike Frysinger) + o Add a few files created by "make verify" to .gitignore. (1a8295a Pierre Phaneuf) + o updates in cmake build (27bd9fa Sergey Nikulov) + o Fix cmake error when the Module path has more than one entry. (befbd13 Acer Yang) + o Fix CMake shared library build (e69d910 Nobuaki Sukegawa) + o Fix warnings when compiling with clang 3.5 (f5b4765 John Ohl) + o Fix mixed declarations and code (forbidden by ISO C90) (8afbdbc Thomas Bernard) + + Bugfixes (miscellaneous) + o tree.h: drop duplicated content of tree.h (6193187 Azat Khuzhin) + o evdns: disable probing with EVDNS_BASE_DISABLE_WHEN_INACTIVE (610410b,ad0493e,fea86a6,d83b337,5ca9e97 Azat Khuzhin) + o [Bugfix] fix grammer error (3a4d249 ufo2243) + o Change return type of evutil_load_windows_system_library_ to HMODULE (f691389) + o Fix a c90 warning (76643dd) + o Fix a typo in a doxygen comment. Reported by 亦得. (be1aeff) + o remove trailing comma from enum (b361b8a Jean-Philippe Ouellet) + + Bugfixes (FreeBSD) + o Handle ENOTCAPABLE from FreeBSD - this is returned if an event in the changelist is for an FD that has been closed. (6fd7394 Adrian Chadd) + + + +Changes in version 2.1.4-alpha (21 Mar 2014) + + Libevent 2.1.4-alpha adds a number of new miscellaneous APIs to make + Libevent more useful, including support for early close detection with + epoll via EPOLLRDHUP, triggering bufferevent callbacks, adding more + evhttp callbacks, and more. There are also numerous bugfixes, including + a number for finalize-related issues from 2.1.3-alpha; and an + alternative (non-primary!) cmake-based build mechanism. + + New APIs (core) + o Added event_base_get_num_events() (0fa107d Mobai Zhang) + o New event_base_active_by_fd API (865a142 Greg Hazel, 5c9da9a, 87fa2b0) + o Add event_base_active_by_signal by analogy (4865943) + o Add access to max event count stats (5173bef, efbd3dc, 26230a2 + Andrew Sweeney) + o Implemented EV_CLOSED event for epoll backend + (EPOLLRDHUP). (b1b69ac Diego Giagio, 53d2793, 43ffcf6, dfe1e52 + Marcin Juszkiewicz, ff26633 Joakim Soderberg, 3908a5e) + + New APIs (evutil_secure_rng) + o Add evutil_secure_rng_set_urandom_device_file (2bbb5d7) + + New APIs (bufferevents) + o Add function to fetch underlying ratelimit cfg (4b3d5af Mark Ellzey) + o Pass and return const for bufferevent_get_token_bucket_cfg (1c77fbb + Mark Ellzey) + o Add watermark introspection (4ce242b OndÅ™ej KuznÃk) + o Add an option to trigger bufferevent I/O callbacks (61ee18b OndÅ™ej KuznÃk) + o Add an option to trigger bufferevent event callbacks (a7384c7 + OndÅ™ej KuznÃk) + o Clarifications in response to merge req. comments (bd41947 OndÅ™ej + KuznÃk) + o Minor optimizations on bufferevent_trigger options (a3172a4) + + New APIs (evhttp) + o Add evhttp_connection_get_server(). (a7f82a3 Maxime Henrion) + o add a http default content type option (5a5acd9 Nicolas Martyanoff) + o http: implement new evhttp_connection_get_addr() api. (0c7f040 Azat + Khuzhin) + o Add a variant of evhttp_send_reply_chunk() with a callback on + evhttp_write_buffer() (8d8decf Julien BLACHE) + o Allow registering callback for parsing HTTP headers (b0bd7fe Balint Reczey) + o Provide on request complete callback facility (b083ca0 Andrew Sweeney) + o evhttp_request_set_on_complete_cb to be more specific about what + the function actually does and usage (da86dda Andrew Sweeney) + o Update unit test to make sure that the callback happens after the + output data is written (b85f398 Andrew Sweeney) + + Features (evdns) + o bug fix for issues #293 evdns_base_load_hosts doesn't remove + outdated addresses (954d2f9, f03d353, 45eba6f Kuldeep Gupta) + + Features: (cmake build support) + o Initial CMake commit. (e415196 Joakim Soderberg) + o Add all tests and benchmarks to CMake project. (e9fc014 Joakim Soderberg) + o More work on adding tests to CMake project (99c1dc3 Joakim Soderberg) + o Generate a dummy evconfig-private.h so things build + properly. (ce14def Joakim Soderberg) + o Link libm on unix platforms. (58fcd42 Joakim Soderberg) + o Added some GCC specific options. (19222e5 Joakim Soderberg) + o Use evutil_closesocket instead. (dbf2b51 Joakim Soderberg) + o Add copyright and licensing files for CMake modules. (c259d53 + Joakim Soderberg) + o Only include WIN32 getopt where it is used. (9bbce0b Joakim Soderberg) + o Fix bench_cascade program on Windows. (78da644 Joakim Soderberg) + o Don't segfault on no found event backend. (8f2af50 Joakim Soderberg) + o Only test the event backends available on the system. (7ea4159 + Joakim Soderberg) + o Added a "make verify" target. (e053c4f Joakim Soderberg) + o Fix the make "verify" target on Windows. (67e5d74 Joakim Soderberg) + o Get rid of deprecation warnings for OpenSSL on OSX 10.7+ (69c3516 + Joakim Söderberg) + o Fix kqueue support. (a831f2f Joakim Söderberg) + o Added a test for testing if kqueue works with pipes. (2799b35 + Joakim Söderberg) + o Change the BSD license from 4 to 3-clause. (86df3ed Joakim Soderberg) + o Minimum required python version is 2.4. (968e97b Joakim Soderberg) + o Get rid of unknown pragma warnings. (0ef1d04 Joakim Soderberg) + o Add a "make verify_coverage" target generation coverage + info. (f2483f8 Joakim Soderberg) + o Fix the "make verify" target on NetBSD (4ac086a Joakim Soderberg) + o Only look for ZLib when it is used (if tests are + included). (f780593 Joakim Soderberg) + o Added EVENT__ENABLE_GCC_WARNINGS, turns all warnings into + errors. (dd413bd Joakim Soderberg) + o Add CMake config and install targets. (f3446ed Joakim Soderberg) + o Fix typo (4b754df Joakim Soderberg) + o Some work on making it possible to simply do add_subdirectory() on + the project. (49ab363 Joakim Soderberg) + o Set USE_DEBUG=1 on EVENT__ENABLE_VERBOSE_DEBUG (fd42e70 Joakim Soderberg) + o Fix so that old nmake project still builds. (24d6466 Joakim + Soderberg) + o Rename README to README.md and use markdown to format. (d2bc39a + Joakim Soderberg) + o Update README with CMake build instructions. (604b8cc Joakim Soderberg) + o Clean up the README some. (8d4cb35 JoakimSoderberg) + o Forgotten headers for old nmake project compatability. (8697b99 + Joakim Soderberg) + o Change all uses of WIN32 to _WIN32 (4e14395 Joakim Söderberg) + o Fix include bug. (2024467 Joakim Söderberg) + o Check if we're on OSX before disabling deprecation in le-proxy + (8b40a5b Joakim Söderberg) + o Fix broken autotools build. (ae1bd82 Joakim Söderberg) + o Disclaimerize cmake a little in the README (d03b5bf) + o Fix CMake compile when OpenSSL is disabled. (e423d42 Joakim + Söderberg) + o CMake: Get rid of python not found warning when regress tests + turned off. (d38d798 Joakim Söderberg) + o Fix https-client compilation on Windows. (d7be788 Joakim Soderberg) + o Guard against EVENT_NOWIN32 being set during testing. (f1715b4 + Joakim Soderberg) + o Check for OSX when checking for clang. (e212c54 Joakim Soderberg) + o Added a Travis-CI configuration file. (8c0f0a9 Joakim Soderberg) + o Added -Qunused-arguments for clang on macosx (ed99d92 Trond Norbye) + o Rename event_extras to event_extra (a0dd5df Trond Norbye) + o Add option to build shared library (4545fa9 Trond Norbye) + o Add -Qunused-arguments for clang on macos (b56611d Trond Norbye) + o Add cmake-related files to .gitignore (e061321 Trond Norbye) + o Export event_extra not event_extras. (2b41bcf Joakim Söderberg) + + Bugfixes (core) + o If evsel->del() fails, don't leave the evmap in an inconsistent + state (9b5a527 Maxime Henrion) + o Move event_debug_note_teardown_ before mm_free. (69b5c64) + o Check CLOCK_MONOTONIC_* at runtime if needed. (911abf3) + o Fix reinit of fds with EV_WRITE but not EV_READ. (ebfd8a8 maksqwe) + o Tweaked callbacks to prevent race condition + (https://github.com/libevent/libevent/issues/104) (40830f1, 2ea15ed + John Ohl) + o Move assert(ev) to before we use ev in EV_CLOSURE_EVENT_FINALIZE + case (9805972) + + Bugfixes (evhttp) + o Fix a double close() bug in evhttp when the underlying bufferevent uses + BEV_OPT_CLOSE_ON_FREE. (31db8a0 Maxime Henrion) + o Fix an unlikely but possible error case for http connections (f22049e) + o Avoid racy bufferevent activation (5eb1788 Nate Rosenblum) + + Bugfixes on 2.0 (Windows) + o Use windows vsnprintf fixup logic on all windows environments (e826f19) + o libevent/win32_dealloc() : fix sizeof(pointer) vs sizeof(*pointer) + (b8f5980 Frank Denis) + + Bugfixes (evutil_secure_rng) + o When we seed from /proc/sys/kernel/random/uuid, count it as success + (e35b540) + o We should return after arc4random_buf() (1ea1f26 Makoto Kato) + o Avoid other RNG initialization FS reads when urandom file is + specified (9695e9c) + o Really remove RNG seeds from the stack (f5ced88) + o Fix another arc4random_buf-related warning (e64a2b0) + + Bugfixes (bufferevents) + o Initialize async bufferevent timeout CBs unconditionally (af9b2a7) + + Bugfixes (evdns) + o Checking request nameserver for NULL, before using it. (5c710c0 + Belobrov Andrey) + o Fix SEGFAULT after evdns_base_resume if no nameservers + installed. (14971a8 Azat Khuzhin) + o Actually use the log facility for reporting evdns problems. (e1766a1) + o Fix SEGFAULT after evdns_base_resume if no nameservers + installed. (f8d7df8 Azat Khuzhin) + o fix for ServFail from RIPE Atlas release (62f596b Antony Antony) + + Bugfixes (compilation) + o Fix test compilation with nmake: add the gdi.lib dependency (5ba8ab7) + o Whoops. It is gdi.lib, not gdi32.lib. (github issue #61) (8ab612e) + o Don't use return since return type is void and build error occurs + using clang (838161d Makoto Kato) + o Use void casts to suppress some "unchecked return value" warns (7080d55) + o rpcgen: Generate regress.gen.[c,h] in build rather than src dir + (243386c Ross Lagerwall) + o Fix a compiler warning when checking for arc4random_buf linker + breakage. (5cb3865) + o Fix 'make distcheck' by adding regress.gen.[ch] to DISTCLEANFILES + (239d834) + + o Fix a c90 warning (c207682) + o Fix consts in WIN32-Code/getopt*.[ch] (57abb35) + + Bugfixes (locks, synchronization) + o Missed lock acquire/release in event_base_cancel_single_callback_() + (d3d999a Azat Khuzhin) + o Fix locking in bufferevent_get_options_(). (dbc9cd4 Maxime Henrion) + + Bugfixes (leaks) + o Avoid leaking segment mappings when offset is not a page multiple (d409514) + + Testing + o Add tests for evdns_base_resume(). (1cd9ff5 Azat Khuzhin) + o Fix dns/leak_resume_send_err test. (7e876df Azat Khuzhin) + o Add checks for evhttp_connection_get_server() in unit + tests. (fbc323b Maxime Henrion) + o Fix a (failure-only) null dereference in the unit tests (1104d0b) + o Fix a logic error in test_evbuffer_freeze (7765884) + o Add missing check to test_evbuffer_file_segment_add_cleanup_cb (eba4506) + o Fix some crash-on-fail cases in DNS regression tests (87cd6f0) + o DNS tests: add a missing check (f314900) + o Finalize tests: add a missing check (82b6956) + o test_evutil_rtrim: add another missing check. (e193c95) + o regress_main: logging all if env EVENT_DEBUG_LOGGING_ALL isset + (611e28b Azat Khuzhin) + o regress_http: add tests for evhttp_connection_get_addr() (4dd500c + Azat Khuzhin) + o Update to the latest version of tinytest (7a80476) + o Heap-allocate zlib data structure in regress_zlib tests (4947c18) + + Performance tweaks (core) + o Avoid redundant syscall to make a nonblocking socket nonblocking + (42c03da Maxime Henrion) + o Avoid redundant syscall if making a socket cloexec twice (1f29b18) + o Avoid redundant invocations of init_extension_functions for IOCP (3b77d62) + + Documentation + o Document that arc4random is not a great cryptographic PRNG. (6e49696) + o Small doxygen tweaks (6e67b51) + o Try another doxygen tweak (ccf432b) + o Clarify event_base_loop exit conditions (031a803) + o Fix a typo (be7bf2c OndÅ™ej KuznÃk) + o Document deferred eventcb behaviour (13a9a02 OndÅ™ej KuznÃk) + o Typo fixes from Linus Nordberg (cec62cb, 8cd695b) + o Fix duplicate paragraph in evbuffer_ptr documentation (58408ee) + + Code Improvements (coverity) + o Fix a pile of coverity warnings in the unit tests (867f401) + o Fix coverity warnings in benchmark tools. (ff7f739) + o Whoops; fix compilation in bench.c (544cf88) + o Remove spurious checks in evrpc.c error cases (coverity) (991b362) + o Fix a couple of compilation warnings in regress_http.c (860767e) + o Fix even more coverity warnings. (d240328) + o Stop checking for inet_aton; we don't use it. (f665d5c) + o Add an include to evrpc-internal to fix openbsd compilation warning + (5e161c6) + + Cleanups + o Remove an unreachable return statement in minheap-internal.h (e639a9e) + o Refactor evmap_{io,signal}_active_() to tolerate bad inputs (974c60e) + o Fix needless bufferevent includes in evdns.c (254c04e) + o Fix a couple of "#ifdef WIN32" instances (88ecda3) + o Remove unneeded declaration in bufferevent-internal.h (4c8ebcd) + + Sample code + o le-proxy: Fail more gracefully if opening listener fails (44b2491) + o http-server: drop uri_root from base_url in http-server. (6171e1c Azat Khuzhin) + o https-client: POST supported, args supported (c5887f7 Alexey Ozeritsky) + o https-client: code cleanup (29af65e Alexey Ozeritsky) + o https-client: Small tweaks to https-client.c (90786eb) + o https-client: Set hostname for SNI extension (by f69m) (d1976f8) + o https-client: add a cast to https-client.c (462e6b6) + + + +Changes in version 2.1.3-alpha (1 May 2013) + + Libevent 2.1.3-alpha fixes various bugs, adds new unit tests, and cleans + up the code in a couple of places. It has a new callback in evhttp for + reporting errors during a request, a new feature for allowing evdns to + not keep the event_base looping when there are no requests inflight, and + example code for writing an https client. + + Libevent 2.1.3-alpha also has an important new (experimental) event + finalization feature to allow safe event teardown in multithreaded + programs. This ought to fix the longstanding bug with deadlocks in + multithreaded use of SSL-based bufferevents that some people have been + experiencing since Libevent 2.0. + + + Core (event finalization) + o Implement event_finalize() and related functions to avoid certain + deadlocks (8eedeab) + o Use finalization feature so bufferevents can avoid deadlocks (02fbf68) + o Always run pending finalizers when event_base_free() is called (e9ebef8) + o Remove bufferevent_del_generic_timeout_cbs as now unused (4ea4c6a) + o More documentation for finalization feature (a800b91) + o Make the event_finalize* functions return an error code (5d11f4f) + o Mark the finalize stuff as experiemental in case it needs to + change (23e2e29) + + Evdns + o evdns: New flag to make evdns not prevent the event loop from + exiting (6b7fa62 Azat Khuzhin) + + Bugfixes (Core) + o Make event_remove_timer behave correctly with persistent timers (5623e80) + o Unit test for event_remove_timer with EV_PERSIST. (96150dd) + o Double-check next timeout when adding events (9443868 Nate Rosenblum) + o event_base_update_cache_time should be a no-op if the loop isn't + running (5e6fa2a) + + Bugfixes (evhttp, crash fix, from 2.0) + o fix #73 and fix http_connection_fail_test to catch it (b618204 Greg Hazel) + + Bugfixes (compilation and portability, from 2.0) + o Fix compilation with WIN32_HAVE_CONDITION_VARIABLES enabled (7e45739) + o Fix missing AC_PROG_SED on older Autoconfs (9ab2b3f Tay Ray Chuan) + o Backport libevent to vanilla Autoconf 2.59 (as used in RHEL5) + (74d4c44 Kevin Bowling) + o Use AC_CONFIG_HEADERS in place of AM_CONFIG_HEADERS for autmake + 1.13 compat (817ea36) + o Rename configure.in to configure.ac to appease newer autoconfs (0c79787) + o Avoid using top_srcdir in TESTS: new automakes do not like this (a55514e) + + Bugfixes (resource leaks/lock errors on error, from 2.0) + o Avoid leaking fds on evconnlistener with no callback set (69db261) + o Avoid double-close on getsockname error in evutil_ersatz_socketpair + (0a822a6) + o Fix a locking error in bufferevent_socket_get_dns_error. (0a5eb2e) + + Documentation Fixes (from 2.0) + o Fix a mistake in evbuffer_remove() arguments in example http server code + (c322c20 Gyepi Sam) + o Fix a typo in a comment in buffer.h. Spotted by Alt_F4 (773b0a5) + + Documentation Fixes + o minor documentation typos (809586a Patrick Pelletier) + o Fix cut-and-paste err in whatsnew-2.1 (49905ac) + o Fix comment to refer to sample/include.am correctly (9e8cdf3 Sebastian + Hahn) + o Fix typo : Dispatching instead of Dispaching (0c2bacc Volker Lendecke) + o fix some hinky indentation in evhttp_make_request (80e220e Patrick + Pelletier) + o "buffer" spelling (a452811 Patrick Pelletier) + o Specify return behavior in header for evbuffer_pullup() in corner case + (cf8d1cd Dan Petro) + o Clarify an important point about event_base_foreach_event() (920a5e6) + + Compilation Fixes/Tool Support + o avoid valgrind false positive by zeroing epoll_event (1258614 Patrick + Pelletier) + o Fix harmless clang enum warning (b452a43 Sebastian Hahn) + o remove all exes on "make clean", not just regress.exe (974bfa0 Patrick + Pelletier) + o Make --disable-libevent-regress work again (787fd74) + o Do not build strlcpy.c when it will have no code. (4914620) + + Portability Fixes + o When EWOULDBLOCK is not EAGAIN, treat it as equivalent to it (bf7a0ff) + o Preliminary changes for Minix3. (0dda56a Nicholas Heath) + o Use AC_CONFIG_HEADERS in place of AM_CONFIG_HEADERS for autmake 1.13 + compat (bf278b) + o Avoid using $(top_srcdir) in TESTS. (2863c83) + o build test/test-script.sh on systems with a less-featureful $< (f935e21) + o Implement EVUTIL_ERR_IS_EAGAIN on windows. (42aaf4d) + + Evhttp changes: + o Fix ipv6 support for http. When URL contain domain, not IP + address. (71e709c Azat Khuzhin) + o uri decode: fix for warning "use of uninitialised value" (64b6ece Azat + Khuzhin) + o uri decode: changed the test for the existence of the next character + (e1903e3 Azat Khuzhin) + o Move prototype of evhttp_decode_uri_internal() to http-internal.h + (de8101a Azat Khuzhin) + o Test: decoding just part of string with evhttp_decode_uri_internal() + (1367653 Azat Khuzhin) + o Add new error_cb for actual reporting of HTTP request errors. (7b07719 + Azat Khuzhin) + o Add test for EVREQ_HTTP_REQUEST_CANCEL into http_cancel_test() (862c217 + Azat Khuzhin) + o Drop extra header http_struct.h from regress_http.c (54cc800 Azat Khuzhin) + + Testing + o Add regress test ipv6_for_domain. (9ec88bd Azat Khuzhin) + o Add an environment variable (EVENT_DEBUG_MODE) to run unit tests in debug + mode (2fad0f3) + o Add a test with an active_later event at event_base_free time. (1c3147f) + o Make all tests pass under EVENT_DEBUG_MODE=1 (b1b054f) + o Add some verbose notes to bufferevent unit tests (9d893c9) + o New test for active_later->active transition on event_active (a153874) + o New tests for event_base_foreach_event() (0b096ef) + o Unit tests for event_base_gettimeofday_cached() and + event_base_update_cache_time() (30ea291) + o A test for event_get_assignment() (f09629e) + o More unit tests for initializing common timeouts. (d596739) + o Fix a bug in the new main/event_foreach test (702c9aa) + + Windows: + o use FormatMessage for winsock errors (0c6ec5d, 2078e9b, 4ccdd53, c9ad3af + Patrick Pelletier) + o a program to print out the error strings for winsock errors (7296512 + Patrick Pelletier) + o Fix a warning introduced in 0c6ec5d8 (eeb700c) + o Fix another warning introduced in 0c6ec5d8 (ed26561) + + Examples (http) + o Add sample/https-client.c, an example of stacking evhttp as a client on + top of bufferevent_ssl. (be46c99 Catalin Patulea) + o use ${OPENSSL_LIBS} instead of -lssl -lcrypto (bf31fa5 Patrick Pelletier) + o https-client was putting newlines at 256-byte boundaries (42d7441 Patrick + Pelletier) + o better handling of OpenSSL errors (5754d96 Patrick Pelletier) + o use Debian's default root certificate location (aacd674 Patrick Pelletier) + o use iSECPartners code to validate hostname in certificate (64d9f16 + Patrick Pelletier) + o avoid sign mismatch warning in openssl_hostname_validation.c (6021cb5 + Patrick Pelletier) + o pull in wildcard matching code from cURL (4db9da6 Patrick Pelletier) + o Another tweak to https-client.c (95acdaa) + o Remove http_struct.h usage in sample/https-client.c (8a90a85) + + + +Changes in version 2.1.2-alpha (18 Nov 2012) + + Libevent 2.1.2-alpha includes more portable for monotonic timers, + refactors much of Libevent's internal and external infrastructure, + closes some longstanding gaps in the interface, makde other + improvements. Ths log below tries to organize features by rough area of + effect. It omits a few commits which were pure bugfixes on other commits + listed below. For more detail, see the git changelogs. For more + insight, see the "whatsnew-2.1.txt" document included in the Libevent + 2.1.2-alpha distribution. + + Libevent 2.1.2-alpha also includes all changes made in 2.0.19-stable + through 2.0.21-stable inclusive. + + Performance (core): + o Replace pipe-based notification with EVFILT_USER where possible. This + should make multithreaded programs on OSX and *BSD alert the main thread a + little faster. (53a07fe) + o Make th_base_lock nonrecursive. (9cd5acb) + + New/Changed API Functions: + o New event_get_priority() function to return an event's priority (f90e255) + o Add a bufferevent_get_priority() function (bd39554) + o Add an event_base_loopcontinue() to tell Libevent to rescan for more + events right away (7d6aa5e) + o Add a new callback to get called on evbuffer_file_segment free + (e9f8feb yangacer, 64051b9) + o Expose event_base_foreach_event() as a public API. (84fd6d7 Roman + Puls, 232055e, ffe1643) + o Add an event_remove_timer() to remove timer on an event without + deleting it (e3b2e08) + o Make bufferevent_set_timeouts(bev, NULL, NULL) have plausible + semantics (9dee36b) + o Rename event_enable_lock_debuging() to ..._debugging(). (The old name + should still work.) (07e132e) + o Add missing implementation for event_enable_debug_logging (3b3e21d) + + PORTABLE MONOTONIC TIMERS: + + Libevent 2.1.2 includes internal support for monotonic timers on + (nearly) all supported platforms, including Windows, and OSX. Libevent + applications should now be more resilient to jumps forwards or backwards + in the system clock. Also, on Linux systems with epoll, we now + optionally support microsecond-level timeouts (whereas epoll only + supports millisecond-precision timeouts). + + o Use mach_absolute_time() for monotonic clock support on OSX. (b8fd6f9) + o Do not track use_monotonic field when is no monotonic clock (cb653a0) + o EVENT_BASE_FLAG_PRECISE_TIMER indicates we want fine timer precision + (ddd69d3) + o On Linux, use CLOCK_MONOTONIC_COARSE by default (55780a7) + o Implement a GetTickCount-based monotonic timer for Windows (d5e1d5a) + o Refactor monotonic timer handling into a new type and set of + functions; add a gettimeofday-based ratcheting implementation (f5e4eb0) + o Add EVENT_PRECISE_TIMER environment var for selecting precise-but-slow + timer (a2598ec) + o Implement fast/precise monotonic clocks on Windows (2c47045) + o Simple unit tests for monotonic timers (630f077) + o Improve the monotonic-time unit test: make it check the step size (7428c78) + o When PRECISE_TIMERS is set with epoll, use timerfd for microsecond + precision (26c7582) + o Split out time-related evutil functions into a new evutil_time.c (c419485) + o Split out time-related prototypes into time-internal.h (71bca50) + o Add evutil_time.obj to Makefile.nmake (0ba0683) + o Avoid giving a spurious warning when timerfd support is unavailable + (1aaf9f0 Dave Hart) + o Make test_evutil_monotonic a little more tolerant (def3b83) + o Avoid unused-var warning on systems with clock_gettime but without + CLOCK_MONOTONIC_COARSE (9be5468) + +EVENT_BASE_ONCE LEAKS: + If a callback added by event_base_once() is never invoked, Libevent no + longer leaks internal memory. + + o Free dangling event_once objects on event_base_free() (c17dd59) + o Add a unit test in which an event is created with event_base_once() + but never fires (4343edf) + +TESTING SUPPORT, FIXES AND IMPROVEMENTS: + + Libevent now disables by default its unit tests that would touch the + network, or that tend to fail on heavily-loaded systems. To re-enable + them, invoke the ./test/regress program with the @all alias. + + o Simplify test.sh code significantly. (9b856fd Ross Lagerwall) + o Make all tests that hit the network disabled by default (f2cea87) + o Avoid a resource leak on error in http client benchmark (ea92fba) + o Update to latest tinytest (911b4f0349377) (ef7c4f7) + o Avoid (unlikely) overflow in bench_httpclient.c (5671033) + o Shave 700 msec off the persistent_timeout_jump test (21205b8) + o Check return value of write() in regress.c (c8009d2) + o Make load-dependent monotonic timer tests off-by-default (2b6fe8b) + o Add deferred_cb_skew to list of timing-dependent tests (34c8f31) + o Avoid test -e; older shs don't have one. (f1bd938) + o Fix renegotiation test to work around openssl 1.0.1 bug (c2f3086) + o Fix a couple of compile warnings in the unit tests (5a9a014) + +MISC: + o Change evutil_weakrand_() to avoid platform random() (e86af4b Nicholas + Marriott, 3aa4415) + +INFRASTRUCTURE (Active-later events): + As a simplification and optimization to Libevent's "deferred callback" + logic (introduced in 2.0 to avoid callback recursion), Libevent now + treats all of its deferrable callback types using the same logic it uses + for active events. Now deferred events no longer cause priority + inversion, no longer require special code to cancel them, and so on. + + o Refactor the callback part of an event into its own event_callback + type (cba59e5) + o Add "active later" event_callbacks to supersede deferred (745a63d) + o event_base_assert_ok: check value of event_active_count for + correctness (fec8bae) + o Replace deferred_cbs with event_callback-based implementation. (ae2b84b) + o Replace more deferred_cb names with event_callback (a4079aa) + o Give event_base_process_active a single exit path (581b5be) + o Restore our priority-inversion-prevention code with deferreds (c0e425a) + o Refactor event_persist_closure: raise and extract some common logic + (bec22b4) + o Remove the unused bits from EVLIST_ALL (9889a3d) +||||||| merged common ancestors +Changes in version 2.0.22-stable (?? Dec 2013) + + (As of 3b77d62829c4393bda6f9105a5d3b73b48a64b71.) + +BUGFIXES (evhttp) + o fix #73 and fix http_connection_fail_test to catch it (crash fix) (b618204 Greg Hazel) + o Avoid racy bufferevent activation (5eb1788 Nate Rosenblum) + +BUGFIXES (compilation and portability) + o Fix compilation with WIN32_HAVE_CONDITION_VARIABLES enabled (7e45739) + o Fix missing AC_PROG_SED on older Autoconfs (9ab2b3f Tay Ray Chuan) + o Backport libevent to vanilla Autoconf 2.59 (as used in RHEL5) (74d4c44 Kevin Bowling) + o Use AC_CONFIG_HEADERS in place of AM_CONFIG_HEADERS for autmake 1.13 compat (817ea36) + o Rename configure.in to configure.ac to appease newer autoconfs (0c79787) + o Avoid using top_srcdir in TESTS: new automakes do not like this (a55514e) + o Use windows vsnprintf fixup logic on all windows environments (e826f19) + o Fix a compiler warning when checking for arc4random_buf linker breakage. (5cb3865) + o Fix another arc4random_buf-related warning (e64a2b0) + +BUGFIXES (resource leaks/lock errors on error) + o Avoid leaking fds on evconnlistener with no callback set (69db261) + o Avoid double-close on getsockname error in evutil_ersatz_socketpair (0a822a6) + o Fix a locking error in bufferevent_socket_get_dns_error. (0a5eb2e) + o libevent/win32_dealloc() : fix sizeof(pointer) vs sizeof(*pointer) (b8f5980 Frank Denis) + +BUGFIXES (miscellaneous) + o Avoid other RNG initialization FS reads when urandom file is specified (9695e9c, bb52471) + o Avoid redundant invocations of init_extension_functions for IOCP (3b77d62) + +BUFGIXES (evdns) + o Checking request nameserver for NULL, before using it. (5c710c0 Belobrov Andrey) + o Fix SEGFAULT after evdns_base_resume if no nameservers installed. (f8d7df8 Azat Khuzhin) + +BUGFIXES (evutil_secure_random) + o When we seed from /proc/sys/kernel/random/uuid, count it as success (e35b540) + o Document that arc4random is not a great cryptographic PRNG. (6e49696) + o Add evutil_secure_rng_set_urandom_device_file (2bbb5d7) + o Really remove RNG seeds from the stack (f5ced88) + + +DOCUMENTATION FIXES + o Fix a mistake in evbuffer_remove() arguments in example http server code (c322c20 Gyepi Sam) + o Fix a typo in a comment in buffer.h. Spotted by Alt_F4 (773b0a5) + + + +Changes in version 2.0.21-stable (18 Nov 2012) +BUGFIXES: + o ssl: Don't discard SSL read event when timeout and read come close together (576b29f) + o ssl: Stop looping in "consider_reading" if reading is suspended. (f719b8a Joachim Bauch) + o ssl: No need to reserve space if reading is suspended. (1acf2eb Joachim Bauch) + o dns: Avoid a memory-leak on OOM in evdns. (73e85dd, f2bff75 George Danchev) + o build: Use python2 rather than python (0eb0109 Ross Lagerwall) + o build: Compile without warnings on mingw64 (94866c2) + o build: Fix compilation on mingw64 with -DUSE_DEBUG (62bd2c4) + o build: Make rpcgen_wrapper.sh work on systems without a "python2" binary (f3009e4) + o iocp: Close IOCP listener socket on free when LEV_OPT_CLOSE_ON_FREE is set (cb853ea Juan Pablo Fernandez) + o core: Avoid crash when event_pending() called with no event_base set on event (e3cccf3) + o misc: remove stray 'x' so print_err will compile when uncommented (ac35650 Patrick Pelletier) + o tests: Fix renegotiation test to work around openssl 1.0.1 bug (c2f3086) + o tests: Warn when openssl version in unit test mismatches compiled version. (ac009f9) + + +Changes in version 2.0.20-stable (23 Aug 2012) +BUGFIXES: + o core: Make event_pending() threadsafe. (be7a95c Simon Liu) + o win32: avoid crash when waiting forever on zero fds. (160e58b) + o evhttp: Fix a memory leak on error in evhttp_uriencode (11c8b31) + o evbuffer: Avoid possible needless call to writev. Found by coverity. (6a4ec5c) + o evdns: memset sockaddr_in before using it. Found by coverity. (a1a0e67) + o evhttp: Check more setsockopt return values when binding sockets. Found by coverity (a0912e3) + o evdns: Avoid segfault on weird timeout during name lookup. (dc32077 Greg Hazel) + o bufferevent_ssl: Correctly invoke callbacks when a SSL bufferevent reads some and then blocks. (606ac43) + + +PORTABILITY FIXES: + o check for arc4random_buf at runtime, on OS X (bff5f94 Greg Hazel) + o Correctly check for arc4random_buf (fcec3e8 Sebastian Hahn) + o Add explicit AC_PROG_SED to configure.in so all autoconfs will expose $(SED) (ca80ea6) + +BUILD FIXES: + o Add GCC annotations so that the vsprintf functions get checked properly (117e327) + o Fix an unused variable warning on *BSD. (c0720c1) + +UNIT TEST FIXES: + o Fix a couple of memory leaks (found with Valgrind). (3b2529a Ross Lagerwall) + o Remove deadcode in http regression tests. Found by coverity. (5553346) + o Fix possible uninitialized read in dns regression tests. Found by coverity. (2259777) + o Set umask before calling mkstemp in unit tests. Found by coverity (f1ce15d) + o Fix various check-after-dereference issues in unit tests: found by coverity (4f3732d) + o Fix resource leaks in the unit tests; found by coverity (270f279) + o Add some missing null checks to unit tests; found by coverity (f021c3d) + o Avoid more crashes/bad calls in unit tests; found by coverity (3cde5bf) + o Remove unused variable; spotted by coverity (6355b2a) + o Add checks to various return values in unit tests. Found by coverity (b9e7329) + o Move assignment outside tt_assert in ssl unit tests. Appeases coverity. (a2006c0) + + + +Changes in version 2.0.19-stable (3 May 2012) +BUGFIXES (CORE): + o Refactor event_persist_closure: raise and extract some common logic (bec22b4) + o If time has jumped so we'd reschedule a periodic event in the past, schedule it for the future instead (dfd808c) + o If a higher-priority event becomes active, don't continue running events of the current priority. (2bfda40) + +BUGFIXES (SSL): + o Fixed potential double-readcb execution with openssl bufferevents. (4e62cd1 Mark Ellzey) + +BUGFIXES (DNS): + o Cancel a probe request when the server is freed, and ignore cancelled probe callbacks (94d2336 Greg Hazel) + o Remove redundant DNS_ERR_CANCEL check, move comment (46b8060 Greg Hazel) + o When retransmitting a timed-out DNS request, pick a fresh nameserver. (3d9e52a) + +DOCUMENTATION FIXES: + o Fix a typo in the bufferevent documentation (98e9119) + o Add missing ) to changelog; spotted by rransom (4c7ee6b) + o Fix the website URL in the readme (f775521) + +COMPILATION FIXES: + o Fix a compilation error with MSVC 2005 due to use of mode_t (336dcae) + o Configure with gcc older than 2.95 (4a6fd43 Sebastian Hahn) + o Generate event-config.h with a single sed script (30b6f88 Zack Weinberg) + +FORWARD-COMPATIBILITY: + o Backport: provide EVENT_LOG_* names, and deprecate _EVENT_LOG_* (d1a03b2) + +TESTING/DEBUGGING SUPPORT: + o dns-example.c can now take a resolv.conf file on the commandline (6610fa5) + o Make some evdns.c debug logs more verbose (d873d67) + o Work-around a stupid gcov-breaking bug in OSX 10.6 (b3887cd) + + + +Changes in version 2.0.18-stable (22 Mar 2012) +BUGFIXES (core): + o Make uses of open() close-on-exec safe by introducing an internal evutil_open_closeonexec. (d2b5f72 Ross Lagerwall, 03dce42) + +BUGFIXES (kqueue): + o Properly zero the kevent in kq_setup_kevent() (c2c7b39 Sebastian Hahn) + +BUILD FIXES: + o Added OPENSSL_LDFLAGS env variable which is appended to SSL checks. (9278196 Mark Ellzey) + o Changed OPENSSL_LDFLAGS to OPENSSL_LIBADD (2d67b63 Mark Ellzey) + o Don't do clang version detection when disabling some flags (083296b Sebastian Hahn) + +BUGFIXES (dns): + o Stop crashing in evdns when nameserver probes give a weird error (bec5068) + + +Changes in version 2.0.17-stable (10 Feb 2012) + +BUGFIXES (core): + o Be absolutely sure to clear pncalls before leaving event_signal_closure (11f36a5) + o check for sysctl before we use it (358c745 Mike Frysinger) + o Remove bogus casts of socket to int before calling ev_callback (f032516) + o Make evconnlistener work around bug in older Linux when getting nmapped (ecfc720) + o Fix a list corruption bug when using event_reinit() with signals present (6e41cdc) + o Fix a fd leak in event_reinit() (3f18ad1) + o Do a memberwise comparison of threading function tables (c94a5f2 Nate R) + o Use C-style comments in C source files (for compatibility with compilers such as xlc on AIX). (d84d917 Greg Hewgill) + o Avoid crash when freeing event_iocp and using event_set_mem_functions (19715a6) + o In the kqueue backend, do not report EBADF as an EV_READ (5d7bfa1 Nicholas Marriott) + +BUGFIXES (evbuffer and bufferevents): + o Fix behavior of evbuffer_peek(buf,-1,NULL,NULL,0) (c986f23 Zack Weinberg) + o Loop on filtering SSL reads until we are blocked or exhausted. (5b4b812) + +BUGFIXES (evhttp): + o Force strict validation of HTTP version in response. (790f6b3 Catalin Patulea) + +BUGFIXES (evdns): + o evdns: fix a bug in circular-queue implementation (d6094b1) + +BUILD FIXES: + o Fix a silly compilation error with the sun compiler (1927776 Colin Watt) + o Suppress a gcc warning from ignoring fwrite return in http-sample.c (7206e8c) + +DOCUMENTATION FIXES: + o Slightly clarify evbuffer_peek documentation (7bbf6ca) + o Update copyright notices to 2012 (e49e289) + +NEW APIS: + o Backport evhttp_connection_get_bufferevent to Libevent 2.0 (da70fa7 Arno Bakker) + +TESTS AND TEST FIXES: + o Fix a race condition in the dns/bufferevent_connect_hostname test. (cba48c7) + o Add function to check referential integrity of an event_base (27737d5) + o Check event_base correctness at end of each unit test (3312b02) + o Workaround in the unit tests for an apparent epoll bug in Linux 3.2 (dab9187) + o Better workaround for Linux 3.2 edge-triggered epoll bug (9f9e259) + +Changes in version 2.0.16-stable (18 Nov 2011) +BUGFIXES (core): + o More detailed message in case of libevent self-debugging failure. (9e6a4ef Leonid Evdokimov) + o epoll: close fd on alloc fail at initialization (1aee718 Jamie Iles) + o Fix compile warning from saying event2/*.h inside a comment (447b0ba) + o Warn when unable to construct base because of failing make_base_notifiable (4e797f3) + o Don't try to make notifiable event_base when no threading fns are configured (e787413) + +BUGFIXES (evbuffer): + o unit test for remove_buffer bug (90bd620 Greg Hazel) + o Fix an evbuffer crash in evbuffer_remove_buffer() (c37069c) + +BUGFIXES (bufferevent_openssl): + o Refactor amount-to-read calculations in buffervent_ssl consider_reading() (a186e73 Mark Ellzey) + o Move SSL rate-limit enforcement into bytes_to_read() (96c562f) + o Avoid spinning on OpenSSL reads (2aa036f Mark Ellzey) + +BUGFIXES (dns) + o Empty DNS reply with OK status is another way to say NODATA. (21a08d6 Leonid Evdokimov) + +TESTING: + o Tests for 94fba5b and f72e8f6 (d58c15e Leonid Evdokimov) + o Test for commit aff6ba1 (f7841bf Leonid Evdokimov) + o Style and comment tweaks for dns/leak* tests (5e42202) + o improve test to remove at least one buffer from src (7eb52eb Greg Hazel) + +DOCUMENTATION: + o Add note about evhttp_send_reply_end to its doxygen (724bfb5) + o Update copyright dates to 2011. (3c824bd) + o Fix typo in whatsnew-2.0.txt (674bc6a Mansour Moufid) + o Improve win32 behavior of dns-sample.c code (a3f320e Gisle Vanem) + + + +Changes in version 2.0.15-stable (12 Oct 2011) +BUGFIXES (DNS): + o DNS: add ttl for negative answers using RFC 2308 idea. (f72e8f6 Leonid Evdokimov) + o Add DNS_ERR_NODATA error code to handle empty replies. (94fba5b Leonid Evdokimov) + +BUFGIXES (bufferevents and evbuffers): + o Make evbuffer callbacks get the right n_added value after evbuffer_add (1ef1f68 Alex) + o Prefer mmap to sendfile unless a DRAINS_TO_FD flag is set. Allows add_file to work with SSL. (0ba0af9) + +BUGFIXES (event loop): + o When a signal callback is activated to run multiple times, allow event_base_loopbreak to work even before they all have run. (4e8eb6a) + +DOCUMENTATION FIXES: + o Fix docstring in dns.h (2b6eae5 Leonid Evdokimov) + o refer to non-deprecated evdns functions in comments (ba5c27d Greg Hazel) + +BUILD AND TESTING FIXES: + o le-proxy and regress depend on openssl directly (9ae061a Sergey Avseyev) + o Use _SOURCES, not _sources, in sample/Makefile.am (7f82382) + o Fixed compiler warnings for unchecked read/write calls. (c3b62fd Mark Ellzey) + o Make write-checking fixes use tt_fail_perror (2b76847) + o Fix some "value never used" warnings with gcc 4.6.1 (39c0cf7) + + + +Changes in version 2.0.14-stable (31 Aug 2011) +BUGFIXES (bufferevents and evbuffers): + o Propagate errors on the underlying bufferevent to the user. (4a34394 Joachim Bauch) + o Ignore OpenSSL deprecation warnings on OS X (5d1b255 Sebastian Hahn) + o Fix handling of group rate limits under 64 bytes of burst (6d5440e) + o Solaris sendfile: correctly detect amount of data sent (643922e Michael Herf) + o Make rate limiting work with common_timeout logic (5b18f13) + o clear read watermark on underlying bufferevent when creating filtering bev to fix potentially failing fragmented ssl handshakes (54f7e61 Joachim Bauch) + +BUGFIXES (IOCP): + o IOCP: don't launch reads or writes on an unconnected socket (495c227) + o Make IOCP rate-limiting group support stricter and less surprising. (a98da7b) + o Have test-ratelim.c support IOCP (0ff2c5a) + o Make overlapped reads result in evbuffer callbacks getting invoked (6acfbdd) + o Correctly terminate IO on an async bufferevent on bufferevent_free (e6af35d) + +BUGFIXES (other): + o Fix evsig_dealloc memory leak with debugging turned on. (9b724b2 Leonid Evdokimov) + o Fix request_finished memory leak with debugging turned on. (aff6ba1 Leonid Evdokimov) + +BUILD AND TESTING FIXES: + o Allow OS-neutral builds for platforms where some versions have arc4random_buf (b442302 Mitchell Livingston) + o Try to fix 'make distcheck' errors when building out-of-tree (04656ea Dave Hart) + o Clean up some problems identified by Coverity. (7c11e51 Harlan Stenn) + + +Changes in version 2.0.13-stable (18 Jul 2011) +BUGFIXES + o Avoid race-condition when initializing global locks (b683cae) + o Fix bug in SSL bufferevents backed by a bev with a write high-watermarks (e050703 Joachim Bauch) + o Speed up invoke_callbacks on evbuffers when there are no callbacks (f87f568 Mark Ellzey) + o Avoid a segfault when all methods are disabled or broken (27ce38b) + o Fix incorrect results from evbuffer_search_eol(EOL_LF) (4461f1a) + o Add some missing checks for mm_calloc failures (89d5e09) + o Replace an assertion for event_base_free(NULL) with a check-and-warn (09fe97d) + o Report kqueue ebadf, epipe, and eperm as EV_READ events (1fd34ab) + o Check if the `evhttp_new_object' function in `http.c' returns NULL. (446cc7a Mansour Moufid) + o Use the correct printf args when formatting size_t (3203f88) + o Complain if the caller tries to change threading cbs after setting them (cb6ecee) + +DOCUMENTATION FIXES AND IMPROVEMENTS + o Revise the event/evbuffer/bufferevent doxygen for clarity and accuracy (2888fac) + o Update Doxyfile to produce more useful output (aea0555) + +TEST FIXES + o Fix up test_evutil_snprintf (caf695a) + o Fix tinytest invocation from windows shell (57def34 Ed Day) + +BUILD FIXES + o Use AM_CPPFLAGS in sample/Makefile.am, not AM_CFLAGS (4a5c82d) + o Fix select.c compilation on systems with no NFDBITS (49d1136) + o Fix a few warnings on OpenBSD (8ee9f9c Nicholas Marriott) + o Don't break when building tests from git without python installed (b031adf) + o Don't install event_rpcgen.py when --disable-libevent-install is used (e23cda3 Harlan Stenn) + o Fix AIX build issue with TAILQ_FOREACH definition (e934096) + + +Changes in version 2.0.12-stable (4 Jun 2011) +BUGFIXES + o Fix a warn-and-fail bug in kqueue by providing kevent() room to report errors (28317a0) + o Fix an assert-inducing fencepost bug in the select backend (d90149d) + o Fix failing http assertion introducd in commit 0d6622e (0848814 Kevin Ko) + o Fix a bug that prevented us from configuring IPv6 nameservers. (74760f1) + o Prevent size_t overflow in evhttp_htmlescape. (06c51cd Mansour Moufid) + o Added several checks for under/overflow conditions in evhttp_handle_chunked_read (a279272 Mark Ellzey) + o Added overflow checks in evhttp_read_body and evhttp_get_body (84560fc Mark Ellzey) + +DOCUMENTATION: + o Add missing words to EVLOOP_NONBLOCK documentation (9556a7d) + +BUILD FIXES + o libssl depends on libcrypto, not the other way around. (274dd03 Peter Rosin) + o Libtool brings in the dependencies of libevent_openssl.la automatically (7b819f2 Peter Rosin) + o Use OPENSSL_LIBS in Makefile.am (292092e Sebastian Hahn) + o Move the win32 detection in configure.in (ceb03b9 Sebastian Hahn) + o Correctly detect openssl on windows (6619385 Sebastian Hahn) + o Fix a compile warning with zlib 1.2.4 and 1.2.5 (5786b91 Sebastian Hahn) + o Fix compilation with GCC 2, which had no __builtin_expect (09d39a1 Dave Hart) + o Fix new warnings from GCC 4.6 (06a714f) + o Link with -lshell32 and -ladvapi32 on Win32. (86090ee Peter Rosin) + o Make the tests build when OpenSSL is not available. (07c41be Peter Rosin) + o Bring in the compile script from automake, if needed. (f3c7a4c Peter Rosin) + o MSVC does not provide S_ISDIR, so provide it manually. (70be7d1 Peter Rosin) + o unistd.h and sys/time.h might not exist. (fe93022 Peter Rosin) + o Make sure TINYTEST_LOCAL is defined when building tinytest.c (8fa030c Peter Rosin) + o Fix winsock2.h #include issues with MSVC (3d768dc Peter Rosin) + o Use evutil_gettimeofday instead of relying on the system gettimeofday. (0de87fe Peter Rosin) + o Always use evutil_snprintf, even if OS provides it (d1b2d11 Sebastian Hahn) + o InitializeCriticalSectionAndSpinCount requires _WIN32_WINNT >= 0x0403. (816115a Peter Rosin) + o cygwin: make it possible to build DLLs (d54d3fc) + + + +Changes in version 2.0.11-stable (27 Apr 2011) + [Autogenerated from the Git log, sorted and cleaned by hand.] +BUGFIXES: + o Fix evport handling of POLLHUP and POLLERR (b42ce4b) + o Fix compilation on Windows with NDEBUG (cb8059d) + o Check for POLLERR, POLLHUP and POLLNVAL for Solaris event ports (0144886 Trond Norbye) + o Detect and handle more allocation failures. (666b096 Jardel Weyrich) + o Use event_err() only if the failure is truly unrecoverable. (3f8d22a Jardel Weyrich) + o Handle resize failures in the select backend better. (83e805a) + o Correctly free selectop fields when select_resize fails in select_init (0c0ec0b) + o Make --enable-gcc-warnings a no-op if not using gcc (3267703) + o Fix a type error in our (unused) arc4random_stir() (f736198) + o Correctly detect and stop non-chunked http requests when the body is too long (63a715e) + o Have event_base_gettimeofday_cached() always return wall-clock time (a459ef7) + o Workaround for http crash bug 3078187 (5dc5662 Tomash Brechko) + o Fix incorrect assertions and possible use-after-free in evrpc_free() (4b8f02f Christophe Fillot) + o Reset outgoing http connection when read data in idle state. (272823f Tomash Brechko) + o Fix subtle recursion in evhttp_connection_cb_cleanup(). (218cf19 Tomash Brechko) + o Fix the case when failed evhttp_make_request() leaved request in the queue. (0d6622e Tomash Brechko) + o Fix a crash bug in evdns server circular list code (00e91b3) + o Handle calloc failure in evdns. (Found by Dave Hart) (364291e) + o Fix a memory leak on win32 socket->event map. (b4f89f0) + o Add a forgotten NULL check to evhttp_parse_headers (12311ff Sebastian Hahn) + o Fix possible NULL-deref in evdns_cancel_request (5208544 Sebastian Hahn) + +PORTABILITY: + o Fall back to sscanf if we have no other way to implement strtoll (453317b) + o Build correctly on platforms without sockaddr_storage (9184563) + o Try to build correctly on platforms with no IPv6 support (713c254) + o Build on systems without AI_PASSIVE (cb92113) + o Fix http unit test on non-windows platforms without getaddrinfo (6092f12) + o Do not check for gethostbyname_r versions if we have getaddrinfo (c1260b0) + o Include arpa/inet.h as needed on HPUX (10c834c Harlan Stenn) + o Include util-internal.h as needed to build on platforms with no sockaddr_storage (bbf5515 Harlan Stenn) + o Check for getservbyname even if not on win32. (af08a94 Harlan Stenn) + o Add -D_OSF_SOURCE to fix hpux builds (0b33479 Harlan Stenn) + o Check for allocation failures in apply_socktype_protocol_hack (637d17a) + o Fix the check for multicast or broadcast addresses in evutil_check_interfaces (1a21d7b) + o Avoid a free(NULL) if out-of-memory in evdns_getaddrinfo. Found by Dave Hart (3417f68) + +DEFENSIVE PROGRAMMING: + o Add compile-time check for AF_UNSPEC==PF_UNSPEC (3c8f4e7) + +BUGS IN TESTS: + o Fix test.sh output on solaris (b4f89b6 Dave Hart) + o Make test-eof fail with a timeout if we never get an eof. (05a2c22 Harlan Stenn) + o Use %s with printf in test.sh (039b9bd) + o Add an assert to appease clang's static analyzer (b0ff7eb Sebastian Hahn) + o Add a forgotten return value check in the unit tests (3819b62 Sebastian Hahn) + o Actually send NULL request in http_bad_request_test (b693c32 Sebastian Hahn) + o add some (void) casts for unused variables (65707d7 Sebastian Hahn) + o Refactor test_getaddrinfo_async_cancel_stress() (48c44a6 Sebastian Hahn) + o Be nice and "handle" error return values in sample code (4bac793 Sebastian Hahn) + o Check return value of evbuffer_add_cb in tests (93a1abb Sebastian Hahn) + o Remote some dead code from dns-example.c (744c745 Sebastian Hahn) + o Zero a struct sockaddr_in before using it (646f9fe Sebastian Hahn) + +BUILD FIXES: + o Fix warnings about AC_LANG_PROGRAM usage (f663112 Sebastian Hahn) + o Skip check for zlib if we have no zlib.h (a317c06 Harlan Stenn) + o Fix autoconf bracket issues; make check for getaddrinfo include netdb.h (833e5e9 Harlan Stenn) + o Correct an AM_CFLAGS to an AM_CPPFLAGS in test/Makefile.am (9c469db Dave Hart) + o Fix make distcheck & installation of libevent 1 headers (b5a1f9f Dave Hart) + o Fix compilation under LLVM/clang with --enable-gcc-warnings (ad9ff58 Sebastian Hahn) + +FEATURES: + o Make URI parser able to tolerate nonconformant URIs. (95060b5) + +DOCUMENTATION: + o Clarify event_set_mem_functions doc (926f816) + o Correct evhttp_del_accept_socket documentation on whether socket is closed (f665924) + o fix spelling mistake in whatsnew-2.0.txt (deb2f73) + o Fix sample/http-server ipv6 fixes (eb692be) + o Comment internal headers used in sample code. (4eb281c) + o Be explicit about how long event loops run in event.h documentation (f95bafb) + o Add comment to configure.in to explain gc-sections test logic (c621359) + o Fix a couple of memory leaks in samples/http-server.c. Found by Dave Hart. (2e9f665) + + + +BUILD IMPROVEMENTS: + Libevent 2.1.2-alpha modernizes Libevent's use of autotools, and makes + numerous other build system. Parallel builds should be faster, and all + builds should be quieter. + + o Split long lists in Makefile.am into one-item-per-line (2711cda) + o Remove unnecessary code in configure.in. (e65914f Ross Lagerwall) + o attempt to support OpenSSL in Makefile.nmake (eba0eb2 Patrick Pelletier) + o Use newer syntax for autoconf/automake init (7d60ba8) + o Enable silent build rules by default. Override with V=1 (7b18e5c) + o Switch to non-recursive makefiles (7092f3b) + o Rename subordinate Makefile.ams to include.am (6cdfeeb) + o Make quiet build even quieter (371a123) + o New --quiet option for event_rpcgen.py (aa59c1e) + o Be quiet when making regress.gen.[ch] (607a8ff) + o Fix handling of no-python case for nonrecursive make (1e3123d) + o We now require automake 1.9 or later. Modernize! (b7f6e89) + o Rename configure.in to configure.ac. (b3fea67 Ross Lagerwall) + o Use correct openssl libs and includes in pkgconfig file (d70af27) + o Use the same CFLAGS for openssl when building unit tests as with + libevent (1d9d511) + +DOCUMENTATION + o Note that make_base_notifiable should not be necessary (26ee5f9) + o Be more clear that LEV_OPT_DEFERRED_ACCEPT has tricky prereqs (371efeb) + o Add caveat to docs about bufferevent_free() with data in outbuf (6fab9ee) + o Make it more clear that NOLOCK means "I promise, no multithreading" + (9444524) + o Fix a comment in test-fdleak after 077c7e949. (3881d8f Ross Lagerwall) + o Make the Makefile.nmake warning slightly less dire (e7bf4c8) + o Fix typo : events instead of evets (05f1aca Azat Khuzhin) + o Additional comments about OPENSSL_DIR variable, prompted by Dave Hart + (6bde2ef Patrick Pelletier) + +EVHTTP: + o ignore LWS after field-content in headers (370a2c0 Artem Germanov) + o Clean up rtrim implementation (aa59d80) + o Remove trailing tabs in HTTP headers as well. (ac42519) + o Remove internal ws from multiline http headers correctly (c6ff381) + o Move evutil_rtrim_lws_ to evutil.c where it belongs (61b93af) + o add evhttp_request_get_response_code_line (4f4d0c9 Jay R. Wren) + o Use EVUTIL_SOCKET_ERROR() wrapper to save/restore errno in + evhttp_connection_fail_ (7afbd60) + o preserve errno in evhttp_connection_fail_ for inspection by the + callback (36d0ee5 Patrick Pelletier) + +BUGFIXES: + o Correctly handle running on a system where accept4 doesn't work. (9fbfe9b) + o Avoid double-free on error in evbuffer_add_file. Found by + coverity. (6a81b1f) + o Fix another possible uninitialized read in dns regression tests. Found + by coverity. (13525c5) + o Add checks for functions in test-ratelim.c; found by Coverity (aa501e1) + o Avoid memory leak in test_event_calloc unit test; found by coverity + (92817a1) + o Fix a shadowed variable in addfile_test_readcb; found by coverity + (225344c) + o Check return value when using LEV_OPT_DEFERRED_ACCEPT. Found by + coverity (6487f63) + o Prevent reference leak of bufferevent if getaddrinfo fails. (b757786 + Joachim Bauch) + o Make event_base_getnpriorities work with old "implicit base" code + (c46cb9c) + o Simplify and correct evutil_open_closeonexec_ (0de587f) + o Fix event_dlist definition when sys/queue not included (81b6209 + Derrick Pallas) + + + +Changes in version 2.1.1-alpha (4 Apr 2012) + + Libevent 2.1.1-alpha includes a number of new features and performance + improvements. The log below tries to organize them by rough area of + effect. It omits some commits which were pure bugfixes on other commits + listed below. For more detail, see the git changelogs. For more + insight, see the "whatsnew-2.1.txt" document included in the Libevent + 2.1.1-alpha distribution. + + Performance: Core + o Replace several TAILQ users with LIST. LIST can be a little faster than + TAILQ for cases where we don't need queue-like behavior. (f9db33d, + 6494772, d313c29, 974d004) + o Disabled code to optimize the case where we reinsert an existing + timeout (e47042f, 09cbc3d) + o Remove a needless base-notify when rescheduling the first timeout (77a96fd) + o Save a needless comparison when removing/adjusting timeouts (dd5189b) + o Possible optimization: split event_queue_insert/remove into + separate functions. needs testing (efc4dc5) + o Make event_count maintenance branchless at the expense of an + extra shift. Needs benchmarking (d1cee3b) + o In the 2.1 branch, let's try out lazy gettimeofday/clock_gettime + comparison (2a83ecc) + o Optimization in event_process_active(): ignore maxcb & endtime + for highest priority events. (a9866aa Alexander Drozdov) + o Bypass event_add when using event_base_once() for a 0-sec timeout (35c5c95) + o Remove the eventqueue list and the ev_next pointers. (604569b 066775e) + + Performance: Evbuffers + o Roughly 20% speed increase when line-draining a buffer using + EVBUFFER_EOL_CRLF (5dde0f0 Mina Naguib) + o Try to squeeze a little more speed out of EVBUFFER_EOL_CRLF (7b9d139) + o Fix a bug in the improved EOL_CRLF code (d927965) + o Remove a needless branch in evbuffer_drain() (d19a326) + + Performance: Linux + o Infrastructure for using faster/fewer syscalls when creating + sockets (a1c042b) + o Minimize syscalls during socket creation in listener.c (7e9e289) + o Use a wrapper function to create the notification + pipe/socketpair/eventfd (ca76cd9) + o Use pipes for telling signals to main thread when possible (a35f396) + o Save syscalls when constructing listener sockets for evhttp (af6c9d8) + o Save some syscalls when creating evdns sockets (713e570) + o Save some syscalls when constructing a socket for a bufferevent (33fca62) + o Prefer epoll_create1 on Linuxen that have it (bac906c) + + Performance: Epoll backend + o Use current event set rather than current pending change when + deciding whether to no-op a del (04ba27e Mike Smellie) + o Replace big chain of if/thens in epoll.c with a table lookup (8c83eb6) + o Clean up error handling in epoll_apply_one_change() a little (2d55a19) + + Performance: Evport backend + o evport: use evmap_io to track fdinfo status. Should save time and + RAM. (4687ce4) + o evport: Remove a linear search over recent events when + reactivating them (0f77efe) + o evport: Use portev_user to remember fdinfo struct (276ec0e) + o evport: don't scan more events in ed_pending than needed (849a5cf) + o evport: Remove artificial low limit on max events per getn call (c04d927) + o Reenable main/many_events_slow_add for evport in 2.1 (e903db3) + + Performance: Windows + o Use GetSystemTimeAsFileTime to implement gettimeofday on + win32. It's faster and more accurate than our old + approach. (b8b8aa5) + + New functions and features: debugging + o Add event_enable_debug_logging() to control use of debug logs (e30a82f) + + New functions and features: core + o Add event_config function to limit time/callbacks between calls + to dispatch (fd4de1e, 9fa56bd, a37a0c0, 3c63edd) + o New EVLOOP_NO_EXIT_ON_EMPTY option to keep looping even when no + events are pending (084e68f) + o Add event_base_get_npriorities() function. (ee3a4ee Alexander Drozdov) + o Make evbase_priority_init() and evbase_get_npriorities() + threadsafe (3c55b5e) + o New event_base_update_cache_time() to set cached_tv to current + time (212533e Abel Mathew) + o Add event_self_cbarg() to be used in conjunction with + event_new(). (ed36e6a Ross Lagerwall, fa931bb, 09a1906, 1338e6c, + 33e43ef) + o Add a new libevent_global_shutdown() to free all globals before + exiting. (041ca00 Mark Ellzey, f98c158, 15296d0, 55e991b) + o Use getifaddrs to detect our interfaces if possible (7085a45) + o Add event_base_get_running_event() to get the event* whose cb we + are in (c5732fd, 13dad99) + + New functions and features: building + o Implement --enable-gcc-hardening configure option (7550267 Sebastian Hahn) + + New functions and features: evbuffers + o Add evbuffer_add_file_segment() so one fd can be used efficiently + in more than one evbuffer_add_file at a time (e72afae, c2d9884, + 3f405d2, 0aad014) + o Fix windows file segment mappings (8254de7) + o Allow evbuffer_ptr_set to yield a point just after the end of the + buffer. (e6fe1da) + o Allow evbuffer_ptr to point to position 0 in an empty evbuffer + (7aeb2fd Nir Soffer) + o Set the special "not found" evbuffer_ptr consistently. (e3e97ae Nir Soffer) + o support adding buffers to other buffers non-destructively + (9d7368a Joachim Bauch) + o prevent nested multicast references, reworked locking (26041a8 + Joachim Bauch) + o New EVBUFFER_EOL_NUL to read NUL-terminated strings from an + evbuffer (d7a8b36 Andrea Montefusco, 54142c9) + o Make evbuffer_file_segment_types adaptable (c6bbbf1) + o Added evbuffer_add_iovec and unit tests. (aaec5ac Mark Ellzey, 27b5398) + o Add evbuffer_copyout_from to copy data from the middle of a + buffer (27e2225) + + New functions and features: bufferevents + o Allow users to set allow_dirty_shutdown (099d27d Catalin Patulea) + o Tweak allow_dirty_shutdown documentation (a44cd2b) + o Fix two issues in the allow_dirty_shutdown code. (f3b89de) + o Add a bufferevent_getcb() to find a bufferevent's current + callbacks (a650394) + o bufferevent: Add functions to set/get max_single_read/write + values. (998c813 Alexander Drozdov) + o bev_ssl: Be more specific in event callbacks. evhttp in particular gets + confused without at least one of BEV_EVENT_{READING|WRITING}. (f7eb69a + Catalin Patulea) + + New functions and features: evconnlisteners + o Support TCP_DEFER_ACCEPT sockopts for listeners (5880e4a Mark Ellzey, + a270728) + o Add another caveat to the TCP_DEFER_ACCEPT documentation (a270728) + o Allow evconnlistener to be created in disabled state. (9593a33 + Alexander Drozdov) + o The LEV_OPT_CLOSE_ON_EXEC flag now applies to accepted listener + sockets too (4970329) + + Evhttp: + o Add new evhttp_{connection_}set_timeout_tv() functions to set + finger-grained http timeouts (6350e6c Constantine Verutin) + o Performance tweak to evhttp_parse_request_line. (aee1a97 Mark Ellzey) + o Add missing break to evhttp_parse_request_line (0fcc536) + o Add evhttp callback for bufferevent creation; this lets evhttp + support SSL. (8d3a850) + o Remove calls to deprecated bufferevent functions from evhttp.c (4d63758) + o evhttp: Add evhttp_foreach_bound_socket. (a2c48e3 Samy Al Bahra) + + Build improvements: + o Add AC_USE_SYSTEM_EXTENSIONS to configure.in. Requires follow on + patches for correctness and robustness. (1fa7dbe Kevin Bowling) + o Filter '# define' statements from autoconf and generate + event-private.h (321b558 Kevin Bowling) + o Remove internal usage of _GNU_SOURCE (3b26541 Kevin Bowling) + o Eliminate a couple more manual internal _GNU_SOURCE defines (c51ef93 + Kevin Bowling) + o Add AC_GNU_SOURCE to the fallback case. (ea8fa4c Kevin Bowling) + o Use a Configuration Header Template for evconfig-private.h (868f888 + Kevin Bowling) + o Fix a comment warning and add evconfig-private.h to .gitignore + (f6d66bc Kevin Bowling) + o Include evconfig-private.h in internal files for great good. (0915ca0 + Kevin Bowling) + o Backport libevent to vanilla Autoconf 2.59 (as used in RHEL5) + (ad03952 Kevin Bowling) + o Prefer the ./configure evconfig-private.h in MinGW, just in + case. (f964b72 Kevin Bowling) + o Shell hack for weird mkdir -p commands (fd7b5a8 Kevin Bowling) + o Add evconfig-private to remaining files (ded0a09 Kevin Bowling) + o Allow use of --enable-silent-rules for quieter compilation with + automake 1.11 (f1f8514 Dave Hart) + o Use "_WIN32", not WIN32: it's standard and we don't need to fake it + (9f560b) + o In configure, test for _WIN32 not WIN32. (85078b1 Peter Rosin) + o Do not define WIN32 in Makefile.nmake (d41f3ea Peter Rosin) + o Provide the autoconf m4 macros for the new OpenSSL via pkg-config + stuff. (674dc3d Harlan Stenn) + o Use pkg-config (if available) to handle OpenSSL. (1c63860 Harlan Stenn) + o We need AM_CPPFLAGS when compiling bufferevent_openssl.c (6d2613b + Harlan Stenn) + o Fix OSX build: $(OPENSSL_INCS) needs to be after + $(AM_CPPFLAGS). (46f1769 Zack Weinberg) + o Make gcc warnings on by default, and --enable-gcc-warnings only add + -Werror (d46517e Sebastian Hahn) + o Split up extra-long AC_CHECK_FUNCS/HEADERS lines in configure.in (88a30ad) + o Move libevent 1.x headers to include/, to put all public headers in + one place. (bbea8d6) + o Put #ifdef around some files to support alternate build + systems. (76d4c92 Ross Lagerwall) + o Also make win32select.c conditional for IDE users (bf2c5a7) + + Debugging: + o Add a magic number to debug_locks to better catch lock-coding + errors. (b4a29c0 Dave Hart) + o munge the debug_lock signature before freeing it: it might help us + catch use-after-free (f28084d) + o Added --enable-event-debugging in configure (bc7b4e4, a9c2c9a Mark Ellzey) + o Debug addition for printing usec on TIMEOUT debugging. (ac43ce0 Mark Ellzey) + o Added usec debug in another area for debug (3baab0d Mark Ellzey) + o added timeout debug logs to include event ptr. (4b7d298 Mark Ellzey) + o more event dbg updates (6727543 Mark Ellzey) + o Clarify event_enable_debug_logging a little (6207826) + o Make --enable-verbose-debug option match its help text (10c3450) + o Add argument checks to some memory functions in `event.c'. (c8953d1 + Mansour Moufid) + + Testing: + o More abstraction in test.sh (cd74c4e) + o Add failing test for evbuffer_search_range. (8e26154 Nir Soffer) + o Tweaks to return types with end-of-buf ptrs (9ab8ab8) + o Add an (internal) usleep function for use by unit tests (f25d9d3) + o Synchronize with upstream tinytest (6c81be7) + o Make test-changelist faster (7622d26) + o Reduce the timeout in the main/fork test. (ab14f7c) + o New evhttp function to adjust initial retry timeout (350a3c4) + o Make regression tests run over 3x faster. (67a1763) + o Use test_timeval_diff_eq more consistently (b77b43f) + o Allow more slop in deferred_cb_skew test; freebsd needs it (b9f7e5f) + o When including an -internal.h header outside the main tree, do so + early (95e2455) + o Add a new test: test-fdleak which tests for fd leaks by creating many + sockets. (2ef9278 Ross Lagerwall, f7af194, 1c4288f, etc) + o Add a unit test for event_base_dump_events() (7afe48a, 8d08cce) + o Test more bufferevent_ratelim features (c24f91a) + + Documentation: + o Improve evbuffer_ptr documentation (261ba63) + o added comments to describe refcounting of multicast chains (ba24f61 + Joachim Bauch) + o Add doxygen for event_base_dump_events (cad5753) + + OSX: + o Use "unlimited select" on OSX so that we can have more than + FD_SETSIZE fds (1fb5cc6) + + KQueue: + o Use SIG_IGN instead of a do-nothing handler for signal events with + kqueue (148458e Zack Weinberg) + + evprc: + o event_rpcgen.py now prints status information to stdout and errors to + stderr. (ffb0ba0 Ross Lagerwall) + + Code improvement and refactoring: + o Make event_reinit() more robust and maintainable (272033e) + o Restore fast-path event_reinit() for slower backends (2c4b5de) + o Check changelist as part of checking representational integrity (39b3f38) + o Fix a compile warning in event_reinit (e4a56ed Sebastian Hahn) + o Refactor the functions that run over every event. (c89b4e6) + o Remove the last vestiges of _EVENT_USE_EVENTLIST (a3cec90) + o Make event-config.h depend on Makefile.am (2958a5c) + + Build fixes: + o Don't do clang version detection when disabling some flags (083296b + Sebastian Hahn) + + C standards conformance: + o Check for NULL return on win32 mm_calloc, and set ENOMEM. (af7ba69) + o Convert event-config.h macros to avoid reserved identifiers (68120d9) + o Generate event-config.h using the correct macros. (f82c57e) + o Convert include-guard macro convention to avoid reserved identifiers + (3f8c7cd) + o Make event_rpcgen.py output conform to identifier conventions (372bff1) + o Stop referring to an obsolete include guard in bench_http.h (5c0f7e0) + o Make the generated event-config.h use correct include guards (639383a) + o Fix all identifiers with names beginning with underscore. (cb9da0b) + o Make event_rpcgen.py output conform to identifier conventions, more + (bcefd24) + o Fix some problems introduced by automated identifier cleanup script + (c963534) + o Have all visible internal function names end with an underscore. (8ac3c4c) + o Apply the naming convention to our EVUTIL_IS* functions (c7848fa) + o Clean up lingering _identifiers. (946b584) + o Fix doxygen to use new macro conventions (da455e9) + + Bugfixes: + o Do not use system EAI/AI values if we are not using the system + getaddrinfo. (7bcac07) + + Sample Code: + o Fix up sample/event-test.c to use newer interfaces and make it + actually work. (19bab4f Ross Lagerwall) + o On Unix, remove event.fifo left by sample/event-test.c. (c0dacd2 Ross + Lagerwall) + o Rename event-test.c to event-read-fifo.c. (a5b370a Ross Lagerwall) + o event-read-fifo: Use EV_PERSIST appropriately (24dab0b) + + + + diff --git a/libs/libevent/docs/ChangeLog-1.4 b/libs/libevent/docs/ChangeLog-1.4 new file mode 100644 index 0000000000..166d30872f --- /dev/null +++ b/libs/libevent/docs/ChangeLog-1.4 @@ -0,0 +1,231 @@ +Changes in 1.4.14b-stable + o Set the VERSION_INFO correctly for 1.4.14 + +Changes in 1.4.14-stable + o Add a .gitignore file for the 1.4 branch. (d014edb) + o Backport evbuffer_readln(). (b04cc60 Nicholas Marriott) + o Make the evbuffer_readln backport follow the current API (c545485) + o Valgrind fix: Clear struct kevent before checking for OSX bug. (5713d5d William Ahern) + o Fix a crash when reading badly formatted resolve.conf (5b10d00 Yasuoka Masahiko) + o Fix memory-leak of signal handler array with kqueue. [backport] (01f3775) + o Update sample/signal-test.c to use newer APIs and not leak. (891765c Evan Jones) + o Correct all versions in 1.4 branch (ac0d213) + o Make evutil_make_socket_nonblocking() leave any other flags alone. (81c26ba Jardel Weyrich) + o Adjusted fcntl() retval comparison on evutil_make_socket_nonblocking(). (5f2e250 Jardel Weyrich) + o Correct a debug message in evhttp_parse_request_line (35df59e) + o Merge branch 'readln-backport' into patches-1.4 (8771d5b) + o Do not send an HTTP error when we've already closed or responded. (4fd2dd9 Pavel Plesov) + o Re-add event_siglcb; some old code _was_ still using it. :( (bd03d06) + o Make Libevent 1.4 build on win32 with Unicode enabled. (bce58d6 Brodie Thiesfield) + o Distribute nmake makefile for 1.4 (20d706d) + o do not fail while sending on http connections the client closed. (5c8b446) + o make evhttp_send() safe against terminated connections, too (01ea0c5) + o Fix a free(NULL) in min_heap.h (2458934) + o Fix memory leak when setting up priorities; reported by Alexander Drozdov (cb1a722) + o Clean up properly when adding a signal handler fails. (ae6ece0 Gilad Benjamini) + o Do not abort HTTP requests missing a reason string. (29d7b32 Pierre Phaneuf) + o Fix compile warning in http.c (906d573) + o Define _REENTRANT as needed on Solaris, elsewhere (6cbea13) + + +Changes in 1.4.13-stable: + o If the kernel tells us that there are a negative number of bytes to read from a socket, do not believe it. Fixes bug 2841177; found by Alexander Pronchenkov. + o Do not allocate the maximum event queue and fd array for the epoll backend at startup. Instead, start out accepting 32 events at a time, and double the queue's size when it seems that the OS is generating events faster than we're requesting them. Saves up to 512K per epoll-based event_base. Resolves bug 2839240. + o Fix compilation on Android, which forgot to define fd_mask in its sys/select.h + o Do not drop data from evbuffer when out of memory; reported by Jacek Masiulaniec + o Rename our replacement compat/sys/_time.h header to avoid build a conflict on HPUX; reported by Kathryn Hogg. + o Build kqueue.c correctly on GNU/kFreeBSD platforms. Patch pulled upstream from Debian. + o Fix a problem with excessive memory allocation when using multiple event priorities. + o When running set[ug]id, don't check the environment. Based on a patch from OpenBSD. + + +Changes in 1.4.12-stable: + o Try to contain degree of failure when running on a win32 version so heavily firewalled that we can't fake a socketpair. + o Fix an obscure timing-dependent, allocator-dependent crash in the evdns code. + o Use __VA_ARGS__ syntax for varargs macros in event_rpcgen when compiler is not GCC. + o Activate fd events in a pseudorandom order with O(N) backends, so that we don't systematically favor low fds (select) or earlier-added fds (poll, win32). + o Fix another pair of fencepost bugs in epoll.c. [Patch from Adam Langley.] + o Do not break evdns connections to nameservers when our IP changes. + o Set truncated flag correctly in evdns server replies. + o Disable strict aliasing with GCC: our code is not compliant with it. + +Changes in 1.4.11-stable: + o Fix a bug when removing a timeout from the heap. [Patch from Marko Kreen] + o Remove the limit on size of HTTP headers by removing static buffers. + o Fix a nasty dangling pointer bug in epoll.c that could occur after epoll_recalc(). [Patch from Kevin Springborn] + o Distribute Win32-Code/event-config.h, not ./event-config.h + +Changes in 1.4.10-stable: + o clean up buffered http connection data on reset; reported by Brian O'Kelley + o bug fix and potential race condition in signal handling; from Alexander Drozdov + o rename the Solaris event ports backend to evport + o support compilation on Haiku + o fix signal processing when a signal callback delivers a signal; from Alexander Drozdov + o const-ify some arguments to evdns functions. + o off-by-one error in epoll_recalc; reported by Victor Goya + o include Doxyfile in tar ball; from Jeff Garzik + o correctly parse queries with encoded \r, \n or + characters + +Changes in 1.4.9-stable: + o event_add would not return error for some backends; from Dean McNamee + o Clear the timer cache on entering the event loop; reported by Victor Chang + o Only bind the socket on connect when a local address has been provided; reported by Alejo Sanchez + o Allow setting of local port for evhttp connections to support millions of connections from a single system; from Richard Jones. + o Clear the timer cache when leaving the event loop; reported by Robin Haberkorn + o Fix a typo in setting the global event base; reported by lance. + o Fix a memory leak when reading multi-line headers + o Fix a memory leak by not running explicit close detection for server connections + +Changes in 1.4.8-stable: + o Match the query in DNS replies to the query in the request; from Vsevolod Stakhov. + o Fix a merge problem in which name_from_addr returned pointers to the stack; found by Jiang Hong. + o Do not remove Accept-Encoding header + +Changes in 1.4.7-stable: + o Fix a bug where headers arriving in multiple packets were not parsed; fix from Jiang Hong; test by me. + +Changes in 1.4.6-stable: + o evutil.h now includes <stdarg.h> directly + o switch all uses of [v]snprintf over to evutil + o Correct handling of trailing headers in chunked replies; from Scott Lamb. + o Support multi-line HTTP headers; based on a patch from Moshe Litvin + o Reject negative Content-Length headers; anonymous bug report + o Detect CLOCK_MONOTONIC at runtime for evdns; anonymous bug report + o Fix a bug where deleting signals with the kqueue backend would cause subsequent adds to fail + o Support multiple events listening on the same signal; make signals regular events that go on the same event queue; problem report by Alexander Drozdov. + o Deal with evbuffer_read() returning -1 on EINTR|EAGAIN; from Adam Langley. + o Fix a bug in which the DNS server would incorrectly set the type of a cname reply to a. + o Fix a bug where setting the timeout on a bufferevent would take not effect if the event was already pending. + o Fix a memory leak when using signals for some event bases; reported by Alexander Drozdov. + o Add libevent.vcproj file to distribution to help with Windows build. + o Fix a problem with epoll() and reinit; problem report by Alexander Drozdov. + o Fix off-by-one errors in devpoll; from Ian Bell + o Make event_add not change any state if it fails; reported by Ian Bell. + o Do not warn on accept when errno is either EAGAIN or EINTR + +Changes in 1.4.5-stable: + o Fix connection keep-alive behavior for HTTP/1.0 + o Fix use of freed memory in event_reinit; pointed out by Peter Postma + o Constify struct timeval * where possible; pointed out by Forest Wilkinson + o allow min_heap_erase to be called on removed members; from liusifan. + o Rename INPUT and OUTPUT to EVRPC_INPUT and EVRPC_OUTPUT. Retain INPUT/OUTPUT aliases on on-win32 platforms for backwards compatibility. + o Do not use SO_REUSEADDR when connecting + o Fix Windows build + o Fix a bug in event_rpcgen when generated fixed-sized entries + +Changes in 1.4.4-stable: + o Correct the documentation on buffer printf functions. + o Don't warn on unimplemented epoll_create(): this isn't a problem, just a reason to fall back to poll or select. + o Correctly handle timeouts larger than 35 minutes on Linux with epoll.c. This is probably a kernel defect, but we'll have to support old kernels anyway even if it gets fixed. + o Fix a potential stack corruption bug in tagging on 64-bit CPUs. + o expose bufferevent_setwatermark via header files and fix high watermark on read + o fix a bug in bufferevent read water marks and add a test for them + o introduce bufferevent_setcb and bufferevent_setfd to allow better manipulation of bufferevents + o use libevent's internal timercmp on all platforms, to avoid bugs on old platforms where timercmp(a,b,<=) is buggy. + o reduce system calls for getting current time by caching it. + o fix evhttp_bind_socket() so that multiple sockets can be bound by the same http server. + o Build test directory correctly with CPPFLAGS set. + o Fix build under Visual C++ 2005. + o Expose evhttp_accept_socket() API. + o Merge windows gettimeofday() replacement into a new evutil_gettimeofday() function. + o Fix autoconf script behavior on IRIX. + o Make sure winsock2.h include always comes before windows.h include. + +Changes in 1.4.3-stable: + o include Content-Length in reply for HTTP/1.0 requests with keep-alive + o Patch from Tani Hosokawa: make some functions in http.c threadsafe. + o Do not free the kqop file descriptor in other processes, also allow it to be 0; from Andrei Nigmatulin + o make event_rpcgen.py generate code include event-config.h; reported by Sam Banks. + o make event methods static so that they are not exported; from Andrei Nigmatulin + o make RPC replies use application/octet-stream as mime type + o do not delete uninitialized timeout event in evdns + +Changes in 1.4.2-rc: + o remove pending timeouts on event_base_free() + o also check EAGAIN for Solaris' event ports; from W.C.A. Wijngaards + o devpoll and evport need reinit; tested by W.C.A Wijngaards + o event_base_get_method; from Springande Ulv + o Send CRLF after each chunk in HTTP output, for compliance with RFC2626. Patch from "propanbutan". Fixes bug 1894184. + o Add a int64_t parsing function, with unit tests, so we can apply Scott Lamb's fix to allow large HTTP values. + o Use a 64-bit field to hold HTTP content-lengths. Patch from Scott Lamb. + o Allow regression code to build even without Python installed + o remove NDEBUG ifdefs from evdns.c + o update documentation of event_loop and event_base_loop; from Tani Hosokawa. + o detect integer types properly on platforms without stdint.h + o Remove "AM_MAINTAINER_MODE" declaration in configure.in: now makefiles and configure should get re-generated automatically when Makefile.am or configure.in chanes. + o do not insert event into list when evsel->add fails + +Changes in 1.4.1-beta: + o free minheap on event_base_free(); from Christopher Layne + o debug cleanups in signal.c; from Christopher Layne + o provide event_base_new() that does not set the current_base global + o bufferevent_write now uses a const source argument; report from Charles Kerr + o better documentation for event_base_loopexit; from Scott Lamb. + o Make kqueue have the same behavior as other backends when a signal is caught between event_add() and event_loop(). Previously, it would catch and ignore such signals. + o Make kqueue restore signal handlers correctly when event_del() is called. + o provide event_reinit() to reintialize an event_base after fork + o small improvements to evhttp documentation + o always generate Date and Content-Length headers for HTTP/1.1 replies + o set the correct event base for HTTP close events + o New function, event_{base_}loopbreak. Like event_loopexit, it makes an event loop stop executing and return. Unlike event_loopexit, it keeps subsequent pending events from getting executed. Patch from Scott Lamb + o Removed obsoleted recalc code + o pull setters/getters out of RPC structures into a base class to which we just need to store a pointer; this reduces the memory footprint of these structures. + o fix a bug with event_rpcgen for integers + o move EV_PERSIST handling out of the event backends + o support for 32-bit tag numbers in rpc structures; this is wire compatible, but changes the API slightly. + o prefix {encode,decode}_tag functions with evtag to avoid collisions + o Correctly handle DNS replies with no answers set (Fixes bug 1846282) + o The configure script now takes an --enable-gcc-warnigns option that turns on many optional gcc warnings. (Nick has been building with these for a while, but they might be useful to other developers.) + o When building with GCC, use the "format" attribute to verify type correctness of calls to printf-like functions. + o removed linger from http server socket; reported by Ilya Martynov + o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr. + o demote most http warnings to debug messages + o Fix Solaris compilation; from Magne Mahre + o Add a "Date" header to HTTP responses, as required by HTTP 1.1. + o Support specifying the local address of an evhttp_connection using set_local_address + o Fix a memory leak in which failed HTTP connections would not free the request object + o Make adding of array members in event_rpcgen more efficient, but doubling memory allocation + o Fix a memory leak in the DNS server + o Fix compilation when DNS_USE_OPENSSL_FOR_ID is enabled + o Fix buffer size and string generation in evdns_resolve_reverse_ipv6(). + o Respond to nonstandard DNS queries with "NOTIMPL" rather than by ignoring them. + o In DNS responses, the CD flag should be preserved, not the TC flag. + o Fix http.c to compile properly with USE_DEBUG; from Christopher Layne + o Handle NULL timeouts correctly on Solaris; from Trond Norbye + o Recalculate pending events properly when reallocating event array on Solaris; from Trond Norbye + o Add Doxygen documentation to header files; from Mark Heily + o Add a evdns_set_transaction_id_fn() function to override the default + transaction ID generation code. + o Add an evutil module (with header evutil.h) to implement our standard cross-platform hacks, on the theory that somebody else would like to use them too. + o Fix signals implementation on windows. + o Fix http module on windows to close sockets properly. + o Make autogen.sh script run correctly on systems where /bin/sh isn't bash. (Patch from Trond Norbye, rewritten by Hagne Mahre and then Hannah Schroeter.) + o Skip calling gettime() in timeout_process if we are not in fact waiting for any events. (Patch from Trond Norbye) + o Make test subdirectory compile under mingw. + o Fix win32 buffer.c behavior so that it is correct for sockets (which do not like ReadFile and WriteFile). + o Make the test.sh script run unit tests for the evpoll method. + o Make the entire evdns.h header enclosed in "extern C" as appropriate. + o Fix implementation of strsep on platforms that lack it + o Fix implementation of getaddrinfo on platforms that lack it; mainly, this will make Windows http.c work better. Original patch by Lubomir Marinov. + o Fix evport implementation: port_disassociate called on unassociated events resulting in bogus errors; more efficient memory management; from Trond Norbye and Prakash Sangappa + o support for hooks on rpc input and output; can be used to implement rpc independent processing such as compression or authentication. + o use a min heap instead of a red-black tree for timeouts; as a result finding the min is a O(1) operation now; from Maxim Yegorushkin + o associate an event base with an rpc pool + o added two additional libraries: libevent_core and libevent_extra in addition to the regular libevent. libevent_core contains only the event core whereas libevent_extra contains dns, http and rpc support + o Begin using libtool's library versioning support correctly. If we don't mess up, this will more or less guarantee binaries linked against old versions of libevent continue working when we make changes to libevent that do not break backward compatibility. + o Fix evhttp.h compilation when TAILQ_ENTRY is not defined. + o Small code cleanups in epoll_dispatch(). + o Increase the maximum number of addresses read from a packet in evdns to 32. + o Remove support for the rtsig method: it hasn't compiled for a while, and nobody seems to miss it very much. Let us know if there's a good reason to put it back in. + o Rename the "class" field in evdns_server_request to dns_question_class, so that it won't break compilation under C++. Use a macro so that old code won't break. Mark the macro as deprecated. + o Fix DNS unit tests so that having a DNS server with broken IPv6 support is no longer cause for aborting the unit tests. + o Make event_base_free() succeed even if there are pending non-internal events on a base. This may still leak memory and fds, but at least it no longer crashes. + o Post-process the config.h file into a new, installed event-config.h file that we can install, and whose macros will be safe to include in header files. + o Remove the long-deprecated acconfig.h file. + o Do not require #include <sys/types.h> before #include <event.h>. + o Add new evutil_timer* functions to wrap (or replace) the regular timeval manipulation functions. + o Fix many build issues when using the Microsoft C compiler. + o Remove a bash-ism in autogen.sh + o When calling event_del on a signal, restore the signal handler's previous value rather than setting it to SIG_DFL. Patch from Christopher Layne. + o Make the logic for active events work better with internal events; patch from Christopher Layne. + o We do not need to specially remove a timeout before calling event_del; patch from Christopher Layne. diff --git a/libs/libevent/docs/ChangeLog-2.0 b/libs/libevent/docs/ChangeLog-2.0 new file mode 100644 index 0000000000..a925d33b18 --- /dev/null +++ b/libs/libevent/docs/ChangeLog-2.0 @@ -0,0 +1,1280 @@ +Changes in version 2.0.21-stable (18 Nov 2012) +BUGFIXES: + o ssl: Don't discard SSL read event when timeout and read come close together (576b29f) + o ssl: Stop looping in "consider_reading" if reading is suspended. (f719b8a Joachim Bauch) + o ssl: No need to reserve space if reading is suspended. (1acf2eb Joachim Bauch) + o dns: Avoid a memory-leak on OOM in evdns. (73e85dd, f2bff75 George Danchev) + o build: Use python2 rather than python (0eb0109 Ross Lagerwall) + o build: Compile without warnings on mingw64 (94866c2) + o build: Fix compilation on mingw64 with -DUSE_DEBUG (62bd2c4) + o build: Make rpcgen_wrapper.sh work on systems without a "python2" binary (f3009e4) + o iocp: Close IOCP listener socket on free when LEV_OPT_CLOSE_ON_FREE is set (cb853ea Juan Pablo Fernandez) + o core: Avoid crash when event_pending() called with no event_base set on event (e3cccf3) + o misc: remove stray 'x' so print_err will compile when uncommented (ac35650 Patrick Pelletier) + o tests: Fix renegotiation test to work around openssl 1.0.1 bug (c2f3086) + o tests: Warn when openssl version in unit test mismatches compiled version. (ac009f9) + + +Changes in version 2.0.20-stable (23 Aug 2012) +BUGFIXES: + o core: Make event_pending() threadsafe. (be7a95c Simon Liu) + o win32: avoid crash when waiting forever on zero fds. (160e58b) + o evhttp: Fix a memory leak on error in evhttp_uriencode (11c8b31) + o evbuffer: Avoid possible needless call to writev. Found by coverity. (6a4ec5c) + o evdns: memset sockaddr_in before using it. Found by coverity. (a1a0e67) + o evhttp: Check more setsockopt return values when binding sockets. Found by coverity (a0912e3) + o evdns: Avoid segfault on weird timeout during name lookup. (dc32077 Greg Hazel) + o bufferevent_ssl: Correctly invoke callbacks when a SSL bufferevent reads some and then blocks. (606ac43) + + +PORTABILITY FIXES: + o check for arc4random_buf at runtime, on OS X (bff5f94 Greg Hazel) + o Correctly check for arc4random_buf (fcec3e8 Sebastian Hahn) + o Add explicit AC_PROG_SED to configure.in so all autoconfs will expose $(SED) (ca80ea6) + +BUILD FIXES: + o Add GCC annotations so that the vsprintf functions get checked properly (117e327) + o Fix an unused variable warning on *BSD. (c0720c1) + +UNIT TEST FIXES: + o Fix a couple of memory leaks (found with Valgrind). (3b2529a Ross Lagerwall) + o Remove deadcode in http regression tests. Found by coverity. (5553346) + o Fix possible uninitialized read in dns regression tests. Found by coverity. (2259777) + o Set umask before calling mkstemp in unit tests. Found by coverity (f1ce15d) + o Fix various check-after-dereference issues in unit tests: found by coverity (4f3732d) + o Fix resource leaks in the unit tests; found by coverity (270f279) + o Add some missing null checks to unit tests; found by coverity (f021c3d) + o Avoid more crashes/bad calls in unit tests; found by coverity (3cde5bf) + o Remove unused variable; spotted by coverity (6355b2a) + o Add checks to various return values in unit tests. Found by coverity (b9e7329) + o Move assignment outside tt_assert in ssl unit tests. Appeases coverity. (a2006c0) + + + +Changes in version 2.0.19-stable (3 May 2012) +BUGFIXES (CORE): + o Refactor event_persist_closure: raise and extract some common logic (bec22b4) + o If time has jumped so we'd reschedule a periodic event in the past, schedule it for the future instead (dfd808c) + o If a higher-priority event becomes active, don't continue running events of the current priority. (2bfda40) + +BUGFIXES (SSL): + o Fixed potential double-readcb execution with openssl bufferevents. (4e62cd1 Mark Ellzey) + +BUGFIXES (DNS): + o Cancel a probe request when the server is freed, and ignore cancelled probe callbacks (94d2336 Greg Hazel) + o Remove redundant DNS_ERR_CANCEL check, move comment (46b8060 Greg Hazel) + o When retransmitting a timed-out DNS request, pick a fresh nameserver. (3d9e52a) + +DOCUMENTATION FIXES: + o Fix a typo in the bufferevent documentation (98e9119) + o Add missing ) to changelog; spotted by rransom (4c7ee6b) + o Fix the website URL in the readme (f775521) + +COMPILATION FIXES: + o Fix a compilation error with MSVC 2005 due to use of mode_t (336dcae) + o Configure with gcc older than 2.95 (4a6fd43 Sebastian Hahn) + o Generate event-config.h with a single sed script (30b6f88 Zack Weinberg) + +FORWARD-COMPATIBILITY: + o Backport: provide EVENT_LOG_* names, and deprecate _EVENT_LOG_* (d1a03b2) + +TESTING/DEBUGGING SUPPORT: + o dns-example.c can now take a resolv.conf file on the commandline (6610fa5) + o Make some evdns.c debug logs more verbose (d873d67) + o Work-around a stupid gcov-breaking bug in OSX 10.6 (b3887cd) + + + +Changes in version 2.0.18-stable (22 Mar 2012) +BUGFIXES (core): + o Make uses of open() close-on-exec safe by introducing an internal evutil_open_closeonexec. (d2b5f72 Ross Lagerwall, 03dce42) + +BUGFIXES (kqueue): + o Properly zero the kevent in kq_setup_kevent() (c2c7b39 Sebastian Hahn) + +BUILD FIXES: + o Added OPENSSL_LDFLAGS env variable which is appended to SSL checks. (9278196 Mark Ellzey) + o Changed OPENSSL_LDFLAGS to OPENSSL_LIBADD (2d67b63 Mark Ellzey) + o Don't do clang version detection when disabling some flags (083296b Sebastian Hahn) + +BUGFIXES (dns): + o Stop crashing in evdns when nameserver probes give a weird error (bec5068) + + +Changes in version 2.0.17-stable (10 Feb 2012) + +BUGFIXES (core): + o Be absolutely sure to clear pncalls before leaving event_signal_closure (11f36a5) + o check for sysctl before we use it (358c745 Mike Frysinger) + o Remove bogus casts of socket to int before calling ev_callback (f032516) + o Make evconnlistener work around bug in older Linux when getting nmapped (ecfc720) + o Fix a list corruption bug when using event_reinit() with signals present (6e41cdc) + o Fix a fd leak in event_reinit() (3f18ad1) + o Do a memberwise comparison of threading function tables (c94a5f2 Nate R) + o Use C-style comments in C source files (for compatibility with compilers such as xlc on AIX). (d84d917 Greg Hewgill) + o Avoid crash when freeing event_iocp and using event_set_mem_functions (19715a6) + o In the kqueue backend, do not report EBADF as an EV_READ (5d7bfa1 Nicholas Marriott) + +BUGFIXES (evbuffer and bufferevents): + o Fix behavior of evbuffer_peek(buf,-1,NULL,NULL,0) (c986f23 Zack Weinberg) + o Loop on filtering SSL reads until we are blocked or exhausted. (5b4b812) + +BUGFIXES (evhttp): + o Force strict validation of HTTP version in response. (790f6b3 Catalin Patulea) + +BUGFIXES (evdns): + o evdns: fix a bug in circular-queue implementation (d6094b1) + +BUILD FIXES: + o Fix a silly compilation error with the sun compiler (1927776 Colin Watt) + o Suppress a gcc warning from ignoring fwrite return in http-sample.c (7206e8c) + +DOCUMENTATION FIXES: + o Slightly clarify evbuffer_peek documentation (7bbf6ca) + o Update copyright notices to 2012 (e49e289) + +NEW APIS: + o Backport evhttp_connection_get_bufferevent to Libevent 2.0 (da70fa7 Arno Bakker) + +TESTS AND TEST FIXES: + o Fix a race condition in the dns/bufferevent_connect_hostname test. (cba48c7) + o Add function to check referential integrity of an event_base (27737d5) + o Check event_base correctness at end of each unit test (3312b02) + o Workaround in the unit tests for an apparent epoll bug in Linux 3.2 (dab9187) + o Better workaround for Linux 3.2 edge-triggered epoll bug (9f9e259) + +Changes in version 2.0.16-stable (18 Nov 2011) +BUGFIXES (core): + o More detailed message in case of libevent self-debugging failure. (9e6a4ef Leonid Evdokimov) + o epoll: close fd on alloc fail at initialization (1aee718 Jamie Iles) + o Fix compile warning from saying event2/*.h inside a comment (447b0ba) + o Warn when unable to construct base because of failing make_base_notifiable (4e797f3) + o Don't try to make notifiable event_base when no threading fns are configured (e787413) + +BUGFIXES (evbuffer): + o unit test for remove_buffer bug (90bd620 Greg Hazel) + o Fix an evbuffer crash in evbuffer_remove_buffer() (c37069c) + +BUGFIXES (bufferevent_openssl): + o Refactor amount-to-read calculations in buffervent_ssl consider_reading() (a186e73 Mark Ellzey) + o Move SSL rate-limit enforcement into bytes_to_read() (96c562f) + o Avoid spinning on OpenSSL reads (2aa036f Mark Ellzey) + +BUGFIXES (dns) + o Empty DNS reply with OK status is another way to say NODATA. (21a08d6 Leonid Evdokimov) + +TESTING: + o Tests for 94fba5b and f72e8f6 (d58c15e Leonid Evdokimov) + o Test for commit aff6ba1 (f7841bf Leonid Evdokimov) + o Style and comment tweaks for dns/leak* tests (5e42202) + o improve test to remove at least one buffer from src (7eb52eb Greg Hazel) + +DOCUMENTATION: + o Add note about evhttp_send_reply_end to its doxygen (724bfb5) + o Update copyright dates to 2011. (3c824bd) + o Fix typo in whatsnew-2.0.txt (674bc6a Mansour Moufid) + o Improve win32 behavior of dns-sample.c code (a3f320e Gisle Vanem) + + + +Changes in version 2.0.15-stable (12 Oct 2011) +BUGFIXES (DNS): + o DNS: add ttl for negative answers using RFC 2308 idea. (f72e8f6 Leonid Evdokimov) + o Add DNS_ERR_NODATA error code to handle empty replies. (94fba5b Leonid Evdokimov) + +BUFGIXES (bufferevents and evbuffers): + o Make evbuffer callbacks get the right n_added value after evbuffer_add (1ef1f68 Alex) + o Prefer mmap to sendfile unless a DRAINS_TO_FD flag is set. Allows add_file to work with SSL. (0ba0af9) + +BUGFIXES (event loop): + o When a signal callback is activated to run multiple times, allow event_base_loopbreak to work even before they all have run. (4e8eb6a) + +DOCUMENTATION FIXES: + o Fix docstring in dns.h (2b6eae5 Leonid Evdokimov) + o refer to non-deprecated evdns functions in comments (ba5c27d Greg Hazel) + +BUILD AND TESTING FIXES: + o le-proxy and regress depend on openssl directly (9ae061a Sergey Avseyev) + o Use _SOURCES, not _sources, in sample/Makefile.am (7f82382) + o Fixed compiler warnings for unchecked read/write calls. (c3b62fd Mark Ellzey) + o Make write-checking fixes use tt_fail_perror (2b76847) + o Fix some "value never used" warnings with gcc 4.6.1 (39c0cf7) + + + +Changes in version 2.0.14-stable (31 Aug 2011) +BUGFIXES (bufferevents and evbuffers): + o Propagate errors on the underlying bufferevent to the user. (4a34394 Joachim Bauch) + o Ignore OpenSSL deprecation warnings on OS X (5d1b255 Sebastian Hahn) + o Fix handling of group rate limits under 64 bytes of burst (6d5440e) + o Solaris sendfile: correctly detect amount of data sent (643922e Michael Herf) + o Make rate limiting work with common_timeout logic (5b18f13) + o clear read watermark on underlying bufferevent when creating filtering bev to fix potentially failing fragmented ssl handshakes (54f7e61 Joachim Bauch) + +BUGFIXES (IOCP): + o IOCP: don't launch reads or writes on an unconnected socket (495c227) + o Make IOCP rate-limiting group support stricter and less surprising. (a98da7b) + o Have test-ratelim.c support IOCP (0ff2c5a) + o Make overlapped reads result in evbuffer callbacks getting invoked (6acfbdd) + o Correctly terminate IO on an async bufferevent on bufferevent_free (e6af35d) + +BUGFIXES (other): + o Fix evsig_dealloc memory leak with debugging turned on. (9b724b2 Leonid Evdokimov) + o Fix request_finished memory leak with debugging turned on. (aff6ba1 Leonid Evdokimov) + +BUILD AND TESTING FIXES: + o Allow OS-neutral builds for platforms where some versions have arc4random_buf (b442302 Mitchell Livingston) + o Try to fix 'make distcheck' errors when building out-of-tree (04656ea Dave Hart) + o Clean up some problems identified by Coverity. (7c11e51 Harlan Stenn) + + +Changes in version 2.0.13-stable (18 Jul 2011) +BUGFIXES + o Avoid race-condition when initializing global locks (b683cae) + o Fix bug in SSL bufferevents backed by a bev with a write high-watermarks (e050703 Joachim Bauch) + o Speed up invoke_callbacks on evbuffers when there are no callbacks (f87f568 Mark Ellzey) + o Avoid a segfault when all methods are disabled or broken (27ce38b) + o Fix incorrect results from evbuffer_search_eol(EOL_LF) (4461f1a) + o Add some missing checks for mm_calloc failures (89d5e09) + o Replace an assertion for event_base_free(NULL) with a check-and-warn (09fe97d) + o Report kqueue ebadf, epipe, and eperm as EV_READ events (1fd34ab) + o Check if the `evhttp_new_object' function in `http.c' returns NULL. (446cc7a Mansour Moufid) + o Use the correct printf args when formatting size_t (3203f88) + o Complain if the caller tries to change threading cbs after setting them (cb6ecee) + +DOCUMENTATION FIXES AND IMPROVEMENTS + o Revise the event/evbuffer/bufferevent doxygen for clarity and accuracy (2888fac) + o Update Doxyfile to produce more useful output (aea0555) + +TEST FIXES + o Fix up test_evutil_snprintf (caf695a) + o Fix tinytest invocation from windows shell (57def34 Ed Day) + +BUILD FIXES + o Use AM_CPPFLAGS in sample/Makefile.am, not AM_CFLAGS (4a5c82d) + o Fix select.c compilation on systems with no NFDBITS (49d1136) + o Fix a few warnings on OpenBSD (8ee9f9c Nicholas Marriott) + o Don't break when building tests from git without python installed (b031adf) + o Don't install event_rpcgen.py when --disable-libevent-install is used (e23cda3 Harlan Stenn) + o Fix AIX build issue with TAILQ_FOREACH definition (e934096) + + +Changes in version 2.0.12-stable (4 Jun 2011) +BUGFIXES + o Fix a warn-and-fail bug in kqueue by providing kevent() room to report errors (28317a0) + o Fix an assert-inducing fencepost bug in the select backend (d90149d) + o Fix failing http assertion introducd in commit 0d6622e (0848814 Kevin Ko) + o Fix a bug that prevented us from configuring IPv6 nameservers. (74760f1) + o Prevent size_t overflow in evhttp_htmlescape. (06c51cd Mansour Moufid) + o Added several checks for under/overflow conditions in evhttp_handle_chunked_read (a279272 Mark Ellzey) + o Added overflow checks in evhttp_read_body and evhttp_get_body (84560fc Mark Ellzey) + +DOCUMENTATION: + o Add missing words to EVLOOP_NONBLOCK documentation (9556a7d) + +BUILD FIXES + o libssl depends on libcrypto, not the other way around. (274dd03 Peter Rosin) + o Libtool brings in the dependencies of libevent_openssl.la automatically (7b819f2 Peter Rosin) + o Use OPENSSL_LIBS in Makefile.am (292092e Sebastian Hahn) + o Move the win32 detection in configure.in (ceb03b9 Sebastian Hahn) + o Correctly detect openssl on windows (6619385 Sebastian Hahn) + o Fix a compile warning with zlib 1.2.4 and 1.2.5 (5786b91 Sebastian Hahn) + o Fix compilation with GCC 2, which had no __builtin_expect (09d39a1 Dave Hart) + o Fix new warnings from GCC 4.6 (06a714f) + o Link with -lshell32 and -ladvapi32 on Win32. (86090ee Peter Rosin) + o Make the tests build when OpenSSL is not available. (07c41be Peter Rosin) + o Bring in the compile script from automake, if needed. (f3c7a4c Peter Rosin) + o MSVC does not provide S_ISDIR, so provide it manually. (70be7d1 Peter Rosin) + o unistd.h and sys/time.h might not exist. (fe93022 Peter Rosin) + o Make sure TINYTEST_LOCAL is defined when building tinytest.c (8fa030c Peter Rosin) + o Fix winsock2.h #include issues with MSVC (3d768dc Peter Rosin) + o Use evutil_gettimeofday instead of relying on the system gettimeofday. (0de87fe Peter Rosin) + o Always use evutil_snprintf, even if OS provides it (d1b2d11 Sebastian Hahn) + o InitializeCriticalSectionAndSpinCount requires _WIN32_WINNT >= 0x0403. (816115a Peter Rosin) + o cygwin: make it possible to build DLLs (d54d3fc) + + + +Changes in version 2.0.11-stable (27 Apr 2011) + [Autogenerated from the Git log, sorted and cleaned by hand.] +BUGFIXES: + o Fix evport handling of POLLHUP and POLLERR (b42ce4b) + o Fix compilation on Windows with NDEBUG (cb8059d) + o Check for POLLERR, POLLHUP and POLLNVAL for Solaris event ports (0144886 Trond Norbye) + o Detect and handle more allocation failures. (666b096 Jardel Weyrich) + o Use event_err() only if the failure is truly unrecoverable. (3f8d22a Jardel Weyrich) + o Handle resize failures in the select backend better. (83e805a) + o Correctly free selectop fields when select_resize fails in select_init (0c0ec0b) + o Make --enable-gcc-warnings a no-op if not using gcc (3267703) + o Fix a type error in our (unused) arc4random_stir() (f736198) + o Correctly detect and stop non-chunked http requests when the body is too long (63a715e) + o Have event_base_gettimeofday_cached() always return wall-clock time (a459ef7) + o Workaround for http crash bug 3078187 (5dc5662 Tomash Brechko) + o Fix incorrect assertions and possible use-after-free in evrpc_free() (4b8f02f Christophe Fillot) + o Reset outgoing http connection when read data in idle state. (272823f Tomash Brechko) + o Fix subtle recursion in evhttp_connection_cb_cleanup(). (218cf19 Tomash Brechko) + o Fix the case when failed evhttp_make_request() leaved request in the queue. (0d6622e Tomash Brechko) + o Fix a crash bug in evdns server circular list code (00e91b3) + o Handle calloc failure in evdns. (Found by Dave Hart) (364291e) + o Fix a memory leak on win32 socket->event map. (b4f89f0) + o Add a forgotten NULL check to evhttp_parse_headers (12311ff Sebastian Hahn) + o Fix possible NULL-deref in evdns_cancel_request (5208544 Sebastian Hahn) + +PORTABILITY: + o Fall back to sscanf if we have no other way to implement strtoll (453317b) + o Build correctly on platforms without sockaddr_storage (9184563) + o Try to build correctly on platforms with no IPv6 support (713c254) + o Build on systems without AI_PASSIVE (cb92113) + o Fix http unit test on non-windows platforms without getaddrinfo (6092f12) + o Do not check for gethostbyname_r versions if we have getaddrinfo (c1260b0) + o Include arpa/inet.h as needed on HPUX (10c834c Harlan Stenn) + o Include util-internal.h as needed to build on platforms with no sockaddr_storage (bbf5515 Harlan Stenn) + o Check for getservbyname even if not on win32. (af08a94 Harlan Stenn) + o Add -D_OSF_SOURCE to fix hpux builds (0b33479 Harlan Stenn) + o Check for allocation failures in apply_socktype_protocol_hack (637d17a) + o Fix the check for multicast or broadcast addresses in evutil_check_interfaces (1a21d7b) + o Avoid a free(NULL) if out-of-memory in evdns_getaddrinfo. Found by Dave Hart (3417f68) + +DEFENSIVE PROGRAMMING: + o Add compile-time check for AF_UNSPEC==PF_UNSPEC (3c8f4e7) + +BUGS IN TESTS: + o Fix test.sh output on solaris (b4f89b6 Dave Hart) + o Make test-eof fail with a timeout if we never get an eof. (05a2c22 Harlan Stenn) + o Use %s with printf in test.sh (039b9bd) + o Add an assert to appease clang's static analyzer (b0ff7eb Sebastian Hahn) + o Add a forgotten return value check in the unit tests (3819b62 Sebastian Hahn) + o Actually send NULL request in http_bad_request_test (b693c32 Sebastian Hahn) + o add some (void) casts for unused variables (65707d7 Sebastian Hahn) + o Refactor test_getaddrinfo_async_cancel_stress() (48c44a6 Sebastian Hahn) + o Be nice and "handle" error return values in sample code (4bac793 Sebastian Hahn) + o Check return value of evbuffer_add_cb in tests (93a1abb Sebastian Hahn) + o Remote some dead code from dns-example.c (744c745 Sebastian Hahn) + o Zero a struct sockaddr_in before using it (646f9fe Sebastian Hahn) + +BUILD FIXES: + o Fix warnings about AC_LANG_PROGRAM usage (f663112 Sebastian Hahn) + o Skip check for zlib if we have no zlib.h (a317c06 Harlan Stenn) + o Fix autoconf bracket issues; make check for getaddrinfo include netdb.h (833e5e9 Harlan Stenn) + o Correct an AM_CFLAGS to an AM_CPPFLAGS in test/Makefile.am (9c469db Dave Hart) + o Fix make distcheck & installation of libevent 1 headers (b5a1f9f Dave Hart) + o Fix compilation under LLVM/clang with --enable-gcc-warnings (ad9ff58 Sebastian Hahn) + +FEATURES: + o Make URI parser able to tolerate nonconformant URIs. (95060b5) + +DOCUMENTATION: + o Clarify event_set_mem_functions doc (926f816) + o Correct evhttp_del_accept_socket documentation on whether socket is closed (f665924) + o fix spelling mistake in whatsnew-2.0.txt (deb2f73) + o Fix sample/http-server ipv6 fixes (eb692be) + o Comment internal headers used in sample code. (4eb281c) + o Be explicit about how long event loops run in event.h documentation (f95bafb) + o Add comment to configure.in to explain gc-sections test logic (c621359) + o Fix a couple of memory leaks in samples/http-server.c. Found by Dave Hart. (2e9f665) + +BUILD IMPROVEMENTS: + o Use the gcc -ffunction-segments feature to allow gc when linking with static libevent (0965c56 Dave Hart) + o Add configure options to disable installation, regression tests (49e9bb7 Dave Hart) + + + +Changes in version 2.0.10-stable (16 Dec 2010) + [Autogenerated from the Git log, sorted and cleaned by hand.] +BUGFIXES + o Minor fix for IOCP shutdown handling fix (2599b2d Kelly Brock) + o Correctly notify the main thread when activating an event from a subthread (5beeec9) + o Reject overlong http requests early when Expect:100-continue is set (d23839f Constantine Verutin) + o EVUTIL_ASSERT: Use sizeof() to avoid "unused variable" warnings with -DNDEBUG. (b63ab17 Evan Jones) + +CODE CLEANUPS + o bufferevent-internal.h: Use the new event2/util.h header, not evutil.h (ef5e65a Evan Jones) + o Use relative includes instead of system includes consistently. (fbe64f2 Evan Jones) + o Make whitespace more consistent + +TESTING + o tests: Use new event2 headers instead of old compatibility headers. (4f33209 Evan Jones) + +DOCUMENTATION + o Document that the cpu_hint is only used on Windows with IOCP for now (57689c4) + o Add stuff to "whats new in 2.0" based on reading include changes since August. (18adc3f) + + +Changes in 2.0.9-rc (30 Nov 2010): + [Autogenerated from the Git log, sorted and cleaned by hand.] +NEW AND MODIFIED APIs + o Add a function to change a listener's callback. (46ee061) + o Make evbuffer_add_file take ev_off_t, not off_t (ac7e52d) + o Make rate-limits go up to SIZE_MAX/EV_SSIZE_MAX, not just INT32_MAX (2cbb1a1) + o Add a bufferevent_get_base function (aab49b6) + +MAJOR BUGFIXES + o Disable changelist for epoll by default because of Linux dup() bug; add an option and/or an envvar to reenable it for speed. (9531763) + o Fix a 100%-CPU bug where an SSL connection would sometimes never stop trying to write (1213d3d) + o Fix a nasty bug related to use of dup() with epoll on Linux (c281aba) + o Fix bugs in posix thread-id calculation when sizeof(pthread_t) != sizeof(long) (fbaf077) + o Fix some ints to evutil_socket_t; make tests pass on win64. (f817bfa Dimitre Piskyulev) + o Set _EVENT_SIZEOF_VOID_P correctly on win32 and win64 (1ae82cd Dimitre Piskyulev) + o Avoid double-invocation of user callback with EVUTIL_EAI_CANCEL (abf01ed) + o Set SO_UPDATE_ACCEPT_CONTEXT on sockets from AcceptEx so that shutdown() can work (52aa419) + o When closing a filtering bufferevent, clear callbacks on the underlying bufferevent (fc7b1b0) + +NEW AND MODIFIED HTTP APIs + o Add evhttp_parse_query_str to be used with evhttp_uri_parse. (2075fbc) + o Add evhttp_response_code to remove one more reason to include http_struct.h (22e0a9b) + o Define enumerators for all HTTP methods, including PATCH from RFC5789 (75a7341 Felix Nawothnig) + o Functions to actually use evhttp_bound_socket with/as evconnlistener. (006efa7) + o Add evhttp_request_get_command so code can tell GET from POST without peeking at the struct. (49f4bf7) + o Introduce absolute URI parsing helpers. (86dd720 Pavel Plesov) + o Revise evhttp_uri_parse implementation to handle more of RFC3986 (eaa5f1d) + o Add evhttp_connection_get_base() to get the event_base from an http connection (cd00079) + o Let evhttp_parse_query return -1 on failure (b1756d0) + o New evhttp_uri(encode|decode) functions to handle + and NUL characters right (a8148ce) + o Add evhttp_response_code to remove one more reason to include http_struct.h (22e0a9b) + o Tweak interface for allowed methods (f5b391e) + o Add evhttp server alias interface, correct flagging of proxy requests. (aab8c38 Christopher Davis) + +HTTP BUGFIXES + o Add some comments to http.c and make a few functions static. (90b3ed5) + o Fix Content-Length when trying send more than 100GB of data (!) on an evhttp. (525da3e) + o Fix a bug where we would read too much data in HTTP bodies or requests. (58a1cc6) + o Correctly count req->body_size on http usage without Content-Length (8e342e5) + o Avoid missed-request bug when entire http request arrives before data is flushed (74c0e86) + o reset "chunked" flag when sending non-chunked reply (aa5f55f Joachim Bauch) + o evhttp_encode_uri encodes all reserved characters, including !$'()*+,/:=@ (2e63a60) + o Replace exact-version checks for HTTP/1.1 with >= or < checks (647e094) + o evhttp: Return 501 when we get an unrecognized method, not 400. (536311a) + o Don't disable reading from the HTTP connection after sending the request to be notified of connection-close in time (c76640b Felix Nawothnig) + o Never call evhttp_readcb while writing. (0512487) + o Try to fix an assertion failure related to close detection (0faaa39) + o Correctly detect timeouts during http connects (04861d5) + o Preliminary support for Continue expectation in evhttp. (fa9305f Christopher Davis) + +OTHER BUGFIXES + o Correct logic for realigning a chain in evbuffer_add (e4f34e8) + o Fix a minor syntax error that most compilers didn't care about (e56ff65) + o Fix some uses of int for socket in regress (5d389dc) + o Check return value for ioctlsocket on win32 (f5ad31c Trond Norbye) + o Fix som event_warns that should have been event_warnx (19c71e7) + o Fix signal handler types for win64. (b81217f) + o Try to clear up more size_t vs int/long issues. (598d133) + o Make sure IOCP evconnlistener uses virtual events. (7b40a00 Christopher Davis) + o Don't free evdns_request handles until after the callback is invoked (9ed30de) + o Fix some more cancel-related bugs in getaddrinfo_async (c7cfbcf) + o Make evdns_getaddrinfo_cancel threadsafe (d51b2fc) + o Only clear underlying callbacks when the user hasn't reset them. (1ac5b23) + o Fix bug in bufferevent_connect on an openssl bufferevent that already had an fd (4f228a1) + o Resolve an evport bug in the thread/forking test (3a67d0b) + o Make sure the CLOEXEC flag is set on fds we open for base notification (3ab578f) + o Fix IRIX build. sa_family collides with a #define in sys/socket.h on IRIX. (e874982 Kevin Bowling) + o If not WIN32, include <sys/socket.h> in event2/util.h. (1cd45e5 Kevin Bowling) + o Fix some C99-style comments to work with the xlC compiler. (c2e5e22 Kevin Bowling) + o Add some checks since lack of TAILQ_FOREACH doesn't imply lack of FIRST, END, NEXT, or INSERT_BEFORE. Quiet some warnings in XL C. (c4dc335 Kevin Bowling) + o Reworked AIX __ss_family workaround to use AC_STRUCT_MEMBER. (2e2a3d7 Kevin Bowling) + o Take select from <sys/select.h> when testing in autoconf. AIX build fix. (a3a9f6b Kevin Bowling) + o Fix snprintf related failures on IRIX. (3239073 Kevin Bowling) + o Remove _event_initialized(); make event_initialized() a function(); make it consistent on windows and non-windows (652024b) + o Do not let EVLOOP_ONCE exit the loop until all deferred callbacks have run (2d5e1bd) + o Make EVLOOP_ONCE ignore internal events (0617a81) + o Possible crash fix when freeing an underlying bufferevent of an openssl bufferevent (29f7623) + +HTTP CLEANUPS + o Stop using Libevent-1 headers in regress_http (1f507d7) + o Modernize header usage in bench_http.c (e587069) + o fix signed/unsigned warnings in http.c (74a91e5) + o Update the HTTP regression tests to use Libevent2 apis for non-http stuff (d9ffa89) + o Start porting http tests to not use legacy interfaces (8505a74) + o Convert the rest of the http tests to be non-legacy unit tests. (9bb8239) + o Rename the confusing "base" static variable in regress_http.c (353402a) + o Stop accessing http request struct directly from in the unit tests. (0b137f4) + o Refactor http version parsing into a single function (a38140b) + +TESTING + o Improvements to tinytest_macros.h (ad923a1) + o Add a huge pile of tests for the new URI functions, and make them pass. (a5a76e6) + o Unit tests for evhttp_uri_set* (bc98f5e) + o Increase the skew tolerance to 2 seconds in thread/deferred_cb_skew (f806476 Christopher Davis) + o Reorder backends in test.sh to match preference order in event.c (ece974f) + o Add a stress test for getaddrinfo_cancel (da1bf52) + o Units test for unexpected evhttp methods. (75e3320) + +DOCUMENTATION + o Document behavior of URI parsing more thoroughly. (3a33462) + o Document that two bufferevent functions only work on socket bufferevents (70e1b60) + o add a requested docstring for event_rpcgen.CommandLine.__init__ (f1250eb) + o Fix a mistake in http documentation found by Julien Blache (229714d) + o Add a basic example of how to write a static HTTP server. (4e794d5) + o Document event_get_assignment (88be27d) + o Note that reentrant calls to libevent from logging cbs may fail badly (e431bcd) + o Clarify EVLOOP_* documentation to be more precise. (057a514) + +CLEANUPS + o Simplify the logic for choosing EPOLL_CTL_ADD vs EPOLL_CTL_MOD (2c66983) + o Rename "size" variables in win32select that were really fd counts. (b6a158c) + o Fix even more win64 warnings (7484df6) + o Fix even more win64 warnings: buffer, event_tagging, http, evdns, evrpc (545a611) + o Fix more wn64 warnings. (34b84b9 Christopher Davis) + o Use the label_len local variable in evdns instead of recalculating it over and over (ba01456) + o Fix some irix compilation warnings spotted by Kevin Bowling (7bcace2) + + + +Changes in 2.0.8-rc (14 Oct 2010): + [Autogenerated from the Git log, sorted and cleaned by hand.] +NEW APIS + o Add error callback to evconnlistener (c4be8d8 Simon Perreault) + o Add a LEV_OPT_THREADSAFE option for threadsafe evconnlisteners (127d4f2) + +CHANGED BEHAVIOR + o Correct logic on disabling underlying bufferevents when disabling a filter (ac27eb8) + +BUGFIXES + o Obey enabled status when unsuspending (040a019 Simon Perreault) + o Warn when using the error-prone EV_SIGNAL interface in an error-prone way. Also, fix a couple of race conditions in signal.c (720bd93) + O Make default signal backend fully threadsafe (95a7d41) + o Put internal events at highest priority (90651b3) + o Fix warnings in the main codebase flagged by -Wsigned-compare (9c8db0, 5e4bafb, 5c214a, 6be589a, e06f514) + o Fix compile in kqueue.c (b395392 Sebastian Hahn) + o Do not search outside of the system directory for windows DLLs (d49b5e3) + o Fix a spurious-call bug on epoll.c (0faaee0) + o Send a shutdown(SHUT_WR) before closing an http connection (e0fd870 Christopher Davis) + o Fix warnings on mingw with gcc 4.5 (5b7a370) + o Fix an EINVAL on evbuffer_write_iovec on OpenSolaris. (fdc640b) + o Fix allocation error for IOCP listeners. Probably harmless, since struct event is big (481ef92) + o Make iocp/listener/error work; don't accept again if lev is disabled. (62b429a Christopher Davis) + o Handle rate-limiting for reading on OpenSSL bufferevents correctly. (819b171) + o Fix serious bugs in per-bufferevent rate-limiting code (34d64f8) + o Avoid spurious reads from just-created open openssl bufferevents (223ee40) + o Fix a case where an ssl bufferevent with CLOSE_ON_FREE didn't close its fd (93bb7d8) + o The corrected bufferevent filter semantics let us fix our openssl tests (34331e4) + +TESTING + o Make SSL tests cover enabling/disabling EV_READ. (a5ce9ad) + o Bump to the latest version of tinytest (f0bd83e) + o Unit tests for listener error callbacks (045eef4) + o New unit test for ssl bufferevents starting with connected SSLs. (02f6259) + +DEBUGGABILITY + o Make debugging output for epoll backend more comprehensive (ec2b05e) + o Make event.c debugging messages report fds (e119899) + o Make the --enable-gcc-warnings option include signed comparison warnings (d3b096c) + +DEADCODE REMOVAL + o Remove the now-useless evsig_caught and evsig_process (4858b79) + o Remove event_base.evsigbase; nothing used it. (38d0960) + + + +Changes in 2.0.7-rc (9 Sep 2010): + [Autogenerated from the Git log, sorted and cleaned by hand.] +NEW APIS + o Expose a evdns_base_nameserver_sockaddr_add() function to add a nameserver by sockaddr (1952143) + o Add event_config_set_num_cpus_hint() for tuning win32 IOCP thread pools, etc. (2447fe8 Christopher Davis) + +BUGFIXES + o Fix a nasty dangling-event bug when using rate-limiting groups (0bffe43) + o Clean up syntax on TAILQ_ENTRY() usage to build correctly with recent MSVC (60433a0 Gilad Benjamini) + o Make definition of WIN32_LEAN_AND_MEAN in event.h conditional (3920172 Gilad Benjamini) + o Correctly detect failure to delete bufferevent read-timeout event (da6e7cd) + o Set close-on-exec bit for filedescriptors created by dns subsystem (d0b8843) + o Fix kqueue correctness test on x84_64 (6123d12) + o Detect events with no ev_base; warn instead of crashing (f1074b7) + o Fix an issue with forking and signal socketpairs in select/poll backends (d61b2f3) + o Stop using global arrays to implement the EVUTIL_ctype functions (1fdec20) + o On windows, make lock/thread function tables static (5de2bcb) + o Close th_notify_fds and open a new pair on reinit (495ed66) + o Declare signal handler function as "__cdecl" on Windows (f0056d0) + o Use the _func() replacements for open, fstat, etc in evutil.c on win32 (e50c0fc) + o Only process up to MAX_DEFERRED deferred_cbs at a time (17a14f1 Christopher Davis) + +THREADING BUGFIXES + o Avoid deadlock when activating signals (970e6ad) + o Add a condition variable backend, with implementations for pthreads and win32 (d4977b5) + o Use conditions instead of current_event_lock to fix a deadlock (e0972c2) + o Fix logic error in win32 TRY_LOCK that caused problems with rate-limiting (4c32b9d) + o Avoid needlessly calling evthread_notify_base() when the loop is not running (c7a06bf) + o Minimize calls to base_notify implementation functions, thereby avoiding needless syscalls (4632b78) + +IOCP BUGFIXES + o IOCP-related evbuffer fixes (03afa20 Christopher Davis) + o Stop IOCP when freeing the event_base (d844242 Christopher Davis) + o Some IOCP bufferevent tweaks (76f7e7a Christopher Davis) + +TESTS + o Make the regress_pthread.c tests work on windows with current test APIs (d74ae38) + o Add a unit test for conditions (5fb1095) + o Allow more than one copy of regression tests to run at once (a97320a) + o Fix event_del(0) instance in bench.c (b0f284c Shuo Chen) + o Fix a few memory leaks in the tests (1115366) + o IOCP-related unit test tweaks (499452f Christopher Davis) + o Improve testing of when thread-notification occurs (ce85280) + +BUILD AND DISTRIBUTION + o Add pkgconfig files for libevent_{openssl,pthreads} (ebcb1f0) + o Change include order in Makefile.nmake (4022b28) + o Make include/event2/event-config.h not included in source dist (a4af9be) + o Honor NDEBUG; build without warnings with NDEBUG; make NDEBUG always-off in unit test code (743f866) + o Declare evkeyvalq and event_list even if event_struct.h comes before sys/queue.h (d3ceca8) + o Move evkeyvalq into a separate header for evhttp_parse_query users (ca9048f) + o Prefer autoreconf -ivf to manual autogen.sh (7ea8e89) + +CLEANUP + o Completely remove the (mostly-removed) obsolete thread functions (3808168) + o Rename regress_pthread.c to regress_thread.c (041989f) + o Make defer-internal.h use lock macros, not direct calls to lock fns (5218d2a) + +DOCUMENTATION + o Document that DNS_NO_SEARCH is an obsolete alias for DNS_QUERY_NO_SEARCH (33200e7) + o Update the whatsnew-2.0.txt document (4991669) + + + +Changes in 2.0.6-rc (6 Aug 2010): + [Autogenerated from the Git log, sorted by hand.] +DOCUMENTATION + o Document a change in the semantics of event_get_struct_event_size() (e21f5d1) + o Add a comment to describe our plan for library versioning (9659ece) + o Fix sentence fragment in docs for event_get_struct_event_size() (7b259b6) + +NEW FEATURES AND INTERFACE CHANGES + o Remove the obsolete evthread interfaces (c5bab56) + o Let evhttp_send_error infer the right error reasons (3990669) + o Add a function to retrieve the other side of a bufferevent pair (17a8e2d) + o Add bufferevent_lock()/bufferevent_unlock() (215e629) + o Stop asserting when asked for a (unsupported) TCP dns port. Just return NULL. (7e87a59) + o Replace (unused,always 0) is_tcp argument to evdns_add_server_port*() with flags (e1c1167) + o Constify a couple of arguments to evdns_server_request_add_*_reply (cc2379d) + o Add an interface to expose min_share in ratelimiting groups (6ae53d6) + +BUGFIXES + o Avoid event_del on uninitialized event in event_base_free (6d19510) + o Add some missing includes to fix Linux build again (75701e8) + o Avoid close of uninitialized socket in evbuffer unit test (bda21e7) + o Correctly recognize .255 addresses as link-local when looking for interfaces (8c3452b) + o If no evdns request can be launched, return NULL, not a handle (b14f151) + o Use generic win32 interfaces, not ASCII-only ones, where possible. (899b0a3) + o Fix the default HTTP error template (06bd056 Felix Nawothnig) + o Close the file in evutil_read_file whether there's an error or not. (0798dd1 Pierre Phaneuf) + o Fix possible nullptr dereference in evhttp_send_reply_end() (29b2e23 Felix Nawothnig) + o never let bufferevent_rlim functions return negative (0859870) + o Make sample/hello_world work on windows (d89fdba) + o Fix a deadlock related to event-base notification. Diagnosed by Zhou Li, Avi Bab, and Scott Lamb. (17522d2) + o Possible fix to 100% cpu usage with epoll and openssl (cf249e7 Mike Smellie) + o Don't race when calling event_active/event_add on a running signal event (fc5e0a2) + o Suppress a spurious EPERM warning in epoll.c (e73cbde) + o Fix wrong size calculation of iovec buffers when exact=1 (65abdc2 niks) + o Change bufferevent_openssl::do_write so it doesn't call SSL_write with a 0 length buffer (c991317 Mike Smellie) + o Fixed compilation of sample/le-proxy.c on win32 (13b912e Trond Norbye) + o Fix rate-limit calculation on openssl bufferevents. (009f300) + o Remember to initialize timeout events for bufferevent_async (de1f5d6 Christopher Davis) + +BUILD AND DISTRIBUTION CHANGES + o Test the unlocked-deferred callback case of bufferevents (dfb75ab) + o Remove the now-unusable EVTHREAD_LOCK/UNLOCK constants (fdfc3fc) + o Use -Wlogical-op on gcc 4.5 or higher (d14bb92) + o Add the libtool-generated /m4/* stuff to .gitignore (c21c663) + o Remove some automake-generated files from version control. (9b14911) + o Have autogen.sh pass --force-missing to automake (8a44062) + o Set library version for libevent_pthreads correctly (b2d7440) + o Really only add libevent_core.la to LIBADD on mingw (1425003 Sebastian Hahn) + o Build more cleanly with NetBSDs that dislike toupper(char) (42a8c71) + o Fix unit tests with -DUSE_DEBUG enabled (28f31a4) + o Fix evdns build with -DUNICODE (5fa30d2) + o Move event-config.h to include/event2 (ec347b9) + +TESTING + o Add options to test-ratelim.c to check its results (2b44dcc) + o Make test-ratelim clean up after itself better. (b5bfc44) + o Remove the now-obsolete setup_test() and cleanup_test() functions (e73f1d7) + o Remove all non-error prints from test/regress.c (8bc1e3d) + o Make test.sh exit with nonzero status if tests fail (faf2a04) + o Have the unit tests report errors from test.sh (3689bd2) + o Fix logic in correcting high values from FIONREAD (3467f2f) + o Add test for behavior on remote socket close (44d57ee) + o Unit test for event_get_struct_event_size() (7510aac) + o Make test/test.sh call test-changelist (7c92691) + o Fix badly-behaved subtest of dns/bufferevent_connect_hostname (840a72f Joachim Bauch) + o Add option to test-ratelim to test min_share (42f6b62) + o Fix an assertion bug in test-ratelim (b2c6202) + o Make tests quieter on local dns resolver failure (e996b3d) + o Increase the tolerance in our unit tests for sloppy clocks. (170ffd2) + o Use AF_INET socketpair to test sendfile on Solaris (9b60209) + o Make test-changelist count cpu usage right on win32 (ea1ea3d) + +INTERNALS, PERFORMANCE, AND CODE CLEANUPS + o Mark the event_err() functions as __attribute__((noreturn)) (33bbbed) + o Do not check that event_base is set in EVBASE_ACQUIRE_LOCK (218a3c3) + o Replace (safe) use of strcpy with memcpy to appease OpenBSD (caca2f4) + o Remove some dead assignments (47c5dfb) + o Fix a pedantic gcc 4.4 warning in event2/event.h (276e7ee) + o Drain th_notify_fd[0] more bytes at a time. (a5bc15b) + o Tidy up the code in evthread_make_base_notifiable a little (61e1eee) + o Pass flags to fcntl(F_SETFL) and fcntl(F_SETFD) as int, not long (7c2dea1) + o Remove unused variables in test/test-changelist.c (b00d4c0) + o Fix whitespace. (cb927a5) + o Improve error message for failed epoll to make debugging easier. (9e725f7) + o Turn our socketpair() replacement into its own function (57b30cd) + + + +Changes in 2.0.5-beta (10 May 2010): + [Autogenerated from the Git log, sorted by hand.] +DOCUMENTATION + o Update all our copyright notices to say "2010" (17efc1c) + o Add Christopher Clark and Maxim Yegorushkin to the LICENSE file (38b7b57) + o Clarify Christopher Clark's status as writer of original ht code. (78772c3) + o Try to comment some of the event code more (cdd4c49) + o Add a few more evmap/changelist comments (c247adc) + o Add a comment to explain why evdns_request is now separte from request (ceefbe8) + o Document evutil_secure_rng_init() and evutil_secure_rng_add_bytes() (a5bf43a) + o Stop distributing and installing manpages: they were too inaccurate (7731ec8) + +NEW FEATURES AND INTERFACE CHANGES + o Remove signal_assign() and signal_new() macros. (2fac0f7) + o Make evdns use the regular logging system by default (b2f2be6) + o Allow evbuffer_read() to split across more than 2 iovecs (e470ad3) + o Functions to manipulate existing rate limiting groups. (ee41aca) + o Functions to track the total bytes sent over a rate limit group. (fb366c1) + o Detect and refuse reentrant event_base_loop() calls (b557b17) + o Limit the maximum number of events on each socket to 65535 (819f949) + o Add evbuffer_copyout to copy data from an evbuffer without draining (eb86c8c) + o Expose the request and reply members of rpc_req_generic() (07edf78 Shuo Chen) + o Add void* arguments to request_new and reply_new evrpc hooks (755fbf1 Shuo Chen) + o Seed the RNG using sysctl() as well as /dev/urandom (71fc3eb) + o Make evutil_secure_rng_init() work even with builtin arc4random (f980716) + o Report DNS error when lookup fails during bufferevent_socket_connect_hostname. (0ef4070 Christopher Davis) + o Release locks on bufferevents while executing callbacks (a5208fe Joachim Bauch) o Make debug mode catch mixed ET and non-ET events on an fd (cb67074) + o Catch attempts to enable debug_mode too late (9ecf0d4) + o Refuse null keys in evhttp_parse_query() (953e229 Frank Denis) + +BUGFIXES + o Avoid a spurious close(-1) on Linux (70a44b6) + o Do not close(-1) when freeing an uninitialized socket bufferevent (b34abf3) + o Free evdns_base->req_heads on evdns_base_free (859af67) + o Avoid an (untriggerable so far) crash bug in bufferevent_free() (0cf1431) + o Set mem_offset for every bufferevent type (657d1b6) + o Fix infrequent memory leak in bufferevent_init_common(). (8398641 Jardel Weyrich) + o Make evutil_signal_active() match declaration. (e1e703d Patrick Galbraith) + o Fix minheap code to use replacement malloc functions (a527618) + o Fix a free(NULL) in minheap-internal.h (6f20492) + o Fix critical bug in evbuffer_write when writev is not available (cda56ab) + o Make the no_iovecs case of write_atmost compile (8e227b0) + o Fix a memory leak when appending/prepending to a buffer with unused space. (45068a3) + o Clean up a mistake in pointer manipulation in evbuffer_remove (28bfed4 Christopher Davis) + o Always round up when there's a fractional number of msecs. (8f9e60c Christopher Davis) + o Fix compiler warnings under WIN32 (d469c50 Giuseppe Scrivano) + o Clean up properly when adding a signal handler fails. (b84b598 Gilad Benjamini) o Ensure that evdns_request is a persistent handle. (15bb82d Christopher Davis) + o Free search state when finished searching to avoid an infinite loop. (a625840 Christopher Davis) + o Assert for valid requests as necessary. (67072f3 Christopher Davis) + o do not leak the request object on persistent connections (9d8edf2) + o Make evdns logging threadsafe (b1c7950) + o Fix a couple of bugs in the BSD sysctl arc4seed logic (a47a4b7) + o Remove one last bug in last_with_datap logic. Found with valgrind (d49b92a) + o fix a leak when unpausing evrpc requests (94ee125) + o Fix a memory leak when unmarshalling RPC object arrays (f6ab2a2) + o Fix compilation when openssl support is disabled (40c301b) + o Allow empty reason line in HTTP status (739e688 Pierre Phaneuf) + o Fix a compile warning introduced in 739e688 (bd1ed5f Sebastian Hahn) + o Fix nonstandard TAILQ_FOREACH_REVERSE() definition (71afc52 Frank Denis) + o Try /proc on Linux as entropy fallback; use sysctl as last resort (20fda29) + o Fix symbol conflict between mm_*() macros and libmm (99e50e9) + o Fix some crazy macro mistakes in arc4random.c (90d4225) + o Make evbuffer_add_file() work on windows (dcdae6b) + o Fix unused-variable warning when building with threads disabled (ad811cd) + o Numerous opensolaris compilation fixes (c44de06) + o Fix getaddrinfo with protocol unset on Solaris 9. Found by Dagobert Michelsen (2cf2a28) + o Fix another nasty solaris getaddrinfo() behavior (3557071) + o Define _REENTRANT as needed on Solaris, elsewhere (c1cd32a) + o Fix some autoconf issues on OpenBSD (7c519df) + +BUILD AND DISTRIBUTION CHANGES + o Distribute libevent.pc.in, not libevent.pc (22aff04) + o Avoid errors in evutil.c when building with _UNICODE defined (b677032 Brodie Thiesfield) + o Avoid errors in http.c when building with VC 2003 .NET (13e4f3b Brodie Thiesfield) + o Support the standard 'make check' target in place of 'make verify' (426c8fb) + o Remove redundant stuff from EXTRA_DIST (b660edf) + o Switch to using AM conditionals in place of AC_LIBOBJ (2e898f5) + o Remove an orphaned RELEASE flag in Makefile.am (0794b0d) + o Give a better warning for bad automake versions. (77c917d) + o Use dist_bin_SCRIPTS, not EXTRA_DIST, to distribute scripts (9eb2fd7) + o Never test for select() on windows (3eb044d Trond Norbye) + o Do not inhibit automake dependencies generation (10c4c90 Giuseppe Scrivano) + o Create shared libraries under Windows (3cbca86 Giuseppe Scrivano) + o Add ctags/etags files to .gitignore (0861d17) + o Only specify -no-undefined on mingw (25433b9) + o Only add libevent_core.la to LIBADD on mingw (fdc6297) + +TESTING + o Get bench_http to work on Windows; add a switch to enable IOCP. (4ac38a5 Christopher Davis) + o VC has no getopt(), so do without in bench_http. (1273d2f Christopher Davis) + o Fix an obnoxious typo in the bufferevent_timeout_filter test (0d047c3) + o Fix a write of uninitialized RAM in regression tests (68dc742) + o Fix some memory leaks in the unit tests (274a7bd) + o Make 'main/many_events' test 70 fds, not 64. (33874b0) + o Unit-test every evbuffer_add_file() implementation. (06a4443) + o Add more unit tests for evbuffer_expand (8c83e99) + o Test another case of evbuffer_prepend (1234b95) + o Fix a possible double-free bug in SSL bufferevents with CLOSE_ON_FREE (7501895) o Add dns/search_cancel unit test. (39b870b Christopher Davis) + o Make http_base_test stop leaking an event_base. (96730d3) + o Detect broken unsetenv at unit-test runtime (f37cd4c) + o Implement regress_make_tempfile on win32 to test evbuffer_add_file (b4f12a1) + o add more (currently skipped) add_file tests on win32 (05de45d) + o Fix bench_http build on win32. (384d124) + o Make unit test for add_file able to tell "error" from "done" (88a543f) + o Make test for bufferevent_connect_hostname system-neutral (f89168e) + o Make test.sh support mingw/msys on win32 (0ee6f6c) + o Fix test.sh on freebsd (3d9e05b) + +INTERNALS, PERFORMANCE, AND AND CODE CLEANUPS + o Improve the speed of evbuffer_readln() (cc1600a) + o more whitespace normalization (2c2618d) + o Revise evbuffer to add last_with_data (2a6d2a1) + o Use last_with_data in place of previous_to_last (c8ac57f) + o Remove previous_to_last from evbuffer (6f47bd1) + o Fix last_with_data compilation on windows (1e7b986) + o Add some glass-box tests for the last_with_data code. (17da042) + o Improve robustness for refcounting (f1bc125) + o Remove a needless min_heap_shift_up_() call (7204b91) + o Increase MIN_BUFFER_SIZE to 512 (1024 on 64-bit) (2014ae4) + o Do not use evbuffer_expand() to add the first chain to a buffer (5c0ebb3) + o Make evbuffer_prepend handle empty buffers better (c87272b) + o Replace last_with_data with a slightly smarter version (b7442f8) + o Turn the increasingly complex *_CHAIN() macros into functions (96865c4) + o Rewrite evbuffer_expand and its users (d5ebcf3) + o Add evutil_tv_to_msec for safe conversion of timevals to milliseconds. (850c3ff Christopher Davis) + o Initialize last_with_datap correctly in evbuffer_overlapped (a0983b6) + o Replace EVUTIL_CLOSESOCKET macro with a function (899c1dc Sebastian Sjöberg) + o Move domain search state to evdns_request. (beaa14a Christopher Davis) + o Remove redundant checks for lock!=NULL before calling EVLOCK_LOCK (50ec59f) + o Rename current_base symbol to event_global_current_base_ (c16e684) + o Fix whitespace in evutil.c (935e150) + o Replace users of "int fd" with "evutil_socket_t fd" in portable code (c7cf6f0) + + + +Changes in 2.0.4-alpha (28 Feb 2010): + [Autogenerated from the Git log, sorted by hand.] +DOCUMENTATION + o Add stub header for 2.0.4-alpha changelog. (94d0065) + o Improve the README with more information and links. (0b42726) + o Add more people who wrote patches to the acknowledgments (0af10d5) + o Add a warning about the use of event_initialized. (f32b575) + o Add a LICENSE file so people can find our license easily (7067006) + o Add a new "hello world" sample program (becb9f9) + o Clarify status of example programs (d60a1bd) + o Update time-test.c to use event2 (f4190bf) + o Add the arc4random.c license to the LICENSE file. (e15e1e9) + +NEW FEATURES AND INTERFACE CHANGES + o Improved optional lock debugging. (0cd3bb9) + o Rate-limiting for bufferevents; group and individual limits are supported. (737c9cd) + o Testing code for bufferevent rate-limiting. (f0c0124) + o Make the initial nameserver probe timeout configurable. (1e56a32) + o Revise the locking API: deprecate the old locking callbacks and add trylock. (347952f) + o Do not make bufferevent_setfd implicitly disable EV_READ and EV_WRITE. (8274379) + o Do not ignore bufferevent_enable(EV_READ) before bufferevent_connect(). (4a5b534) + o Introduced evutil_make_socket_closeonexec() to preserve fd flags for F_SETFD. (d0939d2 Jardel Weyrich) + o evdns_getaddrinfo() now supports the /etc/hosts file. (72dd666) + o Look at the proper /etc/hosts file on windows. (66c02c7) + o Allow http connections to use evdns for hostname looksups. (c698b77) + o Changelist code to defer event changes until just before dispatch (27308aa) + o do not use a function to assign the evdns base; instead assign it via evhttp_connection_base_new() which is a new function introduced in 2.0 (5032e52) + o Functions to access more fields of struct event. (0683950) + o Make kqueue use changelists. (45e5ae3) + o Remove kqueue->pend_changes. (3225dfb) + o Minimize epoll_ctl calls by using changelist (c8c6a89) + o Add support for a "debug mode" to try to catch common errors. (cd17c3a) + o Note a missing ratelim function (361da8f) + o Add ev_[u]intptr_t to include/event2/util.h (1fa4c81) + o const-ify a few more functions in event.h (d38a7a1) + o Deprecate EVENT_FD and EVENT_SIGNAL. (f6b2694) + o Remove EVUTIL_CHECK_FMT. (6c21c89) + o Add EV_*_MAX macros to event2/util.h to expose limits for ev_* types. (aba1fff) o Functions to view and manipulate rate-limiting buckets. (85047a6) + o Add the rest of the integer limits, and add a test for them. (60742d5) + o Remove the 'flags' argument from evdns_base_set_option() (1dd7e6d) + o Add an arc4random implementation for use by evdns (d4de062) + o Use off_t for the length parameter of evbuffer_add_file (3fe60fd) + o Construct Windows locks using InitializeCriticalSectionAndSpinCount (32c6f1b) + o Expose view of current rate limit as constrained by group limit (162ce8a) + o Provide consistent, tested semantics for bufferevent timeouts (d328829) + +BUGFIXES AND TESTS + o Tolerate code that returns from a fatal_cb. (91fe23f) + o Parenthesize macro arguments more aggressively (07e9e9b) + o Fix memory-leak of signal handler array with kqueue. (e1ffbb8) + o Stop passing EVTHREAD_READ and EVTHREAD_WRITE to non-rw locks. (76cd2b7) + o Fix two use-after-free bugs in unit tests spoted by lock debugging (d84d838) + o Fix a locking bug in event_base_loop() (da1718b) + o Fix an evdns lock violation. (2df1f82 Zhuang Yuyao) + o Valgrind fix: Clear struct kevent before checking for OSX bug. (56771a3 William Ahern) + o Fix up evthread compilation on windows (bd6f1ba Roman Puls) + o Fix regress_iocp.c usage of old lock allocation macros. (31687b4 unknown) + o Update nmake makefile to build evthread.c (b62d979 unknown) + o Fix a crash when reading badly formatted resolve.conf; from Yasuoka Masahiko (6c7c579 Yasuoka Masahiko) + o Fix a snow leopard compile warning in the unit tests. (7ae9445) + o Fix compile on Snow Leopard with gcc warnings enabled (70cdfe4 Sebastian Hahn) + o Only define _GNU_SOURCE if it is not already defined. (ea6b1df Joachim Bauch) + o Update sample/signal-test.c to use newer APIs and not leak. (f6430ac Evan Jones) + o Fix a segfault when writing a very fragmented evbuffer onto an SSL (a6adeca Joachim Bauch) + o Fix a segfault when freeing SSL bufferevents in an unusual order (a773df5 Joachim Bauch) + o Drop install-sh from our git repo: a mismatched version could break "make dist" (6799527) + o Set all instances of the version number correctly. (5a112d3) + o Fix a few locking issues on windows. (c51bb3c unknown) + o Use evutil_socket_t, not int, when logging socket errors. (292467c) + o Fix up behavior of never-defered callbacks a little (390e056) + o Replace some cases of uint32_t with ev_uint32_t. (a47d88d) + o Fix compilation of devpoll.c by adding missing thread includes. (fee2c77 Dagobert Michelsen) + o Make evutil_make_socket_nonblocking() leave any other flags alone. (4c8b7cd Jardel Weyrich) + o Fix an fd leak in evconnlistener_new_bind(). (24fb502 Jardel Weyrich) + o Fix a bogus free in evutil_new_addrinfo() (0d64051 Jardel Weyrich) + o Adjusted fcntl() retval comparison on evutil_make_socket_nonblocking(). (4df7dbc Jardel Weyrich) + o Fix the code that allowed DNS options to not end with : (ee4953f) + o Fix crash bugs when a bufferevent's eventcb is not set. (2e8eeea) + o Fix test-ratelim compilation on Linux. (885b427) + o Fix compilation of rate-limiting code on win32. (165d30e) + o Eradicated the last free() call. Let mm_free() take care of deallocation. (0546ce1 Jardel Weyrich) + o Fix byte counts when mixing deferred and non-deferred evbuffer callbacks. (29151e6) + o Fixed a memory leak on windows threads implementation. The CRITICAL_SECTION was not being free'd in evthread_win32_lock_free(). (2f33e00 Jardel Weyrich) + o Fixed a fd leak in start_accepting(), plus cosmetic changes (4367a33 Jardel Weyrich) + o Improved error handling in evconnlistener_new_async(). Also keeping the fd open because it is not opened by this function, so the caller is responsible for closing it. Additionally, since evconnlistener_new_bind() creates a socket and passes it to the function above, it required error checking to close the same socket. (fec66f9 Jardel Weyrich) + o Don't use a bind address for nameservers on loopback (8d4aaf9) + o Fix compilation of rate-limit code when threading support is disabled (97a8c79) + o Detect setenv/unsetenv; skip main/base_environ test if we can't fake them. (7296971) + o Check more internal event_add() calls for failure (ff3f6cd) + o Fix windows and msvc build (5c7a7bc) + o Call event_debug_unassign on internal events (a19b4a0) + o Try to fix a warning in hash_debug_entry (137f2c6) + o Fix a dumb typo in ev_intptr_t definitions. (27c9a40) + o do not fail while sending on http connections the client closed. (93d7369) + o make evhttp_send() safe against terminated connections, too (3978180) + o Make Libevent 1.4.12 build on win32 with Unicode enabled. (000a33e Brodie Thiesfield) + o Fix some additional -DUNICODE issues on win32. (a7a9431) + o Add a check to make soure our EVUTIL_AI flags do not conflict with the native ones (c18490e) + o Always use our own gai_strerror() replacement. (6810bdb) + o Make RNG work when we have arc4random() but not arc4random_buf() (4ec8fea) + o validate close cb on server when client connection closes (2f782af) + o Fix two unlocked reads in evbuffer. (7116bf2) + o When working without a current event base, don't try to use IOCP listeners (cb52838) + o Fix getpid() usage on Windows (ff2a134) + o Add a unit test for secure rng. (48a29b6) + o Add some headers to fix freebsd compilation (b72be50) + o When connect() succeeds immediately, don't invoke the callback immediately. (7515de9) + o Suspend read/write on bufferevents during hostname lookup (db08f64) + o Make bufferevent_free() clear all callbacks immediately. (b2fbeb3) + o Fix some race conditions in persistent events and event_reinit (e2642f0) + o Fix a bug in resetting timeouts on persistent events when IO triggers. (38ec0a7) + o Add a test for timeouts on filtering bufferevents. (c02bfe1) + o Add test for periodic timers that get activated for other reasons (8fcb7a1) + o Use new timeval diff comparison function in bufferevent test (f3dfe46) + o Delete stack-alloced event in new unit test before returning. (7ffd387) + o Fix mingw compilation (23170a6) + o Try to define a sane _EVENT_SIZEOF_SIZE_T for msvc compilation (1e14f82) + o Fix arc4random compilation on MSVC. (98edb89) + o deal with connect() failing immediately (7bc48bf) + o Small cleanups on freebsd-connect-refused patch. (57b7248) + +BUILD AND DISTRIBUTION CHANGES + o Remove the contents of WIN32-Prj as unmaintained. (c69d5a5) + o Allow the user to redirect the verbose output of test/test.sh to a file (c382de6) + o Allow test.sh to be run as ./test/test.sh (7dfbe94) + o Never believe that we have pthreads on win32, even if gcc thinks we do. (78ed097) + o Make it compile under gcc --std=c89. (e2ca403) + o Fix a number of warnings from gcc -pedantic (918e9c5) + o Add the msvc-generated .lib files to .gitignore. (e244a2e) + o Add the "compile" script to gitignore. (1ba6bed) + +INTERNALS AND CODE CLEANUPS + o Add a .gitignore file. (ba34071) + o New EVTHREAD_TRY_LOCK function to try to grab a lock. (689fc09) + o Add the abilitity to mark some buffer callbacks as never-deferred. (438f9ed) + o Refactor our 'suspend operation' logic on bufferevents. (0d744aa) + o Simplify the read high-watermark checking. (5846bf6) + o Improve readability of evutil_unparse_protoname() (5a43df8 Jardel Weyrich) + o Expose our cached gettimeofday value with a new interface (47854a8) + o Whitespace fixes in test.sh (0b151a9) + o Enable branch-prediction hints with EVUTIL_UNLIKELY. (eaaf27f) + o Refactor code from evdns into a new internal "read a file" function. (0f7144f) + o Comestic changes in evconnlistener_new(), new_accepting_socket(), accepted_socket_invoke_user_cb() and iocp_listener_enable(). (510ab6b Jardel Weyrich) + o Add unit-test for bad_request bug fixed in 1.4 recently. (6cc79c6 Pavel Plesov) o Add a comment on evthread_enable_lock_debuging. (b9f43b2) + o Fix test.sh on shells without echo -n (94131e9) + o More unit tests for getaddrinfo_async: v4timeout and cancel. (a334b31) + o Make http use evconnlistener. (ec34533) + o move dns utility functions into a separate file so that we can use them for http testing (b822639) + o add a test for evhttp_connection_base_new with a dns_base (26714ca) + o forgot to add void to test function (78a50fe) + o Add a forgotten header (changelist-internal.h) (4b9f307) + o Remove some commented-out code in evutil (26e1b6f) + o Remove a needless include of rpc_compat.h (70a4a3e) + o Use less memory for each entry in a hashtable (a66e947) + o Try to untangle the logic in server_port_flush(). (439aea0) + o Use ev_[u]intptr_t types in place of [u]intptr_t (cef61a2) + o Reduce windows header includes in our own headers. (da6135e) + o clean up terminate_chunked test (e8a9782) + o Increment the submicro version number. (63e868e) + o Update event-config.h version number to match configure.in (aae7db5) + o Clean up formatting: Disallow space-before-tab. (8fdf09c) + o Clean up formatting: use tabs, not 8-spaces, to indent. (e5bbd40) + o Clean up formatting: remove trailing spaces (e5cf987) + o Clean up formatting: function/keyword spacing consistency. (4faeaea) + + + +Changes in 2.0.3-alpha (20 Nov 2009): + o Add a new code to support SSL/TLS on bufferevents, using the OpenSSL library (where available). + o Fix a bug where we didn't allocate enough memory in event_get_supported_methods(). + o Avoid segfault during failed allocation of locked evdns_base. (Found by Rocco Carbone.) + o Export new evutil_ascii_* functions to perform locale-independent character type operations. + o Try to compile better with MSVC: patches from Brodie Thiesfield + o New evconnlistener_get_fd function to expose a listener's associated socket. + o Expose an ev_socklen_t type for consistent use across platforms. + o Make bufferevent_socket_connect() work when the original fd was -1. + o Fix a bug in bufferevent_socket_connect() when the connection succeeds too quickly. + o Export an evutil_sockaddr_cmp() to compare to sockaddr objects for equality. + o Add a bufferevent_get_enabled() to tell what a bufferevent has been configured to do. + o Add an evbuffer_search_eol() function to locate the end of a line nondestructively. + o Add an evbuffer_search_range() function to search a bounded range of a buffer. + o Fix a rare crash bug in evdns. + o Have bufferevent_socket_connect() with no arguments put a bufferevent into connecting mode. + o Support sendfile on Solaris: patch from Caitlin Mercer. + o New functions to explicitly reference a socket used by an evhttp object. Patches from David Reiss. + o When we send a BEV_EVENT_CONNECTED to indicate connected status, we no longer invoke the write callback as well unless we actually wrote data too. + o If the kernel tells us that there are a negative number of bytes to read from a socket, do not believe it. Fixes bug 2841177; found by Alexander Pronchenkov. + o Do not detect whether we have monotonic clock support every time a new event base is created: instead do it only once. Patch taken from Chromium. + o Do not allocate the maximum event queue for the epoll backend at startup. Instead, start out accepting 32 events at a time, and double the queue's size when it seems that the OS is generating events faster than we're requesting them. Saves up to 374K per epoll-based event_base. Resolves bug 2839240. + o Treat an event with a negative fd as valid but untriggerable by Libevent. This is useful for applications that want to manually activate events. + o Fix compilation on Android, which forgot to define fd_mask in its sys/select.h + o Do not drop data from evbuffer when out of memory; reported by Jacek Masiulaniec + o New event_base_got_exit() and event_base_got_break() functions to tell whether an event loop exited because of an event_base_loopexit() or an event_base_loopbreak(). Patch from Ka-Hing Cheung. + o When adding or deleting an event from a non-main thread, only wake up the main thread when its behavior actually needs to change. + o Fix some bugs when using the old evdns interfaces to initialize the evdns module. + o Detect errors during bufferevent_connect(). Patch from Christopher Davis. + o Fix compilation for listener.h for C++ - missing extern "C". Patch from Ferenc Szalai. + o Make the event_base_loop() family of functions respect thread-safety better. This should clear up a few hard-to-debug race conditions. + o Fix a bug when using a specialized memory allocator on win32. + o Have the win32 select() backend label TCP-socket-connected events as EV_WRITE, not EV_READ. This should bring it in line with the other backends, and improve portability. Patch from Christopher Davis. + o Stop using enums as arguments or return values when what we mean is a bitfield of enum values. C++ doesn't believe that you can OR two enum values together and get another enum, and C++ takes its typing seriously. Patch from Christopher Davis. + o Add an API to replace all fatal calls to exit() with a user-provided panic function. + o Replace all assert() calls with a variant that is aware of the user-provided logging and panic functions. + o Add a return value to event_assign so that it can fail rather than asserting when the user gives it bad input. event_set still dies on bad input. + o The event_base_new() and event_base_new_with_config() functions now never call exit() on failure. For backward "compatibility", event_init() still does, but more consistently. + o Remove compat/sys/_time.h. It interfered with system headers on HPUX, and its functionality has been subsumed by event2/util.h and util-internal.h. + o Add a new bufferevent_socket_connect_hostname() to encapsulate the resolve-then-connect operation. + o Build kqueue.c correctly on GNU/kFreeBSD platforms. Patch pulled upstream from Debian. + o Alternative queue-based timeout algorithm for programs that use a large number of timeouts with the same value. + o New event_base_config option to disable the timeval cache entirely. + o Make EV_PERSIST timeouts more accurate: schedule the next event based on the scheduled time of the previous event, not based on the current time. + o Allow http.c to handle cases where getaddrinfo returns an IPv6 address. Patch from Ryan Phillips. + o Fix a problem with excessive memory allocation when using multiple event priorities. + o Default to using arc4random for DNS transaction IDs on systems that have it; from OpenBSD. + o Never check the environment when we're running setuid or setgid; from OpenBSD. + o Options passed to evdns_set_option() no longer need to end with a colon. + o Add an evutil_getaddrinfo() function to clone getaddrinfo on platforms that don't have it. + o Add an evdns_getaddrinfo() function to provide a nonblocking getaddrinfo using evdns, so programs can perform useful hostname lookup. + o Finally expose the IOCP-based bufferevent backend. It passes its unit tests, but probably still has some bugs remaining. Code by Nick Mathewson and Christopher Davis. + o Numerous other bugfixes. + o On FreeBSD and other OSes, connect can return ECONREFUSED immediately; instead of failing the function call, pretend with faileld in the callback. + o Fix a race condition in the pthreads test case; found by Nick Mathewson + o Remove most calls to event_err() in http and deal with memory errors instead + + + +Changes in 2.0.2-alpha (25 Jul 2009): + o Add a new flag to bufferevents to make all callbacks automatically deferred. + o Make evdns functionality locked, and automatically defer dns callbacks. + o Fix a possible free(NULL) when freeing an event_base with no signals. + o Add a flag to disable checking environment varibles when making an event_base + o Disallow setting less than 1 priority. + o Fix a bug when removing a timeout from the heap. [Patch from Marko Kreen] + o Use signal.h, not sys/signal.h. [Patch from mmadia] + o Try harder to build with certain older c99 compilers. + o Make sure that an event_config's flags field is always initialized to 0. [Bug report from Victor Goya] + o Avoid data corruption when reading data entirely into the second-to-last chain of an evbuffer. [Bug report from Victor Goya] + o Make sendfile work on FreeBSD + o Do not use vararg macros for accessing evrpc structures; this is not backwards compatible, but we did not promise any backwards compatibility for the rpc code. + o Actually define the event_config_set_flag() function. + o Try harder to compile with Visual C++. + o Move event_set() and its allies to event2/event_compat.h where they belong. + o Remove the event_gotsig code, which has long been deprecated and unused. + o Add an event_get_base() function to return the base assigned to an event. + o New function to automate connecting on a socket-based bufferevent. + o New functions to automate listening for incoming TCP connections. + o Do case-insensitive checks with a locale-independent comparison function. + o Rename the evbuffercb and everrorcb callbacks to bufferevent_data_cb and bufferevent_event_cb respectively. The old names are available in bufferevent_compat.h. + o Rename the EVBUFFER_* codes used by bufferevent event callbacks to BEV_EVENT_*, to avoid namespace collision with evbuffer flags. The old names are available in bufferevent_compat.h. + o Move the EVBUFFER_INPUT and EVBUFFER_OUTPUT macros to bufferevent_compat.h + o Add a bufferevent_getfd() function to mirror bufferevent_setfd() + o Make bufferevent_setfd() return an error code if the operation is not successful. + o Shave 22 bytes off struct event on 32-bit platforms by shrinking and re-ordering fields. The savings on 64-bit platforms is likely higher. + o Cap the maximum number of priorities at 256. + o Change the semantics of evbuffer_cb_set_flags() to be set-flag only; add a new evbuffer_cb_clear_flags() to remove set flags. + o Change the interface of evbuffer_add_reference so that the cleanup callback gets more information + o Revise the new evbuffer_reserve_space/evbuffer_commit_space() interfaces so that you can use them without causing extraneous copies or leaving gaps in the evbuffer. + o Add a new evbuffer_peek() interface to inspect data in an evbuffer without removing it. + o Fix a deadlock when suspending reads in a bufferevent due to a full buffer. (Spotted by Joachim Bauch.) + o Fix a memory error when freeing a thread-enabled event base with registered events. (Spotted by Joachim Bauch.) + o Try to contain degree of failure when running on a win32 version so heavily firewalled that we can't fake a socketpair. + o Activate fd events in a pseudorandom order with O(N) backends, so that we don't systematically favor low fds (select) or earlier-added fds (poll, win32). + o Replace some read()/write() instances with send()/recv() to work properly on win32. + o Set truncated flag correctly in evdns server replies. + o Raise RpcGenError in event_rpcgen.py; from jmanison and Zack Weinberg + o Fix preamble of rpcgen-generated files to rely on event2 includes; based on work by jmansion; patch from Zack Weinberg. + o Allow specifying the output filename for rpcgen; based on work by jmansion; patch from Zack Weinberg. + o Allow C identifiers as struct names; allow multiple comments in .rpc files; from Zack Weinberg + o Mitigate a race condition when using socket bufferevents in multiple threads. + o Use AC_SEARCH_LIBS, not AC_CHECK_LIB to avoid needless library use. + o Do not allow event_del(ev) to return while that event's callback is executing in another thread. This fixes a nasty race condition. + o event_get_supported_methods() now lists methods that have been disabled with the EVENT_NO* environment options. + o Rename encode_int[64] to evtag_encode_int[64] to avoid polluting the global namespace. The old method names are still available as macros in event2/tag_compat.h. + + + +Changes in 2.0.1-alpha (17 Apr 2009): + o free minheap on event_base_free(); from Christopher Layne + o debug cleanups in signal.c; from Christopher Layne + o provide event_base_new() that does not set the current_base global + o bufferevent_write now uses a const source argument; report from Charles Kerr + o improve documentation on event_base_loopexit; patch from Scott Lamb + o New function, event_{base_}loopbreak. Like event_loopexit, it makes an event loop stop executing and return. Unlike event_loopexit, it keeps subsequent pending events from getting executed. Patch from Scott Lamb + o Check return value of event_add in signal.c + o provide event_reinit() to reintialize an event_base after fork + o New function event_set_mem_functinons. It allows the user to give libevent replacement functions to use for memory management in place of malloc(), free(), etc. This should be generally useful for memory instrumentation, specialized allocators, and so on. + o The kqueue implementation now catches signals that are raised after event_add() is called but before the event_loop() call. This makes it match the other implementations. + o The kqueue implementation now restores original signal handlers correctly when its signal events are removed. + o Check return value of event_add in signal.c + o Add a more powerful evbuffer_readln as a replacement for evbuffer_readline. The new function handles more newline styles, and is more useful with buffers that may contain a nul characters. + o Do not mangle socket handles on 64-bit windows. + o The configure script now takes an --enable-gcc-warnigns option that turns on many optional gcc warnings. (Nick has been building with these for a while, but they might be useful to other developers.) + o move EV_PERSIST handling out of the event backends + o small improvements to evhttp documentation + o always generate Date and Content-Length headers for HTTP/1.1 replies + o set the correct event base for HTTP close events + o When building with GCC, use the "format" attribute to verify type correctness of calls to printf-like functions. + o Rewrite win32.c backend to be O(n lg n) rather than O(n^2). + o Removed obsoleted recalc code + o support for 32-bit tag numbers in rpc structures; this is wire compatible, but changes the API slightly. + o pull setters/getters out of RPC structures into a base class to which we just need to store a pointer; this reduces the memory footprint of these structures. + o prefix {encode,decode}_tag functions with evtag to avoid collisions + o fix a bug with event_rpcgen for integers + o Correctly handle DNS replies with no answers set (Fixes bug 1846282) + o add -Wstrict-aliasing to warnings and more cleanup + o removed linger from http server socket; reported by Ilya Martynov + o event_rpcgen now allows creating integer arrays + o support string arrays in event_rpcgen + o change evrpc hooking to allow pausing of RPCs; this will make it possible for the hook to do some meaning ful work; this is not backwards compatible. + o allow an http request callback to take ownership of a request structure + o allow association of meta data with RPC requests for hook processing + o associate more context for hooks to query such as the connection object + o remove pending timeouts on event_base_free() + o also check EAGAIN for Solaris' event ports; from W.C.A. Wijngaards + o devpoll and evport need reinit; tested by W.C.A Wijngaards + o event_base_get_method; from Springande Ulv + o Send CRLF after each chunk in HTTP output, for compliance with RFC2626. Patch from "propanbutan". Fixes bug 1894184. + o Add a int64_t parsing function, with unit tests, so we can apply Scott Lamb's fix to allow large HTTP values. + o Use a 64-bit field to hold HTTP content-lengths. Patch from Scott Lamb. + o Allow regression code to build even without Python installed + o remove NDEBUG ifdefs from evdns.c + o detect integer types properly on platforms without stdint.h + o udpate documentation of event_loop and event_base_loop; from Tani Hosokawa. + o simplify evbuffer by removing orig_buffer + o do not insert event into list when evsel->add fails + o add support for PUT/DELETE requests; from Josh Rotenberg + o introduce evhttp_accept_socket() to accept from an already created socket + o include Content-Length in reply for HTTP/1.0 requests with keep-alive + o increase listen queue for http sockets to 128; if that is not enough the evhttp_accpet_socket() api can be used with a prepared socket. + o Patch from Tani Hosokawa: make some functions in http.c threadsafe. + o test support for PUT/DELETE requests; from Josh Rotenberg + o rewrite of the evbuffer code to reduce memory copies + o Some older Solaris versions demand that _REENTRANT be defined to get strtok_r(); do so. + o Do not free the kqop file descriptor in other processes, also allow it to be 0; from Andrei Nigmatulin + o Provide OpenSSL style support for multiple threads accessing the same event_base + o make event_rpcgen.py generate code include event-config.h; reported by Sam Banks. + o switch thread support so that locks get allocated as they are needed. + o make event methods static so that they are not exported; from Andrei Nigmatulin + o make RPC replies use application/octet-stream as mime type + o do not delete uninitialized timeout event in evdns + o Correct the documentation on buffer printf functions. + o Don't warn on unimplemented epoll_create(): this isn't a problem, just a reason to fall back to poll or select. + o Correctly handle timeouts larger than 35 minutes on Linux with epoll.c. This is probably a kernel defect, but we'll have to support old kernels anyway even if it gets fixed. + o Make name_from_addr() threadsafe in http.c + o Add new thread-safe interfaces to evdns functions. + o Make all event_tagging interfaces threadsafe. + o Rename internal memory management functions. + o New functions (event_assign, event_new, event_free) for use by apps that want to be safely threadsafe, or want to remain ignorant of the contents of struct event. + o introduce bufferevent_read_buffer; allows reading without memory copy. + o expose bufferevent_setwatermark via header files and fix high watermark on read + o fix a bug in buffrevent read water marks and add a test for them + o fix a bug in which bufferevent_write_buffer would not schedule a write event + o provide bufferevent_input and bufferevent_output without requiring knowledge of the structure + o introduce bufferevent_setcb and bufferevent_setfd to allow better manipulation of bufferevents + o convert evhttp_connection to use bufferevents. + o use libevent's internal timercmp on all platforms, to avoid bugs on old platforms where timercmp(a,b,<=) is buggy. + o Remove the never-exported, never-used evhttp_hostportfile function. + o Support input/output filters for bufferevents; somewhat similar to libio's model. This will allow us to implement SSL, compression, etc, transparently to users of bufferevents such as the http layer. + o allow connections to be removed from an rpc pool + o add new evtimer_assign, signal_assign, evtimer_new, and signal_new functions to manipulate timer and signal events, analagous to the now-recommended event_assign and event_new + o switch internal uses of event_set over to use event_assign. + o introduce evbuffer_contiguous_space() api that tells a user how much data is available in the first buffer chain + o introduce evbuffer_reserve_space() and evbuffer_commit_space() to make processing in filters more efficient. + o reduce system calls for getting current time by caching it. + o separate signal events from io events; making the code less complex. + o support for periodic timeouts + o support for virtual HTTP hosts. + o turn event_initialized() into a function, and add function equivalents to EVENT_SIGNAL and EVENT_FD so that people don't need to include event_struct.h + o Build test directory correctly with CPPFLAGS set. + o Provide an API for retrieving the supported event mechanisms. + o event_base_new_with_config() and corresponding config APIs. + o migrate the evhttp header to event2/ but accessors are still missing. + o deprecate timeout_* event functions by moving them to event_compat.h + o Move windows gettimeofday replacement into a new evutil_gettimeofday(). + o Make configure script work on IRIX. + o provide a method for canceling ongoing http requests. + o Make vsnprintf() returns consistent on win32. + o Fix connection keep-alive behavior for HTTP/1.0 + o Fix use of freed memory in event_reinit; pointed out by Peter Postma + o constify struct timeval * where possible + o make event_get_supported_methods obey environment variables + o support for edge-triggered events on epoll and kqueue backends: patch from Valery Kholodkov + o support for selecting event backends by their features, and for querying the features of a backend. + o change failing behavior of event_base_new_with_config: if a config is provided and no backend is selected, return NULL instead of aborting. + o deliver partial data to request callbacks when chunked callback is set even if there is no chunking on the http level; allows cancelation of requests from within the chunked callback; from Scott Lamb. + o allow min_heap_erase to be called on removed members; from liusifan. + o Rename INPUT and OUTPUT to EVRPC_INPUT and EVRPC_OUTPUT. Retain INPUT/OUTPUT aliases on on-win32 platforms for backwards compatibility. + o Do not use SO_REUSEADDR when connecting + o Support 64-bit integers in RPC structs + o Correct handling of trailing headers in chunked replies; from Scott Lamb. + o Support multi-line HTTP headers; based on a patch from Moshe Litvin + o Reject negative Content-Length headers; anonymous bug report + o Detect CLOCK_MONOTONIC at runtime for evdns; anonymous bug report + o Various HTTP correctness fixes from Scott Lamb + o Fix a bug where deleting signals with the kqueue backend would cause subsequent adds to fail + o Support multiple events listening on the same signal; make signals regular events that go on the same event queue; problem report by Alexander Drozdov. + o Fix a problem with epoll() and reinit; problem report by Alexander Drozdov. + o Fix off-by-one errors in devpoll; from Ian Bell + o Make event_add not change any state if it fails; reported by Ian Bell. + o Fix a bug where headers arriving in multiple packets were not parsed; fix from Jiang Hong; test by me. + o Match the query in DNS replies to the query in the request; from Vsevolod Stakhov. + o Add new utility functions to correctly observe and log winsock errors. + o Do not remove Accept-Encoding header + o Clear the timer cache on entering the event loop; reported by Victor Chang + o Only bind the socket on connect when a local address has been provided; reported by Alejo Sanchez + o Allow setting of local port for evhttp connections to support millions of connections from a single system; from Richard Jones. + o Clear the timer cache when leaving the event loop; reported by Robin Haberkorn + o Fix a typo in setting the global event base; reported by lance. + o Set the 0x20 bit on outgoing alphabetic characters in DNS requests randomly, and insist on a match in replies. This helps resist DNS poisoning attacks. + o Make the http connection close detection work properly with bufferevents and fix a potential memory leak associated with it. + o Restructure the event backends so that they do not need to keep track of events themselves, as a side effect multiple events can use the same fd or signal. + o Add generic implementations for parsing and emiting IPv6 addresses on platforms that do not have inet_ntop and/or inet_pton. + o Allow DNS servers that have IPv6 addresses. + o Add an evbuffer_write_atmost() function to write a limited number of bytes to an fd. + o Refactor internal notify-main-thread logic to prefer eventfd to pipe, then pipe to socketpair, and only use socketpairs as a last resort. + o Try harder to pack all evbuffer reads into as few chains as possible, using readv/WSARecv as appropriate. + o New evthread_use_windows_threads() and evthread_use_pthreads() functions to set up the evthread callbacks with reasonable defaults. + o Change the semantics of timeouts in conjunction with EV_PERSIST; timeouts in that case will now repeat until deleted. + o sendfile, mmap and memory reference support for evbuffers. + o New evutil_make_listen_socket_reuseable() to abstract SO_REUSEADDR. + o New bind-to option to allow DNS clients to bind to an arbitrary port for outgoing requests. + o evbuffers can now be "frozen" to prevent operations at one or both ends. + o Bufferevents now notice external attempts to add data to an inbuf or remove it from an outbuf, and stop them. + o Fix parsing of queries where the encoded queries contained \r, \n or + + o Do not allow internal events to starve lower-priority events. + diff --git a/libs/libevent/docs/Doxyfile b/libs/libevent/docs/Doxyfile new file mode 100644 index 0000000000..d9d6603459 --- /dev/null +++ b/libs/libevent/docs/Doxyfile @@ -0,0 +1,257 @@ +# Doxyfile 1.5.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = libevent + +# Place all output under 'doxygen/' + +OUTPUT_DIRECTORY = doxygen/ + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = include/ + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = \ + include/event2/buffer.h \ + include/event2/buffer_compat.h \ + include/event2/bufferevent.h \ + include/event2/bufferevent_compat.h \ + include/event2/bufferevent_ssl.h \ + include/event2/dns.h \ + include/event2/dns_compat.h \ + include/event2/event.h \ + include/event2/event_compat.h \ + include/event2/http.h \ + include/event2/http_compat.h \ + include/event2/listener.h \ + include/event2/rpc.h \ + include/event2/rpc_compat.h \ + include/event2/tag.h \ + include/event2/tag_compat.h \ + include/event2/thread.h \ + include/event2/util.h + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = TAILQ_ENTRY RB_ENTRY EVENT_DEFINED_TQENTRY_ EVENT_IN_DOXYGEN_ + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES diff --git a/libs/libevent/docs/LICENSE b/libs/libevent/docs/LICENSE new file mode 100644 index 0000000000..402ca50896 --- /dev/null +++ b/libs/libevent/docs/LICENSE @@ -0,0 +1,99 @@ +Libevent is available for use under the following license, commonly known +as the 3-clause (or "modified") BSD license: + +============================== +Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> +Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + +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. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +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. +============================== + +Portions of Libevent are based on works by others, also made available by +them under the three-clause BSD license above. The copyright notices are +available in the corresponding source files; the license is as above. Here's +a list: + +log.c: + Copyright (c) 2000 Dug Song <dugsong@monkey.org> + Copyright (c) 1993 The Regents of the University of California. + +strlcpy.c: + Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + +win32select.c: + Copyright (c) 2003 Michael A. Davis <mike@datanerds.net> + +evport.c: + Copyright (c) 2007 Sun Microsystems + +ht-internal.h: + Copyright (c) 2002 Christopher Clark + +minheap-internal.h: + Copyright (c) 2006 Maxim Yegorushkin <maxim.yegorushkin@gmail.com> + +============================== + +The arc4module is available under the following, sometimes called the +"OpenBSD" license: + + Copyright (c) 1996, David Mazieres <dm@uun.org> + Copyright (c) 2008, Damien Miller <djm@openbsd.org> + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +============================== + +The Windows timer code is based on code from libutp, which is +distributed under this license, sometimes called the "MIT" license. + + +Copyright (c) 2010 BitTorrent, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/libs/libevent/docs/Makefile.am b/libs/libevent/docs/Makefile.am new file mode 100644 index 0000000000..3053cc016a --- /dev/null +++ b/libs/libevent/docs/Makefile.am @@ -0,0 +1,303 @@ +# Makefile.am for libevent +# Copyright 2000-2007 Niels Provos +# Copyright 2007-2012 Niels Provos and Nick Mathewson +# +# See LICENSE for copying information. + +# 'foreign' means that we're not enforcing GNU package rules strictly. +# '1.9' means that we need automake 1.9 or later (and we do). +AUTOMAKE_OPTIONS = foreign 1.9 subdir-objects + +ACLOCAL_AMFLAGS = -I m4 + +# This is the "Release" of the Libevent ABI. It takes precedence over +# the VERSION_INFO, so that two versions of Libevent with the same +# "Release" are never binary-compatible. +# +# This number incremented once for the 2.0 release candidate, and +# will increment for each series until we revise our interfaces enough +# that we can seriously expect ABI compatibility between series. +# +RELEASE = -release 2.1 + +# This is the version info for the libevent binary API. It has three +# numbers: +# Current -- the number of the binary API that we're implementing +# Revision -- which iteration of the implementation of the binary +# API are we supplying? +# Age -- How many previous binary API versions do we also +# support? +# +# To increment a VERSION_INFO (current:revision:age): +# If the ABI didn't change: +# Return (current:revision+1:age) +# If the ABI changed, but it's backward-compatible: +# Return (current+1:0:age+1) +# If the ABI changed and it isn't backward-compatible: +# Return (current+1:0:0) +# +# Once an RC is out, DO NOT MAKE ANY ABI-BREAKING CHANGES IN THAT SERIES +# UNLESS YOU REALLY REALLY HAVE TO. +VERSION_INFO = 5:0:0 + +# History: RELEASE VERSION_INFO +# 2.0.1-alpha -- 2.0 1:0:0 +# 2.0.2-alpha -- 2:0:0 +# 2.0.3-alpha -- 2:0:0 (should have incremented; didn't.) +# 2.0.4-alpha -- 3:0:0 +# 2.0.5-beta -- 4:0:0 +# 2.0.6-rc -- 2.0 2:0:0 +# 2.0.7-rc -- 2.0 3:0:1 +# 2.0.8-rc -- 2.0 4:0:2 +# 2.0.9-rc -- 2.0 5:0:0 (ABI changed slightly) +# 2.0.10-stable-- 2.0 5:1:0 (No ABI change) +# 2.0.11-stable-- 2.0 6:0:1 (ABI changed, backward-compatible) +# 2.0.12-stable-- 2.0 6:1:1 (No ABI change) +# 2.0.13-stable-- 2.0 6:2:1 (No ABI change) +# 2.0.14-stable-- 2.0 6:3:1 (No ABI change) +# 2.0.15-stable-- 2.0 6:3:1 (Forgot to update :( ) +# 2.0.16-stable-- 2.0 6:4:1 (No ABI change) +# 2.0.17-stable-- 2.0 6:5:1 (No ABI change) +# 2.0.18-stable-- 2.0 6:6:1 (No ABI change) +# 2.0.19-stable-- 2.0 6:7:1 (No ABI change) +# 2.0.20-stable-- 2.0 6:8:1 (No ABI change) +# 2.0.21-stable-- 2.0 6:9:1 (No ABI change) +# +# For Libevent 2.1: +# 2.1.1-alpha -- 2.1 1:0:0 +# 2.1.2-alpha -- 2.1 1:0:0 (should have been 2:0:1) +# 2.1.3-alpha -- 2.1 3:0:0 (ABI changed slightly) +# 2.1.4-alpha -- 2.1 4:0:0 (ABI changed slightly) +# 2.1.5-beta -- 2.1 5:0:0 (ABI changed slightly) + +# ABI version history for this package effectively restarts every time +# we change RELEASE. Version 1.4.x had RELEASE of 1.4. +# +# Ideally, we would not be using RELEASE at all; instead we could just +# use the VERSION_INFO field to label our backward-incompatible ABI +# changes, and those would be few and far between. Unfortunately, +# Libevent still exposes far too many volatile structures in its +# headers, so we pretty much have to assume that most development +# series will break ABI compatibility. For now, it's simplest just to +# keep incrementing the RELEASE between series and resetting VERSION_INFO. +# +# Eventually, when we get to the point where the structures in the +# headers are all non-changing (or not there at all!), we can shift to +# a more normal worldview where backward-incompatible ABI changes are +# nice and rare. For the next couple of years, though, 'struct event' +# is user-visible, and so we can pretty much guarantee that release +# series won't be binary-compatible. + +if INSTALL_LIBEVENT +dist_bin_SCRIPTS = event_rpcgen.py +endif + +pkgconfigdir=$(libdir)/pkgconfig +LIBEVENT_PKGCONFIG=libevent.pc + +# These sources are conditionally added by configure.ac or conditionally +# included from other files. +PLATFORM_DEPENDENT_SRC = \ + arc4random.c \ + epoll_sub.c + +EXTRA_DIST = \ + ChangeLog-1.4 \ + ChangeLog-2.0 \ + Doxyfile \ + LICENSE \ + Makefile.nmake test/Makefile.nmake \ + autogen.sh \ + event_rpcgen.py \ + libevent.pc.in \ + make-event-config.sed \ + whatsnew-2.0.txt \ + whatsnew-2.1.txt \ + $(PLATFORM_DEPENDENT_SRC) + +LIBEVENT_LIBS_LA = libevent.la libevent_core.la libevent_extra.la +if PTHREADS +LIBEVENT_LIBS_LA += libevent_pthreads.la +LIBEVENT_PKGCONFIG += libevent_pthreads.pc +endif +if OPENSSL +LIBEVENT_LIBS_LA += libevent_openssl.la +LIBEVENT_PKGCONFIG += libevent_openssl.pc +endif + +if INSTALL_LIBEVENT +lib_LTLIBRARIES = $(LIBEVENT_LIBS_LA) +pkgconfig_DATA = $(LIBEVENT_PKGCONFIG) +else +noinst_LTLIBRARIES = $(LIBEVENT_LIBS_LA) +endif + +EXTRA_SOURCE= +noinst_HEADERS= +noinst_PROGRAMS= +EXTRA_PROGRAMS= +CLEANFILES= +DISTCLEANFILES= +BUILT_SOURCES = +include include/include.am +include sample/include.am +include test/include.am + +if BUILD_WIN32 + +SYS_LIBS = -lws2_32 -lshell32 -ladvapi32 +SYS_SRC = win32select.c evthread_win32.c buffer_iocp.c event_iocp.c \ + bufferevent_async.c +SYS_INCLUDES = -IWIN32-Code -IWIN32-Code/nmake + +else + +SYS_LIBS = +SYS_SRC = +SYS_INCLUDES = + +endif + +if STRLCPY_IMPL +SYS_SRC += strlcpy.c +endif +if SELECT_BACKEND +SYS_SRC += select.c +endif +if POLL_BACKEND +SYS_SRC += poll.c +endif +if DEVPOLL_BACKEND +SYS_SRC += devpoll.c +endif +if KQUEUE_BACKEND +SYS_SRC += kqueue.c +endif +if EPOLL_BACKEND +SYS_SRC += epoll.c +endif +if EVPORT_BACKEND +SYS_SRC += evport.c +endif +if SIGNAL_SUPPORT +SYS_SRC += signal.c +endif + +BUILT_SOURCES += include/event2/event-config.h + +include/event2/event-config.h: config.h make-event-config.sed + $(AM_V_GEN)test -d include/event2 || $(MKDIR_P) include/event2 + $(AM_V_at)$(SED) -f $(srcdir)/make-event-config.sed < config.h > $@T + $(AM_V_at)mv -f $@T $@ + +CORE_SRC = \ + buffer.c \ + bufferevent.c \ + bufferevent_filter.c \ + bufferevent_pair.c \ + bufferevent_ratelim.c \ + bufferevent_sock.c \ + event.c \ + evmap.c \ + evthread.c \ + evutil.c \ + evutil_rand.c \ + evutil_time.c \ + listener.c \ + log.c \ + $(SYS_SRC) + +EXTRAS_SRC = \ + evdns.c \ + event_tagging.c \ + evrpc.c \ + http.c + +if BUILD_WITH_NO_UNDEFINED +NO_UNDEFINED = -no-undefined +MAYBE_CORE = libevent_core.la +else +NO_UNDEFINED = +MAYBE_CORE = +endif + +GENERIC_LDFLAGS = -version-info $(VERSION_INFO) $(RELEASE) $(NO_UNDEFINED) + +libevent_la_SOURCES = $(CORE_SRC) $(EXTRAS_SRC) +libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS) +libevent_la_LDFLAGS = $(GENERIC_LDFLAGS) + +libevent_core_la_SOURCES = $(CORE_SRC) +libevent_core_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS) +libevent_core_la_LDFLAGS = $(GENERIC_LDFLAGS) + +if PTHREADS +libevent_pthreads_la_SOURCES = evthread_pthread.c +libevent_pthreads_la_LIBADD = $(MAYBE_CORE) +libevent_pthreads_la_LDFLAGS = $(GENERIC_LDFLAGS) +endif + +libevent_extra_la_SOURCES = $(EXTRAS_SRC) +libevent_extra_la_LIBADD = $(MAYBE_CORE) $(SYS_LIBS) +libevent_extra_la_LDFLAGS = $(GENERIC_LDFLAGS) + +if OPENSSL +libevent_openssl_la_SOURCES = bufferevent_openssl.c +libevent_openssl_la_LIBADD = $(MAYBE_CORE) $(OPENSSL_LIBS) +libevent_openssl_la_LDFLAGS = $(GENERIC_LDFLAGS) +libevent_openssl_la_CPPFLAGS = $(AM_CPPFLAGS) $(OPENSSL_INCS) +endif + +noinst_HEADERS += \ + WIN32-Code/nmake/evconfig-private.h \ + WIN32-Code/nmake/event2/event-config.h \ + WIN32-Code/tree.h \ + bufferevent-internal.h \ + changelist-internal.h \ + compat/sys/queue.h \ + defer-internal.h \ + epolltable-internal.h \ + evbuffer-internal.h \ + evconfig-private.h \ + event-internal.h \ + evmap-internal.h \ + evrpc-internal.h \ + evsignal-internal.h \ + evthread-internal.h \ + ht-internal.h \ + http-internal.h \ + iocp-internal.h \ + ipv6-internal.h \ + kqueue-internal.h \ + log-internal.h \ + minheap-internal.h \ + mm-internal.h \ + ratelim-internal.h \ + ratelim-internal.h \ + strlcpy-internal.h \ + time-internal.h \ + util-internal.h + +EVENT1_HDRS = \ + include/evdns.h \ + include/event.h \ + include/evhttp.h \ + include/evrpc.h \ + include/evutil.h + +if INSTALL_LIBEVENT +include_HEADERS = $(EVENT1_HDRS) +else +noinst_HEADERS += $(EVENT1_HDRS) +endif + +AM_CPPFLAGS = -I$(srcdir)/compat -I$(srcdir)/include -I./include $(SYS_INCLUDES) + +verify: check + +doxygen: FORCE + doxygen $(srcdir)/Doxyfile +FORCE: + +DISTCLEANFILES += *~ libevent.pc ./include/event2/event-config.h + diff --git a/libs/libevent/docs/Makefile.nmake b/libs/libevent/docs/Makefile.nmake new file mode 100644 index 0000000000..f27cd61949 --- /dev/null +++ b/libs/libevent/docs/Makefile.nmake @@ -0,0 +1,82 @@ +# WATCH OUT! This makefile is a work in progress. -*- makefile -*- +# +# I'm not very knowledgeable about MSVC and nmake beyond their most basic +# aspects. If anything here looks wrong to you, please let me know. + +# If OPENSSL_DIR is not set, builds without OpenSSL support. If you want +# OpenSSL support, you can set the OPENSSL_DIR variable to where you +# installed OpenSSL. This can be done in the environment: +# set OPENSSL_DIR=c:\openssl +# Or on the nmake command line: +# nmake OPENSSL_DIR=C:\openssl -f Makefile.nmake +# Or by uncommenting the following line here in the makefile... + +# OPENSSL_DIR=c:\openssl + +!IFDEF OPENSSL_DIR +SSL_CFLAGS=/I$(OPENSSL_DIR)\include /DEVENT__HAVE_OPENSSL +!ELSE +SSL_CFLAGS= +!ENDIF + +# Needed for correctness +CFLAGS=/IWIN32-Code /IWIN32-Code/nmake /Iinclude /Icompat /DHAVE_CONFIG_H /I. $(SSL_CFLAGS) + +# For optimization and warnings +CFLAGS=$(CFLAGS) /Ox /W3 /wd4996 /nologo + +# XXXX have a debug mode + +LIBFLAGS=/nologo + +CORE_OBJS=event.obj buffer.obj bufferevent.obj bufferevent_sock.obj \ + bufferevent_pair.obj listener.obj evmap.obj log.obj evutil.obj \ + strlcpy.obj signal.obj bufferevent_filter.obj evthread.obj \ + bufferevent_ratelim.obj evutil_rand.obj evutil_time.obj +WIN_OBJS=win32select.obj evthread_win32.obj buffer_iocp.obj \ + event_iocp.obj bufferevent_async.obj +EXTRA_OBJS=event_tagging.obj http.obj evdns.obj evrpc.obj + +!IFDEF OPENSSL_DIR +SSL_OBJS=bufferevent_openssl.obj +SSL_LIBS=libevent_openssl.lib +!ELSE +SSL_OBJS= +SSL_LIBS= +!ENDIF + +ALL_OBJS=$(CORE_OBJS) $(WIN_OBJS) $(EXTRA_OBJS) $(SSL_OBJS) +STATIC_LIBS=libevent_core.lib libevent_extras.lib libevent.lib $(SSL_LIBS) + + +all: static_libs tests + +static_libs: $(STATIC_LIBS) + +libevent_core.lib: $(CORE_OBJS) $(WIN_OBJS) + lib $(LIBFLAGS) $(CORE_OBJS) $(WIN_OBJS) /out:libevent_core.lib + +libevent_extras.lib: $(EXTRA_OBJS) + lib $(LIBFLAGS) $(EXTRA_OBJS) /out:libevent_extras.lib + +libevent.lib: $(CORE_OBJS) $(WIN_OBJS) $(EXTRA_OBJS) + lib $(LIBFLAGS) $(CORE_OBJS) $(EXTRA_OBJS) $(WIN_OBJS) /out:libevent.lib + +libevent_openssl.lib: $(SSL_OBJS) + lib $(LIBFLAGS) $(SSL_OBJS) /out:libevent_openssl.lib + +clean: + del $(ALL_OBJS) + del $(STATIC_LIBS) + cd test + $(MAKE) /F Makefile.nmake clean + cd .. + +tests: + cd test +!IFDEF OPENSSL_DIR + $(MAKE) OPENSSL_DIR=$(OPENSSL_DIR) /F Makefile.nmake +!ELSE + $(MAKE) /F Makefile.nmake +!ENDIF + cd .. diff --git a/libs/libevent/docs/README.md b/libs/libevent/docs/README.md new file mode 100644 index 0000000000..fdd777ee8d --- /dev/null +++ b/libs/libevent/docs/README.md @@ -0,0 +1,397 @@ +<p align="center"> + <img src="https://strcpy.net/libevent3.png" alt="libevent logo"/> +</p> + + + +[![Appveyor Win32 Build Status](https://ci.appveyor.com/api/projects/status/github/libevent/libevent?branch=master&svg=true)](https://ci.appveyor.com/project/nmathewson/libevent) +[![Travis Build Status](https://travis-ci.org/libevent/libevent.svg?branch=master)](https://travis-ci.org/libevent/libevent) + + + +# 0. BUILDING AND INSTALLATION (Briefly) + +## Autoconf + + $ ./configure + $ make + $ make verify # (optional) + $ sudo make install + +## Cmake (General) + + +The following Libevent specific Cmake variables ar as follows (the values being +the default). + +``` +# Installation directory for executables +EVENT_INSTALL_BIN_DIR:PATH=bin + +# Installation directory for CMake files +EVENT_INSTALL_CMAKE_DIR:PATH=lib/cmake/libevent + +## Installation directory for header files +EVENT_INSTALL_INCLUDE_DIR:PATH=include + +## Installation directory for libraries +EVENT_INSTALL_LIB_DIR:PATH=lib + +## Define if libevent should be built with shared libraries instead of archives +EVENT__BUILD_SHARED_LIBRARIES:BOOL=OFF + +# Enable running gcov to get a test coverage report (only works with +# GCC/CLang). Make sure to enable -DCMAKE_BUILD_TYPE=Debug as well. +EVENT__COVERAGE:BOOL=OFF + +# Defines if libevent should build without the benchmark exectuables +EVENT__DISABLE_BENCHMARK:BOOL=OFF + +# Define if libevent should build without support for a debug mode +EVENT__DISABLE_DEBUG_MODE:BOOL=OFF + +# Define if libevent should not allow replacing the mm functions +EVENT__DISABLE_MM_REPLACEMENT:BOOL=OFF + +# Define if libevent should build without support for OpenSSL encrpytion +EVENT__DISABLE_OPENSSL:BOOL=ON + +# Disable the regress tests +EVENT__DISABLE_REGRESS:BOOL=OFF + +# Disable sample files +EVENT__DISABLE_SAMPLES:BOOL=OFF + +# If tests should be compiled or not +EVENT__DISABLE_TESTS:BOOL=OFF + +# Define if libevent should not be compiled with thread support +EVENT__DISABLE_THREAD_SUPPORT:BOOL=OFF + +# Enables verbose debugging +EVENT__ENABLE_VERBOSE_DEBUG:BOOL=OFF + +# When crosscompiling forces running a test program that verifies that Kqueue +# works with pipes. Note that this requires you to manually run the test program +# on the the cross compilation target to verify that it works. See cmake +# documentation for try_run for more details +EVENT__FORCE_KQUEUE_CHECK:BOOL=OFF + +# set EVENT_STAGE_VERSION +EVENT__STAGE_VERSION:STRING=beta +``` + +__More variables can be found by running `cmake -LAH <sourcedir_path>`__ + + +## CMake (Windows) + +Install CMake: <http://www.cmake.org> + + + $ md build && cd build + $ cmake -G "Visual Studio 10" .. # Or whatever generator you want to use cmake --help for a list. + $ start libevent.sln + +## CMake (Unix) + + $ mkdir build && cd build + $ cmake .. # Default to Unix Makefiles. + $ make + $ make verify # (optional) + + +# 1. BUILDING AND INSTALLATION (In Depth) + +## Autoconf + +To build libevent, type + + $ ./configure && make + + + (If you got libevent from the git repository, you will + first need to run the included "autogen.sh" script in order to + generate the configure script.) + +You can run the regression tests by running + + $ make verify + +Install as root via + + $ make install + +Before reporting any problems, please run the regression tests. + +To enable the low-level tracing build the library as: + + $ CFLAGS=-DUSE_DEBUG ./configure [...] + +Standard configure flags should work. In particular, see: + + --disable-shared Only build static libraries + --prefix Install all files relative to this directory. + + +The configure script also supports the following flags: + + --enable-gcc-warnings Enable extra compiler checking with GCC. + --disable-malloc-replacement + Don't let applications replace our memory + management functions + --disable-openssl Disable support for OpenSSL encryption. + --disable-thread-support Don't support multithreaded environments. + +## CMake (Windows) + +(Note that autoconf is currently the most mature and supported build +enviroment for libevent; the cmake instructions here are new and +experimental, though they _should_ be solid. We hope that cmake will +still be supported in future versions of Libevent, and will try to +make sure that happens.) + +First of all install <http://www.cmake.org>. + +To build libevent using Microsoft Visual studio open the "Visual Studio Command prompt" and type: + +``` +$ cd <libevent source dir> +$ mkdir build && cd build +$ cmake -G "Visual Studio 10" .. # Or whatever generator you want to use cmake --help for a list. +$ start libevent.sln +``` + +In the above, the ".." refers to the dir containing the Libevent source code. +You can build multiple versions (with different compile time settings) from the same source tree +by creating other build directories. + +It is highly recommended to build "out of source" when using +CMake instead of "in source" like the normal behaviour of autoconf for this reason. + +The "NMake Makefiles" CMake generator can be used to build entirely via the command line. + +To get a list of settings available for the project you can type: + +``` +$ cmake -LH .. +``` + +### GUI + +CMake also provides a GUI that lets you specify the source directory and output (binary) directory +that the build should be placed in. + +### OpenSSL support + +To build Libevent with OpenSSL support you will need to have OpenSSL binaries available when building, +these can be found here: <http://www.openssl.org/related/binaries.html> + +# 2. USEFUL LINKS: + +For the latest released version of Libevent, see the official website at +<http://libevent.org/> . + +There's a pretty good work-in-progress manual up at + <http://www.wangafu.net/~nickm/libevent-book/> . + +For the latest development versions of Libevent, access our Git repository +via + +``` +$ git clone https://github.com/libevent/libevent.git +``` + +You can browse the git repository online at: + +<https://github.com/libevent/Libevent> + +To report bugs, issues, or ask for new features: + +__Patches__: https://github.com/libevent/libevent/pulls +> OK, those are not really _patches_ You fork, modify, and hit the "Create Pull Request" button. +> You can still submit normal git patchs via the mailing list. + +__Bugs, Features [RFC], and Issus__: https://github.com/libevent/libevent/issues +> Or you can do it via the mailing list. + +There's also a libevent-users mailing list for talking about Libevent +use and development: + +<http://archives.seul.org/libevent/users/> + +# 3. ACKNOWLEDGMENTS + +The following people have helped with suggestions, ideas, code or +fixing bugs: + + * Samy Al Bahra + * Antony Antony + * Jacob Appelbaum + * Arno Bakker + * Weston Andros Adamson + * William Ahern + * Ivan Andropov + * Sergey Avseyev + * Avi Bab + * Joachim Bauch + * Andrey Belobrov + * Gilad Benjamini + * Stas Bekman + * Denis Bilenko + * Julien Blache + * Kevin Bowling + * Tomash Brechko + * Kelly Brock + * Ralph Castain + * Adrian Chadd + * Lawnstein Chan + * Shuo Chen + * Ka-Hing Cheung + * Andrew Cox + * Paul Croome + * George Danchev + * Andrew Danforth + * Ed Day + * Christopher Davis + * Mike Davis + * Frank Denis + * Antony Dovgal + * Mihai Draghicioiu + * Alexander Drozdov + * Mark Ellzey + * Shie Erlich + * Leonid Evdokimov + * Juan Pablo Fernandez + * Christophe Fillot + * Mike Frysinger + * Remi Gacogne + * Artem Germanov + * Alexander von Gernler + * Diego Giagio + * Artur Grabowski + * Diwaker Gupta + * Kuldeep Gupta + * Sebastian Hahn + * Dave Hart + * Greg Hazel + * Nicholas Heath + * Michael Herf + * Sebastian Hahn + * Savg He + * Mark Heily + * Maxime Henrion + * Michael Herf + * Greg Hewgill + * Andrew Hochhaus + * Aaron Hopkins + * Tani Hosokawa + * Jamie Iles + * Xiuqiang Jiang + * Claudio Jeker + * Evan Jones + * Marcin Juszkiewicz + * George Kadianakis + * Makoto Kato + * Phua Keat + * Azat Khuzhin + * Alexander Klauer + * Kevin Ko + * Brian Koehmstedt + * Marko Kreen + * OndÅ™ej KuznÃk + * Valery Kyholodov + * Ross Lagerwall + * Scott Lamb + * Christopher Layne + * Adam Langley + * Graham Leggett + * Volker Lendecke + * Philip Lewis + * Zhou Li + * David Libenzi + * Yan Lin + * Moshe Litvin + * Simon Liu + * Mitchell Livingston + * Hagne Mahre + * Lubomir Marinov + * Abilio Marques + * Nicolas Martyanoff + * Abel Mathew + * Nick Mathewson + * James Mansion + * Nicholas Marriott + * Andrey Matveev + * Caitlin Mercer + * Dagobert Michelsen + * Andrea Montefusco + * Mansour Moufid + * Mina Naguib + * Felix Nawothnig + * Trond Norbye + * Linus Nordberg + * Richard Nyberg + * Jon Oberheide + * John Ohl + * Phil Oleson + * Alexey Ozeritsky + * Dave Pacheco + * Derrick Pallas + * Tassilo von Parseval + * Catalin Patulea + * Patrick Pelletier + * Simon Perreault + * Dan Petro + * Pierre Phaneuf + * Amarin Phaosawasdi + * Ryan Phillips + * Dimitre Piskyulev + * Pavel Plesov + * Jon Poland + * Roman Puls + * Nate R + * Robert Ransom + * Balint Reczey + * Bert JW Regeer + * Nate Rosenblum + * Peter Rosin + * Maseeb Abdul Qadir + * Wang Qin + * Alex S + * Gyepi Sam + * Hanna Schroeter + * Ralf Schmitt + * Mike Smellie + * Steve Snyder + * Nir Soffer + * Dug Song + * Dongsheng Song + * Hannes Sowa + * Joakim Soderberg + * Joseph Spadavecchia + * Kevin Springborn + * Harlan Stenn + * Andrew Sweeney + * Ferenc Szalai + * Brodie Thiesfield + * Jason Toffaletti + * Brian Utterback + * Gisle Vanem + * Bas Verhoeven + * Constantine Verutin + * Colin Watt + * Zack Weinberg + * Jardel Weyrich + * Jay R. Wren + * Zack Weinberg + * Mobai Zhang + * Alejo + * Alex + * Taral + * propanbutan + * masksqwe + * mmadia + * yangacer + +If we have forgotten your name, please contact us. diff --git a/libs/libevent/docs/appveyor.yml b/libs/libevent/docs/appveyor.yml new file mode 100644 index 0000000000..f9af62d2db --- /dev/null +++ b/libs/libevent/docs/appveyor.yml @@ -0,0 +1,45 @@ +version: 2.1.5.{build} +shallow_clone: true + +os: Visual Studio 2015 RC + +build: + verbosity: detailed + +environment: + global: + CYG_ROOT: C:/MinGW/msys/1.0 + +init: + - 'echo Building libevent %version% for Windows' + - 'echo System architecture: %PLATFORM%' + - 'echo Repo build branch is: %APPVEYOR_REPO_BRANCH%' + - 'echo Build folder is: %APPVEYOR_BUILD_FOLDER%' + +install: + - set PATH=%PATH%;C:\MinGW\msys\1.0\bin;C:\MinGW\bin + - appveyor DownloadFile https://strcpy.net/packages/Win32OpenSSL-1_0_2a.exe + - Win32OpenSSL-1_0_2a.exe /silent /verysilent /sp- /suppressmsgboxes + +build_script: + - cmd: 'echo Cygwin root is: %CYG_ROOT%' + - cmd: 'echo Build folder is: %APPVEYOR_BUILD_FOLDER%' + - cmd: 'echo Repo build branch is: %APPVEYOR_REPO_BRANCH%' + - cmd: 'echo Repo build commit is: %APPVEYOR_REPO_COMMIT%' + - cmd: "echo installing stuff" + - cmd: 'echo "C:\MinGW /mingw" >%CYG_ROOT%/etc/fstab' + - cmd: 'C:\MinGW\bin\mingw-get install autotools autoconf automake python' + - cmd: 'echo Autogen running...' + - cmd: '%CYG_ROOT%/bin/bash -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null;mount C:/MinGW /mingw; bash -x ./autogen.sh; ./configure; make; make verify"' + +#install: +# - appveyor DownloadFile https://strcpy.net/packages/Win32OpenSSL-1_0_2a.exe +# - Win32OpenSSL-1_0_2a.exe /silent /verysilent /sp- /suppressmsgboxes +#build_script: +# - md build +# - cd build +# - cmake .. +# - cmake --build . +# - ctest --output-on-failure +cache: + - C:\OpenSSL-Win32 diff --git a/libs/libevent/docs/autogen.sh b/libs/libevent/docs/autogen.sh new file mode 100644 index 0000000000..57eeb940b9 --- /dev/null +++ b/libs/libevent/docs/autogen.sh @@ -0,0 +1,15 @@ +#!/bin/sh +if [ -x "`which autoreconf 2>/dev/null`" ] ; then + exec autoreconf -ivf +fi + +LIBTOOLIZE=libtoolize +SYSNAME=`uname` +if [ "x$SYSNAME" = "xDarwin" ] ; then + LIBTOOLIZE=glibtoolize +fi +aclocal -I m4 && \ + autoheader && \ + $LIBTOOLIZE && \ + autoconf && \ + automake --add-missing --force-missing --copy diff --git a/libs/libevent/docs/cmake/AddCompilerFlags.cmake b/libs/libevent/docs/cmake/AddCompilerFlags.cmake new file mode 100644 index 0000000000..c7da188b7a --- /dev/null +++ b/libs/libevent/docs/cmake/AddCompilerFlags.cmake @@ -0,0 +1,15 @@ +include(CheckCCompilerFlag) + +macro(add_compiler_flags _flags) + foreach(flag ${_flags}) + string(REGEX REPLACE "[-.+/:= ]" "_" _flag_esc "${flag}") + + check_c_compiler_flag("${flag}" check_c_compiler_flag_${_flag_esc}) + + if (check_c_compiler_flag_${_flag_esc}) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flag}") + endif() + endforeach() +endmacro() + + diff --git a/libs/libevent/docs/cmake/COPYING-CMAKE-SCRIPTS b/libs/libevent/docs/cmake/COPYING-CMAKE-SCRIPTS new file mode 100644 index 0000000000..ab3c4d25d1 --- /dev/null +++ b/libs/libevent/docs/cmake/COPYING-CMAKE-SCRIPTS @@ -0,0 +1,22 @@ +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 copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +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.
\ No newline at end of file diff --git a/libs/libevent/docs/cmake/CheckFileOffsetBits.c b/libs/libevent/docs/cmake/CheckFileOffsetBits.c new file mode 100644 index 0000000000..d948fecf2b --- /dev/null +++ b/libs/libevent/docs/cmake/CheckFileOffsetBits.c @@ -0,0 +1,14 @@ +#include <sys/types.h> + +#define KB ((off_t)1024) +#define MB ((off_t)1024 * KB) +#define GB ((off_t)1024 * MB) +#define TB ((off_t)1024 * GB) +int t2[(((64 * GB -1) % 671088649) == 268434537) + && (((TB - (64 * GB -1) + 255) % 1792151290) == 305159546)? 1: -1]; + +int main() +{ + ; + return 0; +} diff --git a/libs/libevent/docs/cmake/CheckFileOffsetBits.cmake b/libs/libevent/docs/cmake/CheckFileOffsetBits.cmake new file mode 100644 index 0000000000..1253440160 --- /dev/null +++ b/libs/libevent/docs/cmake/CheckFileOffsetBits.cmake @@ -0,0 +1,43 @@ +# - Check if _FILE_OFFSET_BITS macro needed for large files +# CHECK_FILE_OFFSET_BITS () +# +# The following variables may be set before calling this macro to +# modify the way the check is run: +# +# CMAKE_REQUIRED_FLAGS = string of compile command line flags +# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) +# CMAKE_REQUIRED_INCLUDES = list of include directories +# Copyright (c) 2009, Michihiro NAKAJIMA +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +#INCLUDE(CheckCSourceCompiles) + +GET_FILENAME_COMPONENT(_selfdir_CheckFileOffsetBits + "${CMAKE_CURRENT_LIST_FILE}" PATH) + +MACRO (CHECK_FILE_OFFSET_BITS) + IF(NOT DEFINED _FILE_OFFSET_BITS) + MESSAGE(STATUS "Cheking _FILE_OFFSET_BITS for large files") + TRY_COMPILE(__WITHOUT_FILE_OFFSET_BITS_64 + ${CMAKE_CURRENT_BINARY_DIR} + ${_selfdir_CheckFileOffsetBits}/CheckFileOffsetBits.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}) + IF(NOT __WITHOUT_FILE_OFFSET_BITS_64) + TRY_COMPILE(__WITH_FILE_OFFSET_BITS_64 + ${CMAKE_CURRENT_BINARY_DIR} + ${_selfdir_CheckFileOffsetBits}/CheckFileOffsetBits.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_FILE_OFFSET_BITS=64) + ENDIF(NOT __WITHOUT_FILE_OFFSET_BITS_64) + + IF(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64) + SET(_FILE_OFFSET_BITS 64 CACHE INTERNAL "_FILE_OFFSET_BITS macro needed for large files") + MESSAGE(STATUS "Cheking _FILE_OFFSET_BITS for large files - needed") + ELSE(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64) + SET(_FILE_OFFSET_BITS "" CACHE INTERNAL "_FILE_OFFSET_BITS macro needed for large files") + MESSAGE(STATUS "Cheking _FILE_OFFSET_BITS for large files - not needed") + ENDIF(NOT __WITHOUT_FILE_OFFSET_BITS_64 AND __WITH_FILE_OFFSET_BITS_64) + ENDIF(NOT DEFINED _FILE_OFFSET_BITS) + +ENDMACRO (CHECK_FILE_OFFSET_BITS) diff --git a/libs/libevent/docs/cmake/CheckFunctionExistsEx.c b/libs/libevent/docs/cmake/CheckFunctionExistsEx.c new file mode 100644 index 0000000000..5ee3e5913a --- /dev/null +++ b/libs/libevent/docs/cmake/CheckFunctionExistsEx.c @@ -0,0 +1,30 @@ +#ifdef CHECK_FUNCTION_EXISTS + +#ifndef _WIN32 +char CHECK_FUNCTION_EXISTS(); +#endif + +#ifdef __CLASSIC_C__ +int main(){ + int ac; + char*av[]; +#else +int main(int ac, char*av[]){ +#endif +#ifdef _WIN32 + void * p = &CHECK_FUNCTION_EXISTS; +#else + CHECK_FUNCTION_EXISTS(); +#endif + if(ac > 1000) + { + return *av[0]; + } + return 0; +} + +#else /* CHECK_FUNCTION_EXISTS */ + +# error "CHECK_FUNCTION_EXISTS has to specify the function" + +#endif /* CHECK_FUNCTION_EXISTS */ diff --git a/libs/libevent/docs/cmake/CheckFunctionExistsEx.cmake b/libs/libevent/docs/cmake/CheckFunctionExistsEx.cmake new file mode 100644 index 0000000000..f513f4e108 --- /dev/null +++ b/libs/libevent/docs/cmake/CheckFunctionExistsEx.cmake @@ -0,0 +1,69 @@ +# - Check if a C function can be linked +# CHECK_FUNCTION_EXISTS(<function> <variable>) +# +# Check that the <function> is provided by libraries on the system and +# store the result in a <variable>. This does not verify that any +# system header file declares the function, only that it can be found +# at link time (considure using CheckSymbolExists). +# +# The following variables may be set before calling this macro to +# modify the way the check is run: +# +# CMAKE_REQUIRED_FLAGS = string of compile command line flags +# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) +# CMAKE_REQUIRED_INCLUDES = list of include directories +# CMAKE_REQUIRED_LIBRARIES = list of libraries to link + +#============================================================================= +# Copyright 2002-2011 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(CHECK_FUNCTION_EXISTS_EX FUNCTION VARIABLE) + IF("${VARIABLE}" MATCHES "^${VARIABLE}$") + SET(MACRO_CHECK_FUNCTION_DEFINITIONS + "-DCHECK_FUNCTION_EXISTS=${FUNCTION} ${CMAKE_REQUIRED_FLAGS}") + MESSAGE(STATUS "Looking for ${FUNCTION}") + IF(CMAKE_REQUIRED_LIBRARIES) + SET(CHECK_FUNCTION_EXISTS_ADD_LIBRARIES + "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") + ELSE(CMAKE_REQUIRED_LIBRARIES) + SET(CHECK_FUNCTION_EXISTS_ADD_LIBRARIES) + ENDIF(CMAKE_REQUIRED_LIBRARIES) + IF(CMAKE_REQUIRED_INCLUDES) + SET(CHECK_FUNCTION_EXISTS_ADD_INCLUDES + "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") + ELSE(CMAKE_REQUIRED_INCLUDES) + SET(CHECK_FUNCTION_EXISTS_ADD_INCLUDES) + ENDIF(CMAKE_REQUIRED_INCLUDES) + TRY_COMPILE(${VARIABLE} + ${CMAKE_BINARY_DIR} + ${PROJECT_SOURCE_DIR}/cmake/CheckFunctionExistsEx.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} + "${CHECK_FUNCTION_EXISTS_ADD_LIBRARIES}" + "${CHECK_FUNCTION_EXISTS_ADD_INCLUDES}" + OUTPUT_VARIABLE OUTPUT) + IF(${VARIABLE}) + SET(${VARIABLE} 1 CACHE INTERNAL "Have function ${FUNCTION}") + MESSAGE(STATUS "Looking for ${FUNCTION} - found") + FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determining if the function ${FUNCTION} exists passed with the following output:\n" + "${OUTPUT}\n\n") + ELSE(${VARIABLE}) + MESSAGE(STATUS "Looking for ${FUNCTION} - not found") + SET(${VARIABLE} "" CACHE INTERNAL "Have function ${FUNCTION}") + FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Determining if the function ${FUNCTION} exists failed with the following output:\n" + "${OUTPUT}\n\n") + ENDIF(${VARIABLE}) + ENDIF("${VARIABLE}" MATCHES "^${VARIABLE}$") +ENDMACRO(CHECK_FUNCTION_EXISTS_EX) diff --git a/libs/libevent/docs/cmake/CheckFunctionKeywords.cmake b/libs/libevent/docs/cmake/CheckFunctionKeywords.cmake new file mode 100644 index 0000000000..3d968b8a6b --- /dev/null +++ b/libs/libevent/docs/cmake/CheckFunctionKeywords.cmake @@ -0,0 +1,14 @@ +include(CheckCSourceCompiles) + +macro(check_function_keywords _wordlist) + set(${_result} "") + foreach(flag ${_wordlist}) + string(REGEX REPLACE "[-+/ ()]" "_" flagname "${flag}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${flagname}") + check_c_source_compiles("${flag} void func(); void func() { } int main() { func(); return 0; }" ${have_flag}) + if(${have_flag} AND NOT ${_result}) + set(${_result} "${flag}") + endif(${have_flag} AND NOT ${_result}) + endforeach(flag) +endmacro(check_function_keywords) diff --git a/libs/libevent/docs/cmake/CheckPrototypeDefinition.c.in b/libs/libevent/docs/cmake/CheckPrototypeDefinition.c.in new file mode 100644 index 0000000000..a97344ac3e --- /dev/null +++ b/libs/libevent/docs/cmake/CheckPrototypeDefinition.c.in @@ -0,0 +1,29 @@ +@CHECK_PROTOTYPE_DEFINITION_HEADER@ + +static void cmakeRequireSymbol(int dummy, ...) { + (void) dummy; +} + +static void checkSymbol(void) { +#ifndef @CHECK_PROTOTYPE_DEFINITION_SYMBOL@ + cmakeRequireSymbol(0, &@CHECK_PROTOTYPE_DEFINITION_SYMBOL@); +#endif +} + +@CHECK_PROTOTYPE_DEFINITION_PROTO@ { + return @CHECK_PROTOTYPE_DEFINITION_RETURN@; +} + +#ifdef __CLASSIC_C__ +int main() { + int ac; + char*av[]; +#else +int main(int ac, char *av[]) { +#endif + checkSymbol(); + if (ac > 1000) { + return *av[0]; + } + return 0; +} diff --git a/libs/libevent/docs/cmake/CheckPrototypeDefinition.cmake b/libs/libevent/docs/cmake/CheckPrototypeDefinition.cmake new file mode 100644 index 0000000000..e0c6a572c0 --- /dev/null +++ b/libs/libevent/docs/cmake/CheckPrototypeDefinition.cmake @@ -0,0 +1,84 @@ +# - Check if the protoype we expect is correct. +# check_prototype_definition(FUNCTION PROTOTYPE RETURN HEADER VARIABLE) +# +# FUNCTION - The name of the function (used to check if prototype exists) +# PROTOTYPE- The prototype to check. +# RETURN - The return value of the function. +# HEADER - The header files required. +# VARIABLE - The variable to store the result. +# +# Example: +# +# check_prototype_definition(getpwent_r +# "struct passwd *getpwent_r(struct passwd *src, char *buf, int buflen)" +# "NULL" +# "unistd.h;pwd.h" +# SOLARIS_GETPWENT_R) +# +# The following variables may be set before calling this macro to +# modify the way the check is run: +# +# CMAKE_REQUIRED_FLAGS = string of compile command line flags +# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) +# CMAKE_REQUIRED_INCLUDES = list of include directories +# CMAKE_REQUIRED_LIBRARIES = list of libraries to link + + +function(CHECK_PROTOTYPE_DEFINITION _FUNCTION _PROTOTYPE _RETURN _HEADER _VARIABLE) + + if ("${_VARIABLE}" MATCHES "^${_VARIABLE}$") + set(CHECK_PROTOTYPE_DEFINITION_CONTENT "/* */\n") + + set(CHECK_PROTOTYPE_DEFINITION_FLAGS ${CMAKE_REQUIRED_FLAGS}) + if (CMAKE_REQUIRED_LIBRARIES) + set(CHECK_PROTOTYPE_DEFINITION_LIBS + "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") + else(CMAKE_REQUIRED_LIBRARIES) + set(CHECK_PROTOTYPE_DEFINITION_LIBS) + endif(CMAKE_REQUIRED_LIBRARIES) + if (CMAKE_REQUIRED_INCLUDES) + set(CMAKE_SYMBOL_EXISTS_INCLUDES + "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") + else(CMAKE_REQUIRED_INCLUDES) + set(CMAKE_SYMBOL_EXISTS_INCLUDES) + endif(CMAKE_REQUIRED_INCLUDES) + + foreach(_FILE ${_HEADER}) + set(CHECK_PROTOTYPE_DEFINITION_HEADER + "${CHECK_PROTOTYPE_DEFINITION_HEADER}#include <${_FILE}>\n") + endforeach(_FILE) + + set(CHECK_PROTOTYPE_DEFINITION_SYMBOL ${_FUNCTION}) + set(CHECK_PROTOTYPE_DEFINITION_PROTO ${_PROTOTYPE}) + set(CHECK_PROTOTYPE_DEFINITION_RETURN ${_RETURN}) + + configure_file("${PROJECT_SOURCE_DIR}/cmake/CheckPrototypeDefinition.c.in" + "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c" @ONLY) + + file(READ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c _SOURCE) + + try_compile(${_VARIABLE} + ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${CHECK_PROTOTYPE_DEFINITION_FLAGS} + "${CHECK_PROTOTYPE_DEFINITION_LIBS}" + "${CMAKE_SYMBOL_EXISTS_INCLUDES}" + OUTPUT_VARIABLE OUTPUT) + + if (${_VARIABLE}) + set(${_VARIABLE} 1 CACHE INTERNAL "Have correct prototype for ${_FUNCTION}") + message(STATUS "Checking prototype ${_FUNCTION} for ${_VARIABLE} - True") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determining if the prototype ${_FUNCTION} exists for ${_VARIABLE} passed with the following output:\n" + "${OUTPUT}\n\n") + else (${_VARIABLE}) + message(STATUS "Checking prototype ${_FUNCTION} for ${_VARIABLE} - False") + set(${_VARIABLE} 0 CACHE INTERNAL "Have correct prototype for ${_FUNCTION}") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Determining if the prototype ${_FUNCTION} exists for ${_VARIABLE} failed with the following output:\n" + "${OUTPUT}\n\n${_SOURCE}\n\n") + endif (${_VARIABLE}) + endif("${_VARIABLE}" MATCHES "^${_VARIABLE}$") + +endfunction(CHECK_PROTOTYPE_DEFINITION) diff --git a/libs/libevent/docs/cmake/CheckWorkingKqueue.cmake b/libs/libevent/docs/cmake/CheckWorkingKqueue.cmake new file mode 100644 index 0000000000..47bf4e838a --- /dev/null +++ b/libs/libevent/docs/cmake/CheckWorkingKqueue.cmake @@ -0,0 +1,52 @@ +include(CheckCSourceRuns) + +check_c_source_runs( +" +#include <sys/types.h> +#include <sys/time.h> +#include <sys/event.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> + +int +main(int argc, char **argv) +{ + int kq; + int n; + int fd[2]; + struct kevent ev; + struct timespec ts; + char buf[8000]; + + if (pipe(fd) == -1) + exit(1); + if (fcntl(fd[1], F_SETFL, O_NONBLOCK) == -1) + exit(1); + + while ((n = write(fd[1], buf, sizeof(buf))) == sizeof(buf)) + ; + + if ((kq = kqueue()) == -1) + exit(1); + + memset(&ev, 0, sizeof(ev)); + ev.ident = fd[1]; + ev.filter = EVFILT_WRITE; + ev.flags = EV_ADD | EV_ENABLE; + n = kevent(kq, &ev, 1, NULL, 0, NULL); + if (n == -1) + exit(1); + + read(fd[0], buf, sizeof(buf)); + + ts.tv_sec = 0; + ts.tv_nsec = 0; + n = kevent(kq, NULL, 0, &ev, 1, &ts); + if (n == -1 || n == 0) + exit(1); + + exit(0); +} + +" EVENT__HAVE_WORKING_KQUEUE)
\ No newline at end of file diff --git a/libs/libevent/docs/cmake/CodeCoverage.cmake b/libs/libevent/docs/cmake/CodeCoverage.cmake new file mode 100644 index 0000000000..969f273253 --- /dev/null +++ b/libs/libevent/docs/cmake/CodeCoverage.cmake @@ -0,0 +1,162 @@ +# +# Boost Software License - Version 1.0 - August 17th, 2003 +# +# Permission is hereby granted, free of charge, to any person or organization +# obtaining a copy of the software and accompanying documentation covered by +# this license (the "Software") to use, reproduce, display, distribute, +# execute, and transmit the Software, and to prepare derivative works of the +# Software, and to permit third-parties to whom the Software is furnished to +# do so, all subject to the following: +# +# The copyright notices in the Software and this entire statement, including +# the above license grant, this restriction and the following disclaimer, +# must be included in all copies of the Software, in whole or in part, and +# all derivative works of the Software, unless such copies or derivative +# works are solely in the form of machine-executable object code generated by +# a source language processor. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +# SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +# FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +# 2012-01-31, Lars Bilke +# - Enable Code Coverage +# +# 2013-09-17, Joakim Söderberg +# - Added support for Clang. +# - Some additional usage instructions. +# +# USAGE: +# 1. Copy this file into your cmake modules path. +# +# 2. Add the following line to your CMakeLists.txt: +# INCLUDE(CodeCoverage) +# +# 3. Set compiler flags to turn off optimization and enable coverage: +# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") +# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") +# +# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target +# which runs your test executable and produces a lcov code coverage report: +# Example: +# SETUP_TARGET_FOR_COVERAGE( +# my_coverage_target # Name for custom target. +# test_driver # Name of the test driver executable that runs the tests. +# # NOTE! This should always have a ZERO as exit code +# # otherwise the coverage generation will not complete. +# coverage # Name of output directory. +# ) +# +# 4. Build a Debug build: +# cmake -DCMAKE_BUILD_TYPE=Debug .. +# make +# make my_coverage_target +# +# + +# Check prereqs +FIND_PROGRAM( GCOV_PATH gcov ) +FIND_PROGRAM( LCOV_PATH lcov ) +FIND_PROGRAM( GENHTML_PATH genhtml ) +FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests) + +IF(NOT GCOV_PATH) + MESSAGE(FATAL_ERROR "gcov not found! Aborting...") +ENDIF() # NOT GCOV_PATH + +IF(NOT CMAKE_COMPILER_IS_GNUCC AND NOT CMAKE_COMPILER_IS_GNUCXX) + # Clang version 3.0.0 and greater now supports gcov as well. + MESSAGE(WARNING "Compiler is not GNU gcc! Clang Version 3.0.0 and greater supports gcov as well, but older versions don't.") + + IF(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + MESSAGE(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") + ENDIF() +ENDIF() # NOT CMAKE_COMPILER_IS_GNUCC + +IF ( NOT CMAKE_BUILD_TYPE STREQUAL "Debug" ) + MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" ) +ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" + + +# Param _targetname The name of new the custom make target +# Param _testrunner The name of the target which runs the tests. +# MUST return ZERO always, even on errors. +# If not, no coverage report will be created! +# Param _outputname lcov output is generated as _outputname.info +# HTML report is generated in _outputname/index.html +# Optional fourth parameter is passed as arguments to _testrunner +# Pass them in list form, e.g.: "-j;2" for -j 2 +FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname) + + IF(NOT LCOV_PATH) + MESSAGE(FATAL_ERROR "lcov not found! Aborting...") + ENDIF() # NOT LCOV_PATH + + IF(NOT GENHTML_PATH) + MESSAGE(FATAL_ERROR "genhtml not found! Aborting...") + ENDIF() # NOT GENHTML_PATH + + # Setup target + ADD_CUSTOM_TARGET(${_targetname} + + # Cleanup lcov + ${LCOV_PATH} --directory . --zerocounters + + # Run tests + COMMAND ${_testrunner} ${ARGV3} + + # Capturing lcov counters and generating report + COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info + COMMAND ${LCOV_PATH} --remove ${_outputname}.info 'tests/*' '/usr/*' --output-file ${_outputname}.info.cleaned + COMMAND ${GENHTML_PATH} -o ${_outputname} ${_outputname}.info.cleaned + COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned + + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." + ) + + # Show info where to find the report + ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD + COMMAND ; + COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report." + ) + +ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE + +# Param _targetname The name of new the custom make target +# Param _testrunner The name of the target which runs the tests +# Param _outputname cobertura output is generated as _outputname.xml +# Optional fourth parameter is passed as arguments to _testrunner +# Pass them in list form, e.g.: "-j;2" for -j 2 +FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname) + + IF(NOT PYTHON_EXECUTABLE) + MESSAGE(FATAL_ERROR "Python not found! Aborting...") + ENDIF() # NOT PYTHON_EXECUTABLE + + IF(NOT GCOVR_PATH) + MESSAGE(FATAL_ERROR "gcovr not found! Aborting...") + ENDIF() # NOT GCOVR_PATH + + ADD_CUSTOM_TARGET(${_targetname} + + # Run tests + ${_testrunner} ${ARGV3} + + # Running gcovr + COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/' -o ${_outputname}.xml + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Running gcovr to produce Cobertura code coverage report." + ) + + # Show info where to find the report + ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD + COMMAND ; + COMMENT "Cobertura code coverage report saved in ${_outputname}.xml." + ) + +ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA diff --git a/libs/libevent/docs/cmake/Copyright.txt b/libs/libevent/docs/cmake/Copyright.txt new file mode 100644 index 0000000000..813124f02e --- /dev/null +++ b/libs/libevent/docs/cmake/Copyright.txt @@ -0,0 +1,57 @@ +CMake - Cross Platform Makefile Generator +Copyright 2000-2013 Kitware, Inc. +Copyright 2000-2011 Insight Software Consortium +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 names of Kitware, Inc., the Insight Software Consortium, + nor the names of their 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. + +------------------------------------------------------------------------------ + +The above copyright and license notice applies to distributions of +CMake in source and binary form. Some source files contain additional +notices of original copyright by their contributors; see each source +for details. Third-party software packages supplied with CMake under +compatible licenses provide their own copyright notices documented in +corresponding subdirectories. + +------------------------------------------------------------------------------ + +CMake was initially developed by Kitware with the following sponsorship: + + * National Library of Medicine at the National Institutes of Health + as part of the Insight Segmentation and Registration Toolkit (ITK). + + * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel + Visualization Initiative. + + * National Alliance for Medical Image Computing (NAMIC) is funded by the + National Institutes of Health through the NIH Roadmap for Medical Research, + Grant U54 EB005149. + + * Kitware, Inc.
\ No newline at end of file diff --git a/libs/libevent/docs/cmake/FindGit.cmake b/libs/libevent/docs/cmake/FindGit.cmake new file mode 100644 index 0000000000..2abbfe4e9d --- /dev/null +++ b/libs/libevent/docs/cmake/FindGit.cmake @@ -0,0 +1,45 @@ +# The module defines the following variables: +# GIT_EXECUTABLE - path to git command line client +# GIT_FOUND - true if the command line client was found +# Example usage: +# find_package(Git) +# if(GIT_FOUND) +# message("git found: ${GIT_EXECUTABLE}") +# endif() + +#============================================================================= +# Copyright 2010 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 distributed this file outside of CMake, substitute the full +# License text for the above reference.) + +# Look for 'git' or 'eg' (easy git) +set(git_names git eg) + +# Prefer .cmd variants on Windows unless running in a Makefile +# in the MSYS shell. +if(WIN32) + if(NOT CMAKE_GENERATOR MATCHES "MSYS") + set(git_names git.cmd git eg.cmd eg) + endif() +endif() + +find_program(GIT_EXECUTABLE + NAMES ${git_names} + DOC "git command line client") + +mark_as_advanced(GIT_EXECUTABLE) + +# Handle the QUIETLY and REQUIRED arguments and set GIT_FOUND to TRUE if +# all listed variables are TRUE + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Git DEFAULT_MSG GIT_EXECUTABLE) + diff --git a/libs/libevent/docs/cmake/LibeventConfig.cmake.in b/libs/libevent/docs/cmake/LibeventConfig.cmake.in new file mode 100644 index 0000000000..b28cacb5fb --- /dev/null +++ b/libs/libevent/docs/cmake/LibeventConfig.cmake.in @@ -0,0 +1,17 @@ +# - Config file for the Libevent package +# It defines the following variables +# LIBEVENT_INCLUDE_DIRS - include directories for FooBar +# LIBEVENT_LIBRARIES - libraries to link against + +# Get the path of the current file. +get_filename_component(LIBEVENT_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + +# Set the include directories. +set(LIBEVENT_INCLUDE_DIRS "@EVENT_INSTALL_INCLUDE_DIR@") + +# Include the project Targets file, this contains definitions for IMPORTED targets. +include(${LIBEVENT_CMAKE_DIR}/LibeventTargets.cmake) + +# IMPORTED targets from LibeventTargets.cmake +set(LIBEVENT_LIBRARIES event event_core event_extra) + diff --git a/libs/libevent/docs/cmake/LibeventConfigBuildTree.cmake.in b/libs/libevent/docs/cmake/LibeventConfigBuildTree.cmake.in new file mode 100644 index 0000000000..02edef32fc --- /dev/null +++ b/libs/libevent/docs/cmake/LibeventConfigBuildTree.cmake.in @@ -0,0 +1,17 @@ +# - Config file for the Libevent package +# It defines the following variables +# LIBEVENT_INCLUDE_DIRS - include directories for FooBar +# LIBEVENT_LIBRARIES - libraries to link against + +# Get the path of the current file. +get_filename_component(LIBEVENT_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + +# Set the include directories. +set(LIBEVENT_INCLUDE_DIRS "@EVENT__INCLUDE_DIRS@") + +# Include the project Targets file, this contains definitions for IMPORTED targets. +include(${LIBEVENT_CMAKE_DIR}/LibeventTargets.cmake) + +# IMPORTED targets from LibeventTargets.cmake +set(LIBEVENT_LIBRARIES event event_core event_extra) + diff --git a/libs/libevent/docs/cmake/LibeventConfigVersion.cmake.in b/libs/libevent/docs/cmake/LibeventConfigVersion.cmake.in new file mode 100644 index 0000000000..56371a8fee --- /dev/null +++ b/libs/libevent/docs/cmake/LibeventConfigVersion.cmake.in @@ -0,0 +1,11 @@ +set(PACKAGE_VERSION "@EVENT_PACKAGE_VERSION@") + +# Check whether the requested PACKAGE_FIND_VERSION is compatible +if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/libs/libevent/docs/cmake/VersionViaGit.cmake b/libs/libevent/docs/cmake/VersionViaGit.cmake new file mode 100644 index 0000000000..f183a64713 --- /dev/null +++ b/libs/libevent/docs/cmake/VersionViaGit.cmake @@ -0,0 +1,53 @@ +# This module defines the following variables utilizing +# git to determine the parent tag. And if found the macro +# will attempt to parse them in the github tag fomat +# +# Usful for auto-versionin in ou CMakeLists +# +# EVENT_GIT___VERSION_FOUND - Version variables foud +# EVENT_GIT___VERSION_MAJOR - Major version. +# EVENT_GIT___VERSION_MINOR - Minor version +# EVENT_GIT___VERSION_STAGE - Stage version +# +# Example usage: +# +# event_fuzzy_version_from_git() +# if (EVENT_GIT___VERSION_FOUND) +# message("Libvent major=${EVENT_GIT___VERSION_MAJOR}") +# message(" minor=${EVENT_GIT___VERSION_MINOR}") +# message(" patch=${EVENT_GIT___VERSION_PATCH}") +# message(" stage=${EVENT_GIT___VERSION_STAGE}") +# endif() + +include(FindGit) + +macro(event_fuzzy_version_from_git) + set(EVENT_GIT___VERSION_FOUND FALSE) + + # set our defaults. + set(EVENT_GIT___VERSION_MAJOR 2) + set(EVENT_GIT___VERSION_MINOR 1) + set(EVENT_GIT___VERSION_PATCH 5) + set(EVENT_GIT___VERSION_STAGE "beta") + + find_package(Git) + + if (GIT_FOUND) + execute_process( + COMMAND + ${GIT_EXECUTABLE} describe --abbrev=0 + WORKING_DIRECTORY + ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE + GITRET + OUTPUT_VARIABLE + GITVERSION) + + if (GITRET EQUAL 0) + string(REGEX REPLACE "^release-([0-9]+)\\.([0-9]+)\\.([0-9]+)-(.*)" "\\1" EVENT_GIT___VERSION_MAJOR ${GITVERSION}) + string(REGEX REPLACE "^release-([0-9]+)\\.([0-9]+)\\.([0-9]+)-(.*)" "\\2" EVENT_GIT___VERSION_MINOR ${GITVERSION}) + string(REGEX REPLACE "^release-([0-9]+)\\.([0-9]+)\\.([0-9]+)-(.*)" "\\3" EVENT_GIT___VERSION_PATCH ${GITVERSION}) + string(REGEX REPLACE "^release-([0-9]+)\\.([0-9]+)\\.([0-9]+)-([aA-zZ]+)" "\\4" EVENT_GIT___VERSION_STAGE ${GITVERSION}) + endif() + endif() +endmacro() diff --git a/libs/libevent/docs/configure.ac b/libs/libevent/docs/configure.ac new file mode 100644 index 0000000000..6a669fb701 --- /dev/null +++ b/libs/libevent/docs/configure.ac @@ -0,0 +1,919 @@ +dnl Copyright 2000-2007 Niels Provos +dnl Copyright 2007-2012 Niels Provos and Nick Mathewson +dnl +dnl See LICENSE for copying information. +dnl +dnl Original version Dug Song <dugsong@monkey.org> + +AC_INIT(libevent,2.1.5-beta) +AC_PREREQ(2.59) +AC_CONFIG_SRCDIR(event.c) + +AC_CONFIG_MACRO_DIR([m4]) +AM_INIT_AUTOMAKE +dnl AM_SILENT_RULES req. automake 1.11. [no] defaults V=1 +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) +AC_CONFIG_HEADERS(config.h evconfig-private.h:evconfig-private.h.in) +AC_DEFINE(NUMERIC_VERSION, 0x02010500, [Numeric representation of the version]) + +dnl Initialize prefix. +if test "$prefix" = "NONE"; then + prefix="/usr/local" +fi + +dnl Try and get a full POSIX environment on obscure systems +ifdef([AC_USE_SYSTEM_EXTENSIONS], [ +AC_USE_SYSTEM_EXTENSIONS +], [ +AC_AIX +AC_GNU_SOURCE +AC_MINIX +]) + +AC_CANONICAL_BUILD +AC_CANONICAL_HOST +dnl the 'build' machine is where we run configure and compile +dnl the 'host' machine is where the resulting stuff runs. + +#case "$host_os" in +# +# osf5*) +# CFLAGS="$CFLAGS -D_OSF_SOURCE" +# ;; +#esac + +dnl Checks for programs. +AM_PROG_CC_C_O +AC_PROG_INSTALL +AC_PROG_LN_S +# AC_PROG_MKDIR_P - $(MKDIR_P) should be defined by AM_INIT_AUTOMAKE + +# AC_PROG_SED is only available in Autoconf >= 2.59b; workaround for older +# versions +ifdef([AC_PROG_SED], [AC_PROG_SED], [ +AC_CHECK_PROGS(SED, [gsed sed]) +]) + +AC_PROG_GCC_TRADITIONAL + +# We need to test for at least gcc 2.95 here, because older versions don't +# have -fno-strict-aliasing +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [ +#if !defined(__GNUC__) || (__GNUC__ < 2) || (__GNUC__ == 2 && __GNUC_MINOR__ < 95) +#error +#endif])], have_gcc295=yes, have_gcc295=no) + +if test "$GCC" = "yes" ; then + # Enable many gcc warnings by default... + CFLAGS="$CFLAGS -Wall" + # And disable the strict-aliasing optimization, since it breaks + # our sockaddr-handling code in strange ways. + if test x$have_gcc295 = xyes; then + CFLAGS="$CFLAGS -fno-strict-aliasing" + fi +fi + +# OS X Lion started deprecating the system openssl. Let's just disable +# all deprecation warnings on OS X. +case "$host_os" in + + darwin*) + CFLAGS="$CFLAGS -Wno-deprecated-declarations" + ;; +esac + +AC_ARG_ENABLE(gcc-warnings, + AS_HELP_STRING(--disable-gcc-warnings, disable verbose warnings with GCC)) + +AC_ARG_ENABLE(gcc-hardening, + AS_HELP_STRING(--enable-gcc-hardening, enable compiler security checks), +[if test x$enableval = xyes; then + CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2 -fstack-protector-all" + CFLAGS="$CFLAGS -fwrapv -fPIE -Wstack-protector" + CFLAGS="$CFLAGS --param ssp-buffer-size=1" +fi]) + +AC_ARG_ENABLE(thread-support, + AS_HELP_STRING(--disable-thread-support, disable support for threading), + [], [enable_thread_support=yes]) +AC_ARG_ENABLE(malloc-replacement, + AS_HELP_STRING(--disable-malloc-replacement, disable support for replacing the memory mgt functions), + [], [enable_malloc_replacement=yes]) +AC_ARG_ENABLE(openssl, + AS_HELP_STRING(--disable-openssl, disable support for openssl encryption), + [], [enable_openssl=yes]) +AC_ARG_ENABLE(debug-mode, + AS_HELP_STRING(--disable-debug-mode, disable support for running in debug mode), + [], [enable_debug_mode=yes]) +AC_ARG_ENABLE([libevent-install], + AS_HELP_STRING([--disable-libevent-install, disable installation of libevent]), + [], [enable_libevent_install=yes]) +AC_ARG_ENABLE([libevent-regress], + AS_HELP_STRING([--disable-libevent-regress, skip regress in make check]), + [], [enable_libevent_regress=yes]) +AC_ARG_ENABLE([samples], + AS_HELP_STRING([--disable-samples, skip building of sample programs]), + [], [enable_samples=yes]) +AC_ARG_ENABLE([function-sections], + AS_HELP_STRING([--enable-function-sections, make static library allow smaller binaries with --gc-sections]), + [], [enable_function_sections=no]) +AC_ARG_ENABLE([verbose-debug], + AS_HELP_STRING([--enable-verbose-debug, verbose debug logging]), + [], [enable_verbose_debug=no]) + + +AC_PROG_LIBTOOL + +dnl Uncomment "AC_DISABLE_SHARED" to make shared libraries not get +dnl built by default. You can also turn shared libs on and off from +dnl the command line with --enable-shared and --disable-shared. +dnl AC_DISABLE_SHARED +AC_SUBST(LIBTOOL_DEPS) + +AM_CONDITIONAL([BUILD_SAMPLES], [test "$enable_samples" = "yes"]) +AM_CONDITIONAL([BUILD_REGRESS], [test "$enable_libevent_regress" = "yes"]) + +dnl Checks for libraries. +AC_SEARCH_LIBS([inet_ntoa], [nsl]) +AC_SEARCH_LIBS([socket], [socket]) +AC_SEARCH_LIBS([inet_aton], [resolv]) +AC_SEARCH_LIBS([clock_gettime], [rt]) +AC_SEARCH_LIBS([sendfile], [sendfile]) + +dnl - check if the macro _WIN32 is defined on this compiler. +dnl - (this is how we check for a windows compiler) +AC_MSG_CHECKING(for WIN32) +AC_TRY_COMPILE(, + [ +#ifndef _WIN32 +die horribly +#endif + ], + bwin32=true; AC_MSG_RESULT(yes), + bwin32=false; AC_MSG_RESULT(no), +) + +dnl - check if the macro __CYGWIN__ is defined on this compiler. +dnl - (this is how we check for a cygwin version of GCC) +AC_MSG_CHECKING(for CYGWIN) +AC_TRY_COMPILE(, + [ +#ifndef __CYGWIN__ +die horribly +#endif + ], + cygwin=true; AC_MSG_RESULT(yes), + cygwin=false; AC_MSG_RESULT(no), +) + +AC_CHECK_HEADERS([zlib.h]) + +if test "x$ac_cv_header_zlib_h" = "xyes"; then +dnl Determine if we have zlib for regression tests +dnl Don't put this one in LIBS +save_LIBS="$LIBS" +LIBS="" +ZLIB_LIBS="" +have_zlib=no +AC_SEARCH_LIBS([inflateEnd], [z], + [have_zlib=yes + ZLIB_LIBS="$LIBS" + AC_DEFINE(HAVE_LIBZ, 1, [Define if the system has zlib])]) +LIBS="$save_LIBS" +AC_SUBST(ZLIB_LIBS) +fi +AM_CONDITIONAL(ZLIB_REGRESS, [test "$have_zlib" = "yes"]) + +dnl See if we have openssl. This doesn't go in LIBS either. +if test "$bwin32" = true; then + EV_LIB_WS32=-lws2_32 + EV_LIB_GDI=-lgdi32 +else + EV_LIB_WS32= + EV_LIB_GDI= +fi +AC_SUBST(EV_LIB_WS32) +AC_SUBST(EV_LIB_GDI) +AC_SUBST(OPENSSL_LIBADD) + +AC_SYS_LARGEFILE + +LIBEVENT_OPENSSL + +dnl Checks for header files. +AC_CHECK_HEADERS([ \ + arpa/inet.h \ + fcntl.h \ + ifaddrs.h \ + mach/mach_time.h \ + netdb.h \ + netinet/in.h \ + netinet/in6.h \ + netinet/tcp.h \ + poll.h \ + port.h \ + stdarg.h \ + stddef.h \ + sys/devpoll.h \ + sys/epoll.h \ + sys/event.h \ + sys/eventfd.h \ + sys/ioctl.h \ + sys/mman.h \ + sys/param.h \ + sys/queue.h \ + sys/resource.h \ + sys/select.h \ + sys/sendfile.h \ + sys/socket.h \ + sys/stat.h \ + sys/time.h \ + sys/timerfd.h \ + sys/uio.h \ + sys/wait.h \ +]) + +AC_CHECK_HEADERS(sys/sysctl.h, [], [], [ +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif +]) +if test "x$ac_cv_header_sys_queue_h" = "xyes"; then + AC_MSG_CHECKING(for TAILQ_FOREACH in sys/queue.h) + AC_EGREP_CPP(yes, +[ +#include <sys/queue.h> +#ifdef TAILQ_FOREACH + yes +#endif +], [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_TAILQFOREACH, 1, + [Define if TAILQ_FOREACH is defined in <sys/queue.h>])], + AC_MSG_RESULT(no) + ) +fi + +if test "x$ac_cv_header_sys_time_h" = "xyes"; then + AC_MSG_CHECKING(for timeradd in sys/time.h) + AC_EGREP_CPP(yes, +[ +#include <sys/time.h> +#ifdef timeradd + yes +#endif +], [ AC_DEFINE(HAVE_TIMERADD, 1, + [Define if timeradd is defined in <sys/time.h>]) + AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no) +) +fi + +if test "x$ac_cv_header_sys_time_h" = "xyes"; then + AC_MSG_CHECKING(for timercmp in sys/time.h) + AC_EGREP_CPP(yes, +[ +#include <sys/time.h> +#ifdef timercmp + yes +#endif +], [ AC_DEFINE(HAVE_TIMERCMP, 1, + [Define if timercmp is defined in <sys/time.h>]) + AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no) +) +fi + +if test "x$ac_cv_header_sys_time_h" = "xyes"; then + AC_MSG_CHECKING(for timerclear in sys/time.h) + AC_EGREP_CPP(yes, +[ +#include <sys/time.h> +#ifdef timerclear + yes +#endif +], [ AC_DEFINE(HAVE_TIMERCLEAR, 1, + [Define if timerclear is defined in <sys/time.h>]) + AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no) +) +fi + +if test "x$ac_cv_header_sys_time_h" = "xyes"; then + AC_MSG_CHECKING(for timerisset in sys/time.h) + AC_EGREP_CPP(yes, +[ +#include <sys/time.h> +#ifdef timerisset + yes +#endif +], [ AC_DEFINE(HAVE_TIMERISSET, 1, + [Define if timerisset is defined in <sys/time.h>]) + AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no) +) +fi + +if test "x$ac_cv_header_sys_sysctl_h" = "xyes"; then + AC_CHECK_DECLS([CTL_KERN, KERN_RANDOM, RANDOM_UUID, KERN_ARND], [], [], + [[#include <sys/types.h> + #include <sys/sysctl.h>]] + ) +fi + +AM_CONDITIONAL(BUILD_WIN32, test x$bwin32 = xtrue) +AM_CONDITIONAL(BUILD_CYGWIN, test x$cygwin = xtrue) +AM_CONDITIONAL(BUILD_WITH_NO_UNDEFINED, test x$bwin32 = xtrue || test x$cygwin = xtrue) + +if test x$bwin32 = xtrue; then + AC_SEARCH_LIBS([getservbyname],[ws2_32]) +fi + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_C_INLINE +AC_HEADER_TIME + +dnl Checks for library functions. +AC_CHECK_FUNCS([ \ + accept4 \ + arc4random \ + arc4random_buf \ + clock_gettime \ + eventfd \ + epoll_create1 \ + fcntl \ + getegid \ + geteuid \ + getifaddrs \ + getnameinfo \ + getprotobynumber \ + gettimeofday \ + inet_ntop \ + inet_pton \ + issetugid \ + mach_absolute_time \ + mmap \ + nanosleep \ + pipe \ + pipe2 \ + putenv \ + sendfile \ + setenv \ + setrlimit \ + sigaction \ + signal \ + splice \ + strlcpy \ + strsep \ + strtok_r \ + strtoll \ + sysctl \ + timerfd_create \ + umask \ + unsetenv \ + usleep \ + vasprintf \ + getservbyname \ +]) +AM_CONDITIONAL(STRLCPY_IMPL, [test x"$ac_cv_func_strlcpy" = xno]) + +AC_CACHE_CHECK( + [for getaddrinfo], + [libevent_cv_getaddrinfo], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ + #ifdef HAVE_NETDB_H + #include <netdb.h> + #endif + ]], + [[ + getaddrinfo; + ]] + )], + [libevent_cv_getaddrinfo=yes], + [libevent_cv_getaddrinfo=no] + )] +) +if test "$libevent_cv_getaddrinfo" = "yes" ; then + AC_DEFINE([HAVE_GETADDRINFO], [1], [Do we have getaddrinfo()?]) +else + +# Check for gethostbyname_r in all its glorious incompatible versions. +# (This is cut-and-pasted from Tor, which based its logic on +# Python's configure.in.) +AH_TEMPLATE(HAVE_GETHOSTBYNAME_R, + [Define this if you have any gethostbyname_r()]) + +AC_CHECK_FUNC(gethostbyname_r, [ + AC_MSG_CHECKING([how many arguments gethostbyname_r() wants]) + OLD_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $MY_CPPFLAGS $MY_THREAD_CPPFLAGS $MY_CFLAGS" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include <netdb.h> + ], [[ + char *cp1, *cp2; + struct hostent *h1, *h2; + int i1, i2; + (void)gethostbyname_r(cp1,h1,cp2,i1,&h2,&i2); + ]])],[ + AC_DEFINE(HAVE_GETHOSTBYNAME_R) + AC_DEFINE(HAVE_GETHOSTBYNAME_R_6_ARG, 1, + [Define this if gethostbyname_r takes 6 arguments]) + AC_MSG_RESULT(6) + ], [ + AC_TRY_COMPILE([ +#include <netdb.h> + ], [ + char *cp1, *cp2; + struct hostent *h1; + int i1, i2; + (void)gethostbyname_r(cp1,h1,cp2,i1,&i2); + ], [ + AC_DEFINE(HAVE_GETHOSTBYNAME_R) + AC_DEFINE(HAVE_GETHOSTBYNAME_R_5_ARG, 1, + [Define this if gethostbyname_r takes 5 arguments]) + AC_MSG_RESULT(5) + ], [ + AC_TRY_COMPILE([ +#include <netdb.h> + ], [ + char *cp1; + struct hostent *h1; + struct hostent_data hd; + (void) gethostbyname_r(cp1,h1,&hd); + ], [ + AC_DEFINE(HAVE_GETHOSTBYNAME_R) + AC_DEFINE(HAVE_GETHOSTBYNAME_R_3_ARG, 1, + [Define this if gethostbyname_r takes 3 arguments]) + AC_MSG_RESULT(3) + ], [ + AC_MSG_RESULT(0) + ]) + ]) + ]) + CFLAGS=$OLD_CFLAGS +]) + +fi + +AC_MSG_CHECKING(for F_SETFD in fcntl.h) +AC_EGREP_CPP(yes, +[ +#define _GNU_SOURCE +#include <fcntl.h> +#ifdef F_SETFD +yes +#endif +], [ AC_DEFINE(HAVE_SETFD, 1, + [Define if F_SETFD is defined in <fcntl.h>]) + AC_MSG_RESULT(yes) ], AC_MSG_RESULT(no)) + +needsignal=no +haveselect=no +if test x$bwin32 != xtrue; then + AC_CHECK_FUNCS(select, [haveselect=yes], ) + if test "x$haveselect" = "xyes" ; then + needsignal=yes + fi +fi +AM_CONDITIONAL(SELECT_BACKEND, [test "x$haveselect" = "xyes"]) + +havepoll=no +AC_CHECK_FUNCS(poll, [havepoll=yes], ) +if test "x$havepoll" = "xyes" ; then + needsignal=yes +fi +AM_CONDITIONAL(POLL_BACKEND, [test "x$havepoll" = "xyes"]) + +havedevpoll=no +if test "x$ac_cv_header_sys_devpoll_h" = "xyes"; then + AC_DEFINE(HAVE_DEVPOLL, 1, + [Define if /dev/poll is available]) +fi +AM_CONDITIONAL(DEVPOLL_BACKEND, [test "x$ac_cv_header_sys_devpoll_h" = "xyes"]) + +havekqueue=no +if test "x$ac_cv_header_sys_event_h" = "xyes"; then + AC_CHECK_FUNCS(kqueue, [havekqueue=yes], ) + if test "x$havekqueue" = "xyes" ; then + AC_MSG_CHECKING(for working kqueue) + AC_TRY_RUN( +#include <sys/types.h> +#include <sys/time.h> +#include <sys/event.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> + +int +main(int argc, char **argv) +{ + int kq; + int n; + int fd[[2]]; + struct kevent ev; + struct timespec ts; + char buf[[8000]]; + + if (pipe(fd) == -1) + exit(1); + if (fcntl(fd[[1]], F_SETFL, O_NONBLOCK) == -1) + exit(1); + + while ((n = write(fd[[1]], buf, sizeof(buf))) == sizeof(buf)) + ; + + if ((kq = kqueue()) == -1) + exit(1); + + memset(&ev, 0, sizeof(ev)); + ev.ident = fd[[1]]; + ev.filter = EVFILT_WRITE; + ev.flags = EV_ADD | EV_ENABLE; + n = kevent(kq, &ev, 1, NULL, 0, NULL); + if (n == -1) + exit(1); + + read(fd[[0]], buf, sizeof(buf)); + + ts.tv_sec = 0; + ts.tv_nsec = 0; + n = kevent(kq, NULL, 0, &ev, 1, &ts); + if (n == -1 || n == 0) + exit(1); + + exit(0); +}, [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_WORKING_KQUEUE, 1, + [Define if kqueue works correctly with pipes]) + havekqueue=yes + ], AC_MSG_RESULT(no), AC_MSG_RESULT(no)) + fi +fi +AM_CONDITIONAL(KQUEUE_BACKEND, [test "x$havekqueue" = "xyes"]) + +haveepollsyscall=no +haveepoll=no +AC_CHECK_FUNCS(epoll_ctl, [haveepoll=yes], ) +if test "x$haveepoll" = "xyes" ; then + AC_DEFINE(HAVE_EPOLL, 1, + [Define if your system supports the epoll system calls]) + needsignal=yes +fi +if test "x$ac_cv_header_sys_epoll_h" = "xyes"; then + if test "x$haveepoll" = "xno" ; then + AC_MSG_CHECKING(for epoll system call) + AC_TRY_RUN( +#include <stdint.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/syscall.h> +#include <sys/epoll.h> +#include <unistd.h> + +int +epoll_create(int size) +{ + return (syscall(__NR_epoll_create, size)); +} + +int +main(int argc, char **argv) +{ + int epfd; + + epfd = epoll_create(256); + exit (epfd == -1 ? 1 : 0); +}, [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_EPOLL, 1, + [Define if your system supports the epoll system calls]) + needsignal=yes + have_epoll=yes + AC_LIBOBJ(epoll_sub) + ], AC_MSG_RESULT(no), AC_MSG_RESULT(no)) + fi +fi +AM_CONDITIONAL(EPOLL_BACKEND, [test "x$haveepoll" = "xyes"]) + +haveeventports=no +AC_CHECK_FUNCS(port_create, [haveeventports=yes], ) +if test "x$haveeventports" = "xyes" ; then + AC_DEFINE(HAVE_EVENT_PORTS, 1, + [Define if your system supports event ports]) + needsignal=yes +fi +AM_CONDITIONAL(EVPORT_BACKEND, [test "x$haveeventports" = "xyes"]) + +if test "x$bwin32" = "xtrue"; then + needsignal=yes +fi + +AM_CONDITIONAL(SIGNAL_SUPPORT, [test "x$needsignal" = "xyes"]) + +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T + +AC_CHECK_TYPES([uint64_t, uint32_t, uint16_t, uint8_t, uintptr_t], , , +[#ifdef HAVE_STDINT_H +#include <stdint.h> +#elif defined(HAVE_INTTYPES_H) +#include <inttypes.h> +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif]) + +AC_CHECK_TYPES([fd_mask], , , +[#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif]) + +AC_CHECK_SIZEOF(long long) +AC_CHECK_SIZEOF(long) +AC_CHECK_SIZEOF(int) +AC_CHECK_SIZEOF(short) +AC_CHECK_SIZEOF(size_t) +AC_CHECK_SIZEOF(void *) +AC_CHECK_SIZEOF(off_t) + +AC_CHECK_TYPES([struct in6_addr, struct sockaddr_in6, sa_family_t, struct addrinfo, struct sockaddr_storage], , , +[#define _GNU_SOURCE +#include <sys/types.h> +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_NETINET_IN6_H +#include <netinet/in6.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef _WIN32 +#define WIN32_WINNT 0x400 +#define _WIN32_WINNT 0x400 +#define WIN32_LEAN_AND_MEAN +#if defined(_MSC_VER) && (_MSC_VER < 1300) +#include <winsock.h> +#else +#include <winsock2.h> +#include <ws2tcpip.h> +#endif +#endif +]) +AC_CHECK_MEMBERS([struct in6_addr.s6_addr32, struct in6_addr.s6_addr16, struct sockaddr_in.sin_len, struct sockaddr_in6.sin6_len, struct sockaddr_storage.ss_family, struct sockaddr_storage.__ss_family], , , +[#include <sys/types.h> +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_NETINET_IN6_H +#include <netinet/in6.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef _WIN32 +#define WIN32_WINNT 0x400 +#define _WIN32_WINNT 0x400 +#define WIN32_LEAN_AND_MEAN +#if defined(_MSC_VER) && (_MSC_VER < 1300) +#include <winsock.h> +#else +#include <winsock2.h> +#include <ws2tcpip.h> +#endif +#endif +]) + +AC_CHECK_TYPES([struct so_linger], +[#define HAVE_SO_LINGER], , +[ +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +]) + +AC_MSG_CHECKING([for socklen_t]) +AC_TRY_COMPILE([ + #include <sys/types.h> + #include <sys/socket.h>], + [socklen_t x;], + AC_MSG_RESULT([yes]), + [AC_MSG_RESULT([no]) + AC_DEFINE(socklen_t, unsigned int, + [Define to unsigned int if you dont have it])] +) + +AC_MSG_CHECKING([whether our compiler supports __func__]) +AC_TRY_COMPILE([], + [ const char *cp = __func__; ], + AC_MSG_RESULT([yes]), + AC_MSG_RESULT([no]) + AC_MSG_CHECKING([whether our compiler supports __FUNCTION__]) + AC_TRY_COMPILE([], + [ const char *cp = __FUNCTION__; ], + AC_MSG_RESULT([yes]) + AC_DEFINE(__func__, __FUNCTION__, + [Define to appropriate substitue if compiler doesnt have __func__]), + AC_MSG_RESULT([no]) + AC_DEFINE(__func__, __FILE__, + [Define to appropriate substitue if compiler doesnt have __func__]))) + + +# check if we can compile with pthreads +have_pthreads=no +if test x$bwin32 != xtrue && test "$enable_thread_support" != "no"; then + ACX_PTHREAD([ + AC_DEFINE(HAVE_PTHREADS, 1, + [Define if we have pthreads on this system]) + have_pthreads=yes]) + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + AC_CHECK_SIZEOF(pthread_t, , + [AC_INCLUDES_DEFAULT() + #include <pthread.h> ] + ) +fi +AM_CONDITIONAL(PTHREADS, [test "$have_pthreads" != "no" && test "$enable_thread_support" != "no"]) + +# check if we should compile locking into the library +if test x$enable_thread_support = xno; then + AC_DEFINE(DISABLE_THREAD_SUPPORT, 1, + [Define if libevent should not be compiled with thread support]) +fi + +# check if we should hard-code the mm functions. +if test x$enable_malloc_replacement = xno; then + AC_DEFINE(DISABLE_MM_REPLACEMENT, 1, + [Define if libevent should not allow replacing the mm functions]) +fi + +# check if we should hard-code debugging out +if test x$enable_debug_mode = xno; then + AC_DEFINE(DISABLE_DEBUG_MODE, 1, + [Define if libevent should build without support for a debug mode]) +fi + +# check if we should enable verbose debugging +if test x$enable_verbose_debug = xyes; then + CFLAGS="$CFLAGS -DUSE_DEBUG" +fi + +# check if we have and should use openssl +AM_CONDITIONAL(OPENSSL, [test "$enable_openssl" != "no" && test "$have_openssl" = "yes"]) +if test "x$enable_openssl" = "xyes"; then + AC_SEARCH_LIBS([ERR_remove_thread_state], [crypto], + [AC_DEFINE(HAVE_ERR_REMOVE_THREAD_STATE, 1, [Define to 1 if you have ERR_remove_thread_stat().])]) +fi + +# Add some more warnings which we use in development but not in the +# released versions. (Some relevant gcc versions can't handle these.) +if test x$enable_gcc_warnings != xno && test "$GCC" = "yes"; then + + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [ +#if !defined(__GNUC__) || (__GNUC__ < 4) +#error +#endif])], have_gcc4=yes, have_gcc4=no) + + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [ +#if !defined(__GNUC__) || (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 2) +#error +#endif])], have_gcc42=yes, have_gcc42=no) + + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [ +#if !defined(__GNUC__) || (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 5) +#error +#endif])], have_gcc45=yes, have_gcc45=no) + + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [ +#if !defined(__clang__) +#error +#endif])], have_clang=yes, have_clang=no) + + CFLAGS="$CFLAGS -W -Wfloat-equal -Wundef -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wwrite-strings -Wredundant-decls -Wchar-subscripts -Wcomment -Wformat -Wwrite-strings -Wmissing-declarations -Wredundant-decls -Wnested-externs -Wbad-function-cast -Wswitch-enum" + if test x$enable_gcc_warnings = xyes; then + CFLAGS="$CFLAGS -Werror" + fi + + CFLAGS="$CFLAGS -Wno-unused-parameter -Wstrict-aliasing" + + if test x$have_gcc4 = xyes ; then + # These warnings break gcc 3.3.5 and work on gcc 4.0.2 + CFLAGS="$CFLAGS -Winit-self -Wmissing-field-initializers -Wdeclaration-after-statement" + #CFLAGS="$CFLAGS -Wold-style-definition" + fi + + if test x$have_gcc42 = xyes ; then + # These warnings break gcc 4.0.2 and work on gcc 4.2 + CFLAGS="$CFLAGS -Waddress" + fi + + if test x$have_gcc42 = xyes && test x$have_clang = xno; then + # These warnings break gcc 4.0.2 and clang, but work on gcc 4.2 + CFLAGS="$CFLAGS -Wnormalized=id -Woverride-init" + fi + + if test x$have_gcc45 = xyes ; then + # These warnings work on gcc 4.5 + CFLAGS="$CFLAGS -Wlogical-op" + fi + + if test x$have_clang = xyes; then + # Disable the unused-function warnings, because these trigger + # for minheap-internal.h related code. + CFLAGS="$CFLAGS -Wno-unused-function" + + # clang on macosx emits warnigns for each directory specified which + # isn't "used" generating a lot of build noise (typically 3 warnings + # per file + case "$host_os" in + darwin*) + CFLAGS="$CFLAGS -Qunused-arguments" + ;; + esac + fi + +##This will break the world on some 64-bit architectures +# CFLAGS="$CFLAGS -Winline" + +fi + +LIBEVENT_GC_SECTIONS= +if test "$GCC" = yes && test "$enable_function_sections" = yes ; then + AC_CACHE_CHECK( + [if linker supports omitting unused code and data], + [libevent_cv_gc_sections_runs], + [ + dnl NetBSD will link but likely not run with --gc-sections + dnl http://bugs.ntp.org/1844 + dnl http://gnats.netbsd.org/40401 + dnl --gc-sections causes attempt to load as linux elf, with + dnl wrong syscalls in place. Test a little gauntlet of + dnl simple stdio read code checking for errors, expecting + dnl enough syscall differences that the NetBSD code will + dnl fail even with Linux emulation working as designed. + dnl A shorter test could be refined by someone with access + dnl to a NetBSD host with Linux emulation working. + origCFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -Wl,--gc-sections" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ + #include <stdlib.h> + #include <stdio.h> + ]], + [[ + FILE * fpC; + char buf[32]; + size_t cch; + int read_success_once; + + fpC = fopen("conftest.c", "r"); + if (NULL == fpC) + exit(1); + do { + cch = fread(buf, sizeof(buf), 1, fpC); + read_success_once |= (0 != cch); + } while (0 != cch); + if (!read_success_once) + exit(2); + if (!feof(fpC)) + exit(3); + if (0 != fclose(fpC)) + exit(4); + + exit(EXIT_SUCCESS); + ]] + )], + [ + dnl We have to do this invocation manually so that we can + dnl get the output of conftest.err to make sure it doesn't + dnl mention gc-sections. + if test "X$cross_compiling" = "Xyes" || grep gc-sections conftest.err ; then + libevent_cv_gc_sections_runs=no + else + libevent_cv_gc_sections_runs=no + ./conftest >/dev/null 2>&1 && libevent_cv_gc_sections_runs=yes + fi + ], + [libevent_cv_gc_sections_runs=no] + ) + CFLAGS="$origCFLAGS" + AS_UNSET([origCFLAGS]) + ] + ) + case "$libevent_cv_gc_sections_runs" in + yes) + CFLAGS="-ffunction-sections -fdata-sections $CFLAGS" + LIBEVENT_GC_SECTIONS="-Wl,--gc-sections" + ;; + esac +fi +AC_SUBST([LIBEVENT_GC_SECTIONS]) + +AM_CONDITIONAL([INSTALL_LIBEVENT], [test "$enable_libevent_install" = "yes"]) + +AC_CONFIG_FILES( [libevent.pc libevent_openssl.pc libevent_pthreads.pc] ) +AC_OUTPUT(Makefile) diff --git a/libs/libevent/docs/devpoll.c b/libs/libevent/docs/devpoll.c new file mode 100644 index 0000000000..3a2f86d6f1 --- /dev/null +++ b/libs/libevent/docs/devpoll.c @@ -0,0 +1,311 @@ +/* + * Copyright 2000-2009 Niels Provos <provos@citi.umich.edu> + * Copyright 2009-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "event2/event-config.h" +#include "evconfig-private.h" + +#ifdef EVENT__HAVE_DEVPOLL + +#include <sys/types.h> +#include <sys/resource.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/queue.h> +#include <sys/devpoll.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#include "event2/event.h" +#include "event2/event_struct.h" +#include "event2/thread.h" +#include "event-internal.h" +#include "evsignal-internal.h" +#include "log-internal.h" +#include "evmap-internal.h" +#include "evthread-internal.h" + +struct devpollop { + struct pollfd *events; + int nevents; + int dpfd; + struct pollfd *changes; + int nchanges; +}; + +static void *devpoll_init(struct event_base *); +static int devpoll_add(struct event_base *, int fd, short old, short events, void *); +static int devpoll_del(struct event_base *, int fd, short old, short events, void *); +static int devpoll_dispatch(struct event_base *, struct timeval *); +static void devpoll_dealloc(struct event_base *); + +const struct eventop devpollops = { + "devpoll", + devpoll_init, + devpoll_add, + devpoll_del, + devpoll_dispatch, + devpoll_dealloc, + 1, /* need reinit */ + EV_FEATURE_FDS|EV_FEATURE_O1, + 0 +}; + +#define NEVENT 32000 + +static int +devpoll_commit(struct devpollop *devpollop) +{ + /* + * Due to a bug in Solaris, we have to use pwrite with an offset of 0. + * Write is limited to 2GB of data, until it will fail. + */ + if (pwrite(devpollop->dpfd, devpollop->changes, + sizeof(struct pollfd) * devpollop->nchanges, 0) == -1) + return (-1); + + devpollop->nchanges = 0; + return (0); +} + +static int +devpoll_queue(struct devpollop *devpollop, int fd, int events) { + struct pollfd *pfd; + + if (devpollop->nchanges >= devpollop->nevents) { + /* + * Change buffer is full, must commit it to /dev/poll before + * adding more + */ + if (devpoll_commit(devpollop) != 0) + return (-1); + } + + pfd = &devpollop->changes[devpollop->nchanges++]; + pfd->fd = fd; + pfd->events = events; + pfd->revents = 0; + + return (0); +} + +static void * +devpoll_init(struct event_base *base) +{ + int dpfd, nfiles = NEVENT; + struct rlimit rl; + struct devpollop *devpollop; + + if (!(devpollop = mm_calloc(1, sizeof(struct devpollop)))) + return (NULL); + + if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && + rl.rlim_cur != RLIM_INFINITY) + nfiles = rl.rlim_cur; + + /* Initialize the kernel queue */ + if ((dpfd = evutil_open_closeonexec_("/dev/poll", O_RDWR, 0)) == -1) { + event_warn("open: /dev/poll"); + mm_free(devpollop); + return (NULL); + } + + devpollop->dpfd = dpfd; + + /* Initialize fields */ + /* FIXME: allocating 'nfiles' worth of space here can be + * expensive and unnecessary. See how epoll.c does it instead. */ + devpollop->events = mm_calloc(nfiles, sizeof(struct pollfd)); + if (devpollop->events == NULL) { + mm_free(devpollop); + close(dpfd); + return (NULL); + } + devpollop->nevents = nfiles; + + devpollop->changes = mm_calloc(nfiles, sizeof(struct pollfd)); + if (devpollop->changes == NULL) { + mm_free(devpollop->events); + mm_free(devpollop); + close(dpfd); + return (NULL); + } + + evsig_init_(base); + + return (devpollop); +} + +static int +devpoll_dispatch(struct event_base *base, struct timeval *tv) +{ + struct devpollop *devpollop = base->evbase; + struct pollfd *events = devpollop->events; + struct dvpoll dvp; + int i, res, timeout = -1; + + if (devpollop->nchanges) + devpoll_commit(devpollop); + + if (tv != NULL) + timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; + + dvp.dp_fds = devpollop->events; + dvp.dp_nfds = devpollop->nevents; + dvp.dp_timeout = timeout; + + EVBASE_RELEASE_LOCK(base, th_base_lock); + + res = ioctl(devpollop->dpfd, DP_POLL, &dvp); + + EVBASE_ACQUIRE_LOCK(base, th_base_lock); + + if (res == -1) { + if (errno != EINTR) { + event_warn("ioctl: DP_POLL"); + return (-1); + } + + return (0); + } + + event_debug(("%s: devpoll_wait reports %d", __func__, res)); + + for (i = 0; i < res; i++) { + int which = 0; + int what = events[i].revents; + + if (what & POLLHUP) + what |= POLLIN | POLLOUT; + else if (what & POLLERR) + what |= POLLIN | POLLOUT; + + if (what & POLLIN) + which |= EV_READ; + if (what & POLLOUT) + which |= EV_WRITE; + + if (!which) + continue; + + /* XXX(niels): not sure if this works for devpoll */ + evmap_io_active_(base, events[i].fd, which); + } + + return (0); +} + + +static int +devpoll_add(struct event_base *base, int fd, short old, short events, void *p) +{ + struct devpollop *devpollop = base->evbase; + int res; + (void)p; + + /* + * It's not necessary to OR the existing read/write events that we + * are currently interested in with the new event we are adding. + * The /dev/poll driver ORs any new events with the existing events + * that it has cached for the fd. + */ + + res = 0; + if (events & EV_READ) + res |= POLLIN; + if (events & EV_WRITE) + res |= POLLOUT; + + if (devpoll_queue(devpollop, fd, res) != 0) + return (-1); + + return (0); +} + +static int +devpoll_del(struct event_base *base, int fd, short old, short events, void *p) +{ + struct devpollop *devpollop = base->evbase; + int res; + (void)p; + + res = 0; + if (events & EV_READ) + res |= POLLIN; + if (events & EV_WRITE) + res |= POLLOUT; + + /* + * The only way to remove an fd from the /dev/poll monitored set is + * to use POLLREMOVE by itself. This removes ALL events for the fd + * provided so if we care about two events and are only removing one + * we must re-add the other event after POLLREMOVE. + */ + + if (devpoll_queue(devpollop, fd, POLLREMOVE) != 0) + return (-1); + + if ((res & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) { + /* + * We're not deleting all events, so we must resubmit the + * event that we are still interested in if one exists. + */ + + if ((res & POLLIN) && (old & EV_WRITE)) { + /* Deleting read, still care about write */ + devpoll_queue(devpollop, fd, POLLOUT); + } else if ((res & POLLOUT) && (old & EV_READ)) { + /* Deleting write, still care about read */ + devpoll_queue(devpollop, fd, POLLIN); + } + } + + return (0); +} + +static void +devpoll_dealloc(struct event_base *base) +{ + struct devpollop *devpollop = base->evbase; + + evsig_dealloc_(base); + if (devpollop->events) + mm_free(devpollop->events); + if (devpollop->changes) + mm_free(devpollop->changes); + if (devpollop->dpfd >= 0) + close(devpollop->dpfd); + + memset(devpollop, 0, sizeof(struct devpollop)); + mm_free(devpollop); +} + +#endif /* EVENT__HAVE_DEVPOLL */ diff --git a/libs/libevent/docs/epoll.c b/libs/libevent/docs/epoll.c new file mode 100644 index 0000000000..bf730b23db --- /dev/null +++ b/libs/libevent/docs/epoll.c @@ -0,0 +1,540 @@ +/* + * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright 2007-2012 Niels Provos, Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "event2/event-config.h" +#include "evconfig-private.h" + +#ifdef EVENT__HAVE_EPOLL + +#include <stdint.h> +#include <sys/types.h> +#include <sys/resource.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/queue.h> +#include <sys/epoll.h> +#include <signal.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#ifdef EVENT__HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef EVENT__HAVE_SYS_TIMERFD_H +#include <sys/timerfd.h> +#endif + +#include "event-internal.h" +#include "evsignal-internal.h" +#include "event2/thread.h" +#include "evthread-internal.h" +#include "log-internal.h" +#include "evmap-internal.h" +#include "changelist-internal.h" +#include "time-internal.h" + +/* Since Linux 2.6.17, epoll is able to report about peer half-closed connection + using special EPOLLRDHUP flag on a read event. +*/ +#if !defined(EPOLLRDHUP) +#define EPOLLRDHUP 0 +#define EARLY_CLOSE_IF_HAVE_RDHUP 0 +#else +#define EARLY_CLOSE_IF_HAVE_RDHUP EV_FEATURE_EARLY_CLOSE +#endif + +#include "epolltable-internal.h" + +#if defined(EVENT__HAVE_SYS_TIMERFD_H) && \ + defined(EVENT__HAVE_TIMERFD_CREATE) && \ + defined(HAVE_POSIX_MONOTONIC) && defined(TFD_NONBLOCK) && \ + defined(TFD_CLOEXEC) +/* Note that we only use timerfd if TFD_NONBLOCK and TFD_CLOEXEC are available + and working. This means that we can't support it on 2.6.25 (where timerfd + was introduced) or 2.6.26, since 2.6.27 introduced those flags. + */ +#define USING_TIMERFD +#endif + +struct epollop { + struct epoll_event *events; + int nevents; + int epfd; +#ifdef USING_TIMERFD + int timerfd; +#endif +}; + +static void *epoll_init(struct event_base *); +static int epoll_dispatch(struct event_base *, struct timeval *); +static void epoll_dealloc(struct event_base *); + +static const struct eventop epollops_changelist = { + "epoll (with changelist)", + epoll_init, + event_changelist_add_, + event_changelist_del_, + epoll_dispatch, + epoll_dealloc, + 1, /* need reinit */ + EV_FEATURE_ET|EV_FEATURE_O1| EARLY_CLOSE_IF_HAVE_RDHUP, + EVENT_CHANGELIST_FDINFO_SIZE +}; + + +static int epoll_nochangelist_add(struct event_base *base, evutil_socket_t fd, + short old, short events, void *p); +static int epoll_nochangelist_del(struct event_base *base, evutil_socket_t fd, + short old, short events, void *p); + +const struct eventop epollops = { + "epoll", + epoll_init, + epoll_nochangelist_add, + epoll_nochangelist_del, + epoll_dispatch, + epoll_dealloc, + 1, /* need reinit */ + EV_FEATURE_ET|EV_FEATURE_O1|EV_FEATURE_EARLY_CLOSE, + 0 +}; + +#define INITIAL_NEVENT 32 +#define MAX_NEVENT 4096 + +/* On Linux kernels at least up to 2.6.24.4, epoll can't handle timeout + * values bigger than (LONG_MAX - 999ULL)/HZ. HZ in the wild can be + * as big as 1000, and LONG_MAX can be as small as (1<<31)-1, so the + * largest number of msec we can support here is 2147482. Let's + * round that down by 47 seconds. + */ +#define MAX_EPOLL_TIMEOUT_MSEC (35*60*1000) + +static void * +epoll_init(struct event_base *base) +{ + int epfd = -1; + struct epollop *epollop; + +#ifdef EVENT__HAVE_EPOLL_CREATE1 + /* First, try the shiny new epoll_create1 interface, if we have it. */ + epfd = epoll_create1(EPOLL_CLOEXEC); +#endif + if (epfd == -1) { + /* Initialize the kernel queue using the old interface. (The + size field is ignored since 2.6.8.) */ + if ((epfd = epoll_create(32000)) == -1) { + if (errno != ENOSYS) + event_warn("epoll_create"); + return (NULL); + } + evutil_make_socket_closeonexec(epfd); + } + + if (!(epollop = mm_calloc(1, sizeof(struct epollop)))) { + close(epfd); + return (NULL); + } + + epollop->epfd = epfd; + + /* Initialize fields */ + epollop->events = mm_calloc(INITIAL_NEVENT, sizeof(struct epoll_event)); + if (epollop->events == NULL) { + mm_free(epollop); + close(epfd); + return (NULL); + } + epollop->nevents = INITIAL_NEVENT; + + if ((base->flags & EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST) != 0 || + ((base->flags & EVENT_BASE_FLAG_IGNORE_ENV) == 0 && + evutil_getenv_("EVENT_EPOLL_USE_CHANGELIST") != NULL)) { + + base->evsel = &epollops_changelist; + } + +#ifdef USING_TIMERFD + /* + The epoll interface ordinarily gives us one-millisecond precision, + so on Linux it makes perfect sense to use the CLOCK_MONOTONIC_COARSE + timer. But when the user has set the new PRECISE_TIMER flag for an + event_base, we can try to use timerfd to give them finer granularity. + */ + if ((base->flags & EVENT_BASE_FLAG_PRECISE_TIMER) && + base->monotonic_timer.monotonic_clock == CLOCK_MONOTONIC) { + int fd; + fd = epollop->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC); + if (epollop->timerfd >= 0) { + struct epoll_event epev; + memset(&epev, 0, sizeof(epev)); + epev.data.fd = epollop->timerfd; + epev.events = EPOLLIN; + if (epoll_ctl(epollop->epfd, EPOLL_CTL_ADD, fd, &epev) < 0) { + event_warn("epoll_ctl(timerfd)"); + close(fd); + epollop->timerfd = -1; + } + } else { + if (errno != EINVAL && errno != ENOSYS) { + /* These errors probably mean that we were + * compiled with timerfd/TFD_* support, but + * we're running on a kernel that lacks those. + */ + event_warn("timerfd_create"); + } + epollop->timerfd = -1; + } + } else { + epollop->timerfd = -1; + } +#endif + + evsig_init_(base); + + return (epollop); +} + +static const char * +change_to_string(int change) +{ + change &= (EV_CHANGE_ADD|EV_CHANGE_DEL); + if (change == EV_CHANGE_ADD) { + return "add"; + } else if (change == EV_CHANGE_DEL) { + return "del"; + } else if (change == 0) { + return "none"; + } else { + return "???"; + } +} + +static const char * +epoll_op_to_string(int op) +{ + return op == EPOLL_CTL_ADD?"ADD": + op == EPOLL_CTL_DEL?"DEL": + op == EPOLL_CTL_MOD?"MOD": + "???"; +} + +#define PRINT_CHANGES(op, events, ch, status) \ + "Epoll %s(%d) on fd %d " status ". " \ + "Old events were %d; " \ + "read change was %d (%s); " \ + "write change was %d (%s); " \ + "close change was %d (%s)", \ + epoll_op_to_string(op), \ + events, \ + ch->fd, \ + ch->old_events, \ + ch->read_change, \ + change_to_string(ch->read_change), \ + ch->write_change, \ + change_to_string(ch->write_change), \ + ch->close_change, \ + change_to_string(ch->close_change) + +static int +epoll_apply_one_change(struct event_base *base, + struct epollop *epollop, + const struct event_change *ch) +{ + struct epoll_event epev; + int op, events = 0; + int idx; + + idx = EPOLL_OP_TABLE_INDEX(ch); + op = epoll_op_table[idx].op; + events = epoll_op_table[idx].events; + + if (!events) { + EVUTIL_ASSERT(op == 0); + return 0; + } + + if ((ch->read_change|ch->write_change) & EV_CHANGE_ET) + events |= EPOLLET; + + memset(&epev, 0, sizeof(epev)); + epev.data.fd = ch->fd; + epev.events = events; + if (epoll_ctl(epollop->epfd, op, ch->fd, &epev) == 0) { + event_debug((PRINT_CHANGES(op, epev.events, ch, "okay"))); + return 0; + } + + switch (op) { + case EPOLL_CTL_MOD: + if (errno == ENOENT) { + /* If a MOD operation fails with ENOENT, the + * fd was probably closed and re-opened. We + * should retry the operation as an ADD. + */ + if (epoll_ctl(epollop->epfd, EPOLL_CTL_ADD, ch->fd, &epev) == -1) { + event_warn("Epoll MOD(%d) on %d retried as ADD; that failed too", + (int)epev.events, ch->fd); + return -1; + } else { + event_debug(("Epoll MOD(%d) on %d retried as ADD; succeeded.", + (int)epev.events, + ch->fd)); + return 0; + } + } + break; + case EPOLL_CTL_ADD: + if (errno == EEXIST) { + /* If an ADD operation fails with EEXIST, + * either the operation was redundant (as with a + * precautionary add), or we ran into a fun + * kernel bug where using dup*() to duplicate the + * same file into the same fd gives you the same epitem + * rather than a fresh one. For the second case, + * we must retry with MOD. */ + if (epoll_ctl(epollop->epfd, EPOLL_CTL_MOD, ch->fd, &epev) == -1) { + event_warn("Epoll ADD(%d) on %d retried as MOD; that failed too", + (int)epev.events, ch->fd); + return -1; + } else { + event_debug(("Epoll ADD(%d) on %d retried as MOD; succeeded.", + (int)epev.events, + ch->fd)); + return 0; + } + } + break; + case EPOLL_CTL_DEL: + if (errno == ENOENT || errno == EBADF || errno == EPERM) { + /* If a delete fails with one of these errors, + * that's fine too: we closed the fd before we + * got around to calling epoll_dispatch. */ + event_debug(("Epoll DEL(%d) on fd %d gave %s: DEL was unnecessary.", + (int)epev.events, + ch->fd, + strerror(errno))); + return 0; + } + break; + default: + break; + } + + event_warn(PRINT_CHANGES(op, epev.events, ch, "failed")); + return -1; +} + +static int +epoll_apply_changes(struct event_base *base) +{ + struct event_changelist *changelist = &base->changelist; + struct epollop *epollop = base->evbase; + struct event_change *ch; + + int r = 0; + int i; + + for (i = 0; i < changelist->n_changes; ++i) { + ch = &changelist->changes[i]; + if (epoll_apply_one_change(base, epollop, ch) < 0) + r = -1; + } + + return (r); +} + +static int +epoll_nochangelist_add(struct event_base *base, evutil_socket_t fd, + short old, short events, void *p) +{ + struct event_change ch; + ch.fd = fd; + ch.old_events = old; + ch.read_change = ch.write_change = ch.close_change = 0; + if (events & EV_WRITE) + ch.write_change = EV_CHANGE_ADD | + (events & EV_ET); + if (events & EV_READ) + ch.read_change = EV_CHANGE_ADD | + (events & EV_ET); + if (events & EV_CLOSED) + ch.close_change = EV_CHANGE_ADD | + (events & EV_ET); + + return epoll_apply_one_change(base, base->evbase, &ch); +} + +static int +epoll_nochangelist_del(struct event_base *base, evutil_socket_t fd, + short old, short events, void *p) +{ + struct event_change ch; + ch.fd = fd; + ch.old_events = old; + ch.read_change = ch.write_change = ch.close_change = 0; + if (events & EV_WRITE) + ch.write_change = EV_CHANGE_DEL; + if (events & EV_READ) + ch.read_change = EV_CHANGE_DEL; + if (events & EV_CLOSED) + ch.close_change = EV_CHANGE_DEL; + + return epoll_apply_one_change(base, base->evbase, &ch); +} + +static int +epoll_dispatch(struct event_base *base, struct timeval *tv) +{ + struct epollop *epollop = base->evbase; + struct epoll_event *events = epollop->events; + int i, res; + long timeout = -1; + +#ifdef USING_TIMERFD + if (epollop->timerfd >= 0) { + struct itimerspec is; + is.it_interval.tv_sec = 0; + is.it_interval.tv_nsec = 0; + if (tv == NULL) { + /* No timeout; disarm the timer. */ + is.it_value.tv_sec = 0; + is.it_value.tv_nsec = 0; + } else { + if (tv->tv_sec == 0 && tv->tv_usec == 0) { + /* we need to exit immediately; timerfd can't + * do that. */ + timeout = 0; + } + is.it_value.tv_sec = tv->tv_sec; + is.it_value.tv_nsec = tv->tv_usec * 1000; + } + /* TODO: we could avoid unnecessary syscalls here by only + calling timerfd_settime when the top timeout changes, or + when we're called with a different timeval. + */ + if (timerfd_settime(epollop->timerfd, 0, &is, NULL) < 0) { + event_warn("timerfd_settime"); + } + } else +#endif + if (tv != NULL) { + timeout = evutil_tv_to_msec_(tv); + if (timeout < 0 || timeout > MAX_EPOLL_TIMEOUT_MSEC) { + /* Linux kernels can wait forever if the timeout is + * too big; see comment on MAX_EPOLL_TIMEOUT_MSEC. */ + timeout = MAX_EPOLL_TIMEOUT_MSEC; + } + } + + epoll_apply_changes(base); + event_changelist_remove_all_(&base->changelist, base); + + EVBASE_RELEASE_LOCK(base, th_base_lock); + + res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout); + + EVBASE_ACQUIRE_LOCK(base, th_base_lock); + + if (res == -1) { + if (errno != EINTR) { + event_warn("epoll_wait"); + return (-1); + } + + return (0); + } + + event_debug(("%s: epoll_wait reports %d", __func__, res)); + EVUTIL_ASSERT(res <= epollop->nevents); + + for (i = 0; i < res; i++) { + int what = events[i].events; + short ev = 0; +#ifdef USING_TIMERFD + if (events[i].data.fd == epollop->timerfd) + continue; +#endif + + if (what & (EPOLLHUP|EPOLLERR)) { + ev = EV_READ | EV_WRITE; + } else { + if (what & EPOLLIN) + ev |= EV_READ; + if (what & EPOLLOUT) + ev |= EV_WRITE; + if (what & EPOLLRDHUP) + ev |= EV_CLOSED; + } + + if (!ev) + continue; + + evmap_io_active_(base, events[i].data.fd, ev | EV_ET); + } + + if (res == epollop->nevents && epollop->nevents < MAX_NEVENT) { + /* We used all of the event space this time. We should + be ready for more events next time. */ + int new_nevents = epollop->nevents * 2; + struct epoll_event *new_events; + + new_events = mm_realloc(epollop->events, + new_nevents * sizeof(struct epoll_event)); + if (new_events) { + epollop->events = new_events; + epollop->nevents = new_nevents; + } + } + + return (0); +} + + +static void +epoll_dealloc(struct event_base *base) +{ + struct epollop *epollop = base->evbase; + + evsig_dealloc_(base); + if (epollop->events) + mm_free(epollop->events); + if (epollop->epfd >= 0) + close(epollop->epfd); +#ifdef USING_TIMERFD + if (epollop->timerfd >= 0) + close(epollop->timerfd); +#endif + + memset(epollop, 0, sizeof(struct epollop)); + mm_free(epollop); +} + +#endif /* EVENT__HAVE_EPOLL */ diff --git a/libs/libevent/docs/epoll_sub.c b/libs/libevent/docs/epoll_sub.c new file mode 100644 index 0000000000..3f01f6a699 --- /dev/null +++ b/libs/libevent/docs/epoll_sub.c @@ -0,0 +1,66 @@ +/* + * Copyright 2003-2009 Niels Provos <provos@citi.umich.edu> + * Copyright 2009-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "evconfig-private.h" +#include <stdint.h> + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/syscall.h> +#include <sys/epoll.h> +#include <unistd.h> +#include <errno.h> + +int +epoll_create(int size) +{ +#if !defined(__NR_epoll_create) && defined(__NR_epoll_create1) + if (size <= 0) { + errno = EINVAL; + return -1; + } + return (syscall(__NR_epoll_create1, 0)); +#else + return (syscall(__NR_epoll_create, size)); +#endif +} + +int +epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) +{ + + return (syscall(__NR_epoll_ctl, epfd, op, fd, event)); +} + +int +epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) +{ +#if !defined(__NR_epoll_wait) && defined(__NR_epoll_pwait) + return (syscall(__NR_epoll_pwait, epfd, events, maxevents, timeout, NULL, 0)); +#else + return (syscall(__NR_epoll_wait, epfd, events, maxevents, timeout)); +#endif +} diff --git a/libs/libevent/docs/evconfig-private.h.cmake b/libs/libevent/docs/evconfig-private.h.cmake new file mode 100644 index 0000000000..32f04794d2 --- /dev/null +++ b/libs/libevent/docs/evconfig-private.h.cmake @@ -0,0 +1,35 @@ + +#ifndef EVCONFIG_PRIVATE_H_INCLUDED_ +#define EVCONFIG_PRIVATE_H_INCLUDED_ + +/* Enable extensions on AIX 3, Interix. */ +#cmakedefine _ALL_SOURCE + +/* Enable GNU extensions on systems that have them. */ +#cmakedefine _GNU_SOURCE 1 + +/* Enable threading extensions on Solaris. */ +#cmakedefine _POSIX_PTHREAD_SEMANTICS 1 + +/* Enable extensions on HP NonStop. */ +#cmakedefine _TANDEM_SOURCE 1 + +/* Enable general extensions on Solaris. */ +#cmakedefine __EXTENSIONS__ + +/* Number of bits in a file offset, on hosts where this is settable. */ +#cmakedefine _FILE_OFFSET_BITS 1 +/* Define for large files, on AIX-style hosts. */ +#cmakedefine _LARGE_FILES 1 + +/* Define to 1 if on MINIX. */ +#cmakedefine _MINIX 1 + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#cmakedefine _POSIX_1_SOURCE 1 + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#cmakedefine _POSIX_SOURCE 1 + +#endif diff --git a/libs/libevent/docs/evconfig-private.h.in b/libs/libevent/docs/evconfig-private.h.in new file mode 100644 index 0000000000..7b3dfdb10e --- /dev/null +++ b/libs/libevent/docs/evconfig-private.h.in @@ -0,0 +1,48 @@ +/* evconfig-private.h template - see "Configuration Header Templates" */ +/* in AC manual. Kevin Bowling <kevin.bowling@kev009.com */ +#ifndef EVCONFIG_PRIVATE_H_INCLUDED_ +#define EVCONFIG_PRIVATE_H_INCLUDED_ + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to 1 if on MINIX. */ +#ifndef _MINIX +#undef _MINIX +#endif + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#ifndef _POSIX_1_SOURCE +#undef _POSIX_1_SOURCE +#endif + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#ifndef _POSIX_SOURCE +#undef _POSIX_SOURCE +#endif + +#endif diff --git a/libs/libevent/docs/evdns.3 b/libs/libevent/docs/evdns.3 new file mode 100644 index 0000000000..10414fa2ef --- /dev/null +++ b/libs/libevent/docs/evdns.3 @@ -0,0 +1,322 @@ +.\" +.\" Copyright (c) 2006 Niels Provos <provos@citi.umich.edu> +.\" 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. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED ``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. +.\" +.Dd October 7, 2006 +.Dt EVDNS 3 +.Os +.Sh NAME +.Nm evdns_init +.Nm evdns_shutdown +.Nm evdns_err_to_string +.Nm evdns_nameserver_add +.Nm evdns_count_nameservers +.Nm evdns_clear_nameservers_and_suspend +.Nm evdns_resume +.Nm evdns_nameserver_ip_add +.Nm evdns_resolve_ipv4 +.Nm evdns_resolve_reverse +.Nm evdns_resolv_conf_parse +.Nm evdns_config_windows_nameservers +.Nm evdns_search_clear +.Nm evdns_search_add +.Nm evdns_search_ndots_set +.Nm evdns_set_log_fn +.Nd asynchronous functions for DNS resolution. +.Sh SYNOPSIS +.Fd #include <sys/time.h> +.Fd #include <event.h> +.Fd #include <evdns.h> +.Ft int +.Fn evdns_init +.Ft void +.Fn evdns_shutdown "int fail_requests" +.Ft "const char *" +.Fn evdns_err_to_string "int err" +.Ft int +.Fn evdns_nameserver_add "unsigned long int address" +.Ft int +.Fn evdns_count_nameservers +.Ft int +.Fn evdns_clear_nameservers_and_suspend +.Ft int +.Fn evdns_resume +.Ft int +.Fn evdns_nameserver_ip_add(const char *ip_as_string); +.Ft int +.Fn evdns_resolve_ipv4 "const char *name" "int flags" "evdns_callback_type callback" "void *ptr" +.Ft int +.Fn evdns_resolve_reverse "struct in_addr *in" "int flags" "evdns_callback_type callback" "void *ptr" +.Ft int +.Fn evdns_resolv_conf_parse "int flags" "const char *" +.Ft void +.Fn evdns_search_clear +.Ft void +.Fn evdns_search_add "const char *domain" +.Ft void +.Fn evdns_search_ndots_set "const int ndots" +.Ft void +.Fn evdns_set_log_fn "evdns_debug_log_fn_type fn" +.Ft int +.Fn evdns_config_windows_nameservers +.Sh DESCRIPTION +Welcome, gentle reader +.Pp +Async DNS lookups are really a whole lot harder than they should be, +mostly stemming from the fact that the libc resolver has never been +very good at them. Before you use this library you should see if libc +can do the job for you with the modern async call getaddrinfo_a +(see http://www.imperialviolet.org/page25.html#e498). Otherwise, +please continue. +.Pp +This code is based on libevent and you must call event_init before +any of the APIs in this file. You must also seed the OpenSSL random +source if you are using OpenSSL for ids (see below). +.Pp +This library is designed to be included and shipped with your source +code. You statically link with it. You should also test for the +existence of strtok_r and define HAVE_STRTOK_R if you have it. +.Pp +The DNS protocol requires a good source of id numbers and these +numbers should be unpredictable for spoofing reasons. There are +three methods for generating them here and you must define exactly +one of them. In increasing order of preference: +.Pp +.Bl -tag -width "DNS_USE_GETTIMEOFDAY_FOR_ID" -compact -offset indent +.It DNS_USE_GETTIMEOFDAY_FOR_ID +Using the bottom 16 bits of the usec result from gettimeofday. This +is a pretty poor solution but should work anywhere. +.It DNS_USE_CPU_CLOCK_FOR_ID +Using the bottom 16 bits of the nsec result from the CPU's time +counter. This is better, but may not work everywhere. Requires +POSIX realtime support and you'll need to link against -lrt on +glibc systems at least. +.It DNS_USE_OPENSSL_FOR_ID +Uses the OpenSSL RAND_bytes call to generate the data. You must +have seeded the pool before making any calls to this library. +.El +.Pp +The library keeps track of the state of nameservers and will avoid +them when they go down. Otherwise it will round robin between them. +.Pp +Quick start guide: + #include "evdns.h" + void callback(int result, char type, int count, int ttl, + void *addresses, void *arg); + evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf"); + evdns_resolve("www.hostname.com", 0, callback, NULL); +.Pp +When the lookup is complete the callback function is called. The +first argument will be one of the DNS_ERR_* defines in evdns.h. +Hopefully it will be DNS_ERR_NONE, in which case type will be +DNS_IPv4_A, count will be the number of IP addresses, ttl is the time +which the data can be cached for (in seconds), addresses will point +to an array of uint32_t's and arg will be whatever you passed to +evdns_resolve. +.Pp +Searching: +.Pp +In order for this library to be a good replacement for glibc's resolver it +supports searching. This involves setting a list of default domains, in +which names will be queried for. The number of dots in the query name +determines the order in which this list is used. +.Pp +Searching appears to be a single lookup from the point of view of the API, +although many DNS queries may be generated from a single call to +evdns_resolve. Searching can also drastically slow down the resolution +of names. +.Pp +To disable searching: +.Bl -enum -compact -offset indent +.It +Never set it up. If you never call +.Fn evdns_resolv_conf_parse, +.Fn evdns_init, +or +.Fn evdns_search_add +then no searching will occur. +.It +If you do call +.Fn evdns_resolv_conf_parse +then don't pass +.Va DNS_OPTION_SEARCH +(or +.Va DNS_OPTIONS_ALL, +which implies it). +.It +When calling +.Fn evdns_resolve, +pass the +.Va DNS_QUERY_NO_SEARCH +flag. +.El +.Pp +The order of searches depends on the number of dots in the name. If the +number is greater than the ndots setting then the names is first tried +globally. Otherwise each search domain is appended in turn. +.Pp +The ndots setting can either be set from a resolv.conf, or by calling +evdns_search_ndots_set. +.Pp +For example, with ndots set to 1 (the default) and a search domain list of +["myhome.net"]: + Query: www + Order: www.myhome.net, www. +.Pp + Query: www.abc + Order: www.abc., www.abc.myhome.net +.Pp +.Sh API reference +.Pp +.Bl -tag -width 0123456 +.It Ft int Fn evdns_init +Initializes support for non-blocking name resolution by calling +.Fn evdns_resolv_conf_parse +on UNIX and +.Fn evdns_config_windows_nameservers +on Windows. +.It Ft int Fn evdns_nameserver_add "unsigned long int address" +Add a nameserver. The address should be an IP address in +network byte order. The type of address is chosen so that +it matches in_addr.s_addr. +Returns non-zero on error. +.It Ft int Fn evdns_nameserver_ip_add "const char *ip_as_string" +This wraps the above function by parsing a string as an IP +address and adds it as a nameserver. +Returns non-zero on error +.It Ft int Fn evdns_resolve "const char *name" "int flags" "evdns_callback_type callback" "void *ptr" +Resolve a name. The name parameter should be a DNS name. +The flags parameter should be 0, or DNS_QUERY_NO_SEARCH +which disables searching for this query. (see defn of +searching above). +.Pp +The callback argument is a function which is called when +this query completes and ptr is an argument which is passed +to that callback function. +.Pp +Returns non-zero on error +.It Ft void Fn evdns_search_clear +Clears the list of search domains +.It Ft void Fn evdns_search_add "const char *domain" +Add a domain to the list of search domains +.It Ft void Fn evdns_search_ndots_set "int ndots" +Set the number of dots which, when found in a name, causes +the first query to be without any search domain. +.It Ft int Fn evdns_count_nameservers "void" +Return the number of configured nameservers (not necessarily the +number of running nameservers). This is useful for double-checking +whether our calls to the various nameserver configuration functions +have been successful. +.It Ft int Fn evdns_clear_nameservers_and_suspend "void" +Remove all currently configured nameservers, and suspend all pending +resolves. Resolves will not necessarily be re-attempted until +evdns_resume() is called. +.It Ft int Fn evdns_resume "void" +Re-attempt resolves left in limbo after an earlier call to +evdns_clear_nameservers_and_suspend(). +.It Ft int Fn evdns_config_windows_nameservers "void" +Attempt to configure a set of nameservers based on platform settings on +a win32 host. Preferentially tries to use GetNetworkParams; if that fails, +looks in the registry. Returns 0 on success, nonzero on failure. +.It Ft int Fn evdns_resolv_conf_parse "int flags" "const char *filename" +Parse a resolv.conf like file from the given filename. +.Pp +See the man page for resolv.conf for the format of this file. +The flags argument determines what information is parsed from +this file: +.Bl -tag -width "DNS_OPTION_NAMESERVERS" -offset indent -compact -nested +.It DNS_OPTION_SEARCH +domain, search and ndots options +.It DNS_OPTION_NAMESERVERS +nameserver lines +.It DNS_OPTION_MISC +timeout and attempts options +.It DNS_OPTIONS_ALL +all of the above +.El +.Pp +The following directives are not parsed from the file: + sortlist, rotate, no-check-names, inet6, debug +.Pp +Returns non-zero on error: +.Bl -tag -width "0" -offset indent -compact -nested +.It 0 +no errors +.It 1 +failed to open file +.It 2 +failed to stat file +.It 3 +file too large +.It 4 +out of memory +.It 5 +short read from file +.El +.El +.Sh Internals: +Requests are kept in two queues. The first is the inflight queue. In +this queue requests have an allocated transaction id and nameserver. +They will soon be transmitted if they haven't already been. +.Pp +The second is the waiting queue. The size of the inflight ring is +limited and all other requests wait in waiting queue for space. This +bounds the number of concurrent requests so that we don't flood the +nameserver. Several algorithms require a full walk of the inflight +queue and so bounding its size keeps thing going nicely under huge +(many thousands of requests) loads. +.Pp +If a nameserver loses too many requests it is considered down and we +try not to use it. After a while we send a probe to that nameserver +(a lookup for google.com) and, if it replies, we consider it working +again. If the nameserver fails a probe we wait longer to try again +with the next probe. +.Sh SEE ALSO +.Xr event 3 , +.Xr gethostbyname 3 , +.Xr resolv.conf 5 +.Sh HISTORY +The +.Nm evdns +API was developed by Adam Langley on top of the +.Nm libevent +API. +The code was integrate into +.Nm Tor +by Nick Mathewson and finally put into +.Nm libevent +itself by Niels Provos. +.Sh AUTHORS +The +.Nm evdns +API and code was written by Adam Langley with significant +contributions by Nick Mathewson. +.Sh BUGS +This documentation is neither complete nor authoritative. +If you are in doubt about the usage of this API then +check the source code to find out how it works, write +up the missing piece of documentation and send it to +me for inclusion in this man page. diff --git a/libs/libevent/docs/event-config.h.cmake b/libs/libevent/docs/event-config.h.cmake new file mode 100644 index 0000000000..76e0d1f981 --- /dev/null +++ b/libs/libevent/docs/event-config.h.cmake @@ -0,0 +1,534 @@ +/* event-config.h + * + * This file was generated by cmake when the makefiles were generated. + * + * DO NOT EDIT THIS FILE. + * + * Do not rely on macros in this file existing in later versions. + */ +#ifndef EVENT2_EVENT_CONFIG_H_INCLUDED_ +#define EVENT2_EVENT_CONFIG_H_INCLUDED_ + +/* Numeric representation of the version */ +#define EVENT__NUMERIC_VERSION @EVENT_NUMERIC_VERSION@ +#define EVENT__PACKAGE_VERSION "@EVENT_PACKAGE_VERSION@" + +#define EVENT__VERSION_MAJOR @EVENT_VERSION_MAJOR@ +#define EVENT__VERSION_MINOR @EVENT_VERSION_MINOR@ +#define EVENT__VERSION_PATCH @EVENT_VERSION_PATCH@ + +/* Version number of package */ +#define EVENT__VERSION "@EVENT_VERSION@" + +/* Name of package */ +#define EVENT__PACKAGE "libevent" + +/* Define to the address where bug reports for this package should be sent. */ +#define EVENT__PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define EVENT__PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define EVENT__PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define EVENT__PACKAGE_TARNAME "" + +/* Define if libevent should build without support for a debug mode */ +#cmakedefine EVENT__DISABLE_DEBUG_MODE + +/* Define if libevent should not allow replacing the mm functions */ +#cmakedefine EVENT__DISABLE_MM_REPLACEMENT + +/* Define if libevent should not be compiled with thread support */ +#cmakedefine EVENT__DISABLE_THREAD_SUPPORT + +/* Define to 1 if you have the `accept4' function. */ +#cmakedefine EVENT__HAVE_ACCEPT4 + +/* Define to 1 if you have the `arc4random' function. */ +#cmakedefine EVENT__HAVE_ARC4RANDOM + +/* Define to 1 if you have the `arc4random_buf' function. */ +#cmakedefine EVENT__HAVE_ARC4RANDOM_BUF + +/* Define if clock_gettime is available in libc */ +#cmakedefine EVENT__DNS_USE_CPU_CLOCK_FOR_ID + +/* Define is no secure id variant is available */ +#cmakedefine EVENT__DNS_USE_GETTIMEOFDAY_FOR_ID +#cmakedefine EVENT__DNS_USE_FTIME_FOR_ID + +/* Define to 1 if you have the <arpa/inet.h> header file. */ +#cmakedefine EVENT__HAVE_ARPA_INET_H + +/* Define to 1 if you have the `clock_gettime' function. */ +#cmakedefine EVENT__HAVE_CLOCK_GETTIME + +/* Define to 1 if you have the declaration of `CTL_KERN'. */ +#cmakedefine EVENT__HAVE_DECL_CTL_KERN + +/* Define to 1 if you have the declaration of `KERN_ARND'. */ +#cmakedefine EVENT__HAVE_DECL_KERN_ARND + +/* Define to 1 if you have the declaration of `KERN_RANDOM'. */ +#cmakedefine EVENT__HAVE_DECL_KERN_RANDOM + +/* Define if /dev/poll is available */ +#cmakedefine EVENT__HAVE_DEVPOLL + +/* Define to 1 if you have the <netdb.h> header file. */ +#cmakedefine EVENT__HAVE_NETDB_H + +/* Define to 1 if fd_mask type is defined */ +#cmakedefine EVENT__HAVE_FD_MASK + +/* Define to 1 if the <sys/queue.h> header file defines TAILQ_FOREACH. */ +#cmakedefine EVENT__HAVE_TAILQFOREACH + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#cmakedefine EVENT__HAVE_DLFCN_H + +/* Define if your system supports the epoll system calls */ +#cmakedefine EVENT__HAVE_EPOLL + +/* Define to 1 if you have the `epoll_create1' function. */ +#cmakedefine EVENT__HAVE_EPOLL_CREATE1 + +/* Define to 1 if you have the `epoll_ctl' function. */ +#cmakedefine EVENT__HAVE_EPOLL_CTL + +/* Define to 1 if you have the `eventfd' function. */ +#cmakedefine EVENT__HAVE_EVENTFD + +/* Define if your system supports event ports */ +#cmakedefine EVENT__HAVE_EVENT_PORTS + +/* Define to 1 if you have the `fcntl' function. */ +#cmakedefine EVENT__HAVE_FCNTL + +/* Define to 1 if you have the <fcntl.h> header file. */ +#cmakedefine EVENT__HAVE_FCNTL_H + +/* Define to 1 if you have the `getaddrinfo' function. */ +#cmakedefine EVENT__HAVE_GETADDRINFO + +/* Define to 1 if you have the `getegid' function. */ +#cmakedefine EVENT__HAVE_GETEGID + +/* Define to 1 if you have the `geteuid' function. */ +#cmakedefine EVENT__HAVE_GETEUID + +/* TODO: Check for different gethostname argument counts. CheckPrototypeDefinition.cmake can be used. */ +/* Define this if you have any gethostbyname_r() */ +#cmakedefine EVENT__HAVE_GETHOSTBYNAME_R + +/* Define this if gethostbyname_r takes 3 arguments */ +#cmakedefine EVENT__HAVE_GETHOSTBYNAME_R_3_ARG + +/* Define this if gethostbyname_r takes 5 arguments */ +#cmakedefine EVENT__HAVE_GETHOSTBYNAME_R_5_ARG + +/* Define this if gethostbyname_r takes 6 arguments */ +#cmakedefine EVENT__HAVE_GETHOSTBYNAME_R_6_ARG + +/* Define to 1 if you have the `getifaddrs' function. */ +#cmakedefine EVENT__HAVE_GETIFADDRS + +/* Define to 1 if you have the `getnameinfo' function. */ +#cmakedefine EVENT__HAVE_GETNAMEINFO + +/* Define to 1 if you have the `getprotobynumber' function. */ +#cmakedefine EVENT__HAVE_GETPROTOBYNUMBER + +/* Define to 1 if you have the `getservbyname' function. */ +#cmakedefine EVENT__HAVE_GETSERVBYNAME + +/* Define to 1 if you have the `gettimeofday' function. */ +#cmakedefine EVENT__HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the <ifaddrs.h> header file. */ +#cmakedefine EVENT__HAVE_IFADDRS_H + +/* Define to 1 if you have the `inet_ntop' function. */ +#cmakedefine EVENT__HAVE_INET_NTOP + +/* Define to 1 if you have the `inet_pton' function. */ +#cmakedefine EVENT__HAVE_INET_PTON + +/* Define to 1 if you have the <inttypes.h> header file. */ +#cmakedefine EVENT__HAVE_INTTYPES_H + +/* Define to 1 if you have the `issetugid' function. */ +#cmakedefine EVENT__HAVE_ISSETUGID + +/* Define to 1 if you have the `kqueue' function. */ +#cmakedefine EVENT__HAVE_KQUEUE + +/* Define if the system has zlib */ +#cmakedefine EVENT__HAVE_LIBZ + +/* Define to 1 if you have the `mach_absolute_time' function. */ +#cmakedefine EVENT__HAVE_MACH_ABSOLUTE_TIME + +/* Define to 1 if you have the <mach/mach_time.h> header file. */ +#cmakedefine EVENT__HAVE_MACH_MACH_TIME_H + +/* Define to 1 if you have the <memory.h> header file. */ +#cmakedefine EVENT__HAVE_MEMORY_H + +/* Define to 1 if you have the `mmap' function. */ +#cmakedefine EVENT__HAVE_MMAP + +/* Define to 1 if you have the `nanosleep' function. */ +#cmakedefine EVENT__HAVE_NANOSLEEP + +/* Define to 1 if you have the `usleep' function. */ +#cmakedefine EVENT__HAVE_USLEEP + +/* Define to 1 if you have the <netdb.h> header file. */ +#cmakedefine EVENT__HAVE_NETDB_H + +/* Define to 1 if you have the <netinet/in6.h> header file. */ +#cmakedefine EVENT__HAVE_NETINET_IN6_H + +/* Define to 1 if you have the <netinet/in.h> header file. */ +#cmakedefine EVENT__HAVE_NETINET_IN_H + +/* Define to 1 if you have the <netinet/tcp.h> header file. */ +#cmakedefine EVENT__HAVE_NETINET_TCP_H + +/* Define if the system has openssl */ +#cmakedefine EVENT__HAVE_OPENSSL + +/* Defines if the system has zlib */ +#cmakedefine EVENT__HAVE_ZLIB + +/* Define to 1 if you have the `pipe' function. */ +#cmakedefine EVENT__HAVE_PIPE + +/* Define to 1 if you have the `pipe2' function. */ +#cmakedefine EVENT__HAVE_PIPE2 + +/* Define to 1 if you have the `poll' function. */ +#cmakedefine EVENT__HAVE_POLL + +/* Define to 1 if you have the <poll.h> header file. */ +#cmakedefine EVENT__HAVE_POLL_H + +/* Define to 1 if you have the `port_create' function. */ +#cmakedefine EVENT__HAVE_PORT_CREATE + +/* Define to 1 if you have the <port.h> header file. */ +#cmakedefine EVENT__HAVE_PORT_H + +/* Define if you have POSIX threads libraries and header files. */ +#cmakedefine EVENT__HAVE_PTHREAD + +/* Define if we have pthreads on this system */ +#cmakedefine EVENT__HAVE_PTHREADS + +/* Define to 1 if you have the `putenv' function. */ +#cmakedefine EVENT__HAVE_PUTENV + +/* Define to 1 if the system has the type `sa_family_t'. */ +#cmakedefine EVENT__HAVE_SA_FAMILY_T + +/* Define to 1 if you have the `select' function. */ +#cmakedefine EVENT__HAVE_SELECT + +/* Define to 1 if you have the `setenv' function. */ +#cmakedefine EVENT__HAVE_SETENV + +/* Define if F_SETFD is defined in <fcntl.h> */ +#cmakedefine EVENT__HAVE_SETFD + +/* Define to 1 if you have the `setrlimit' function. */ +#cmakedefine EVENT__HAVE_SETRLIMIT + +/* Define to 1 if you have the `sendfile' function. */ +#cmakedefine EVENT__HAVE_SENDFILE + +/* Define if F_SETFD is defined in <fcntl.h> */ +#cmakedefine EVENT__HAVE_SETFD + +/* Define to 1 if you have the `sigaction' function. */ +#cmakedefine EVENT__HAVE_SIGACTION + +/* Define to 1 if you have the `signal' function. */ +#cmakedefine EVENT__HAVE_SIGNAL + +/* Define to 1 if you have the `splice' function. */ +#cmakedefine EVENT__HAVE_SPLICE + +/* Define to 1 if you have the <stdarg.h> header file. */ +#cmakedefine EVENT__HAVE_STDARG_H + +/* Define to 1 if you have the <stddef.h> header file. */ +#cmakedefine EVENT__HAVE_STDDEF_H + +/* Define to 1 if you have the <stdint.h> header file. */ +#cmakedefine EVENT__HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#cmakedefine EVENT__HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#cmakedefine EVENT__HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#cmakedefine EVENT__HAVE_STRING_H + +/* Define to 1 if you have the `strlcpy' function. */ +#cmakedefine EVENT__HAVE_STRLCPY + +/* Define to 1 if you have the `strsep' function. */ +#cmakedefine EVENT__HAVE_STRSEP + +/* Define to 1 if you have the `strtok_r' function. */ +#cmakedefine EVENT__HAVE_STRTOK_R + +/* Define to 1 if you have the `strtoll' function. */ +#cmakedefine EVENT__HAVE_STRTOLL + +/* Define to 1 if the system has the type `struct addrinfo'. */ +#cmakedefine EVENT__HAVE_STRUCT_ADDRINFO + +/* Define to 1 if the system has the type `struct in6_addr'. */ +#cmakedefine EVENT__HAVE_STRUCT_IN6_ADDR + +/* Define to 1 if `s6_addr16' is member of `struct in6_addr'. */ +#cmakedefine EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR16 + +/* Define to 1 if `s6_addr32' is member of `struct in6_addr'. */ +#cmakedefine EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR32 + +/* Define to 1 if the system has the type `struct sockaddr_in6'. */ +#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_IN6 + +/* Define to 1 if `sin6_len' is member of `struct sockaddr_in6'. */ +#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN + +/* Define to 1 if `sin_len' is member of `struct sockaddr_in'. */ +#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + +/* Define to 1 if the system has the type `struct sockaddr_storage'. */ +#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_STORAGE + +/* Define to 1 if `ss_family' is a member of `struct sockaddr_storage'. */ +#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY + +/* Define to 1 if `__ss_family' is a member of `struct sockaddr_storage'. */ +#cmakedefine EVENT__HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY + +/* Define to 1 if you have the `sysctl' function. */ +#cmakedefine EVENT__HAVE_SYSCTL + +/* Define to 1 if you have the <sys/devpoll.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_DEVPOLL_H + +/* Define to 1 if you have the <sys/epoll.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_EPOLL_H + +/* Define to 1 if you have the <sys/eventfd.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_EVENTFD_H + +/* Define to 1 if you have the <sys/event.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_EVENT_H + +/* Define to 1 if you have the <sys/ioctl.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the <sys/mman.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_MMAN_H + +/* Define to 1 if you have the <sys/param.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_PARAM_H + +/* Define to 1 if you have the <sys/queue.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_QUEUE_H + +/* Define to 1 if you have the <sys/resource.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_RESOURCE_H + +/* Define to 1 if you have the <sys/select.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_SELECT_H + +/* Define to 1 if you have the <sys/sendfile.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_SENDFILE_H + +/* Define to 1 if you have the <sys/socket.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/sysctl.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_SYSCTL_H + +/* Define to 1 if you have the <sys/timerfd.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_TIMERFD_H */ + +/* Define to 1 if you have the <sys/time.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_TIME_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <sys/uio.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_UIO_H + +/* Define to 1 if you have the <sys/wait.h> header file. */ +#cmakedefine EVENT__HAVE_SYS_WAIT_H + +/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */ +#cmakedefine EVENT__HAVE_TAILQFOREACH + +/* Define if timeradd is defined in <sys/time.h> */ +#cmakedefine EVENT__HAVE_TIMERADD + +/* Define if timerclear is defined in <sys/time.h> */ +#cmakedefine EVENT__HAVE_TIMERCLEAR + +/* Define if timercmp is defined in <sys/time.h> */ +#cmakedefine EVENT__HAVE_TIMERCMP + +/* Define to 1 if you have the `timerfd_create' function. */ +#cmakedefine EVENT__HAVE_TIMERFD_CREATE + +/* Define if timerisset is defined in <sys/time.h> */ +#cmakedefine EVENT__HAVE_TIMERISSET + +/* Define to 1 if the system has the type `uint8_t'. */ +#cmakedefine EVENT__HAVE_UINT8_T + +/* Define to 1 if the system has the type `uint16_t'. */ +#cmakedefine EVENT__HAVE_UINT16_T + +/* Define to 1 if the system has the type `uint32_t'. */ +#cmakedefine EVENT__HAVE_UINT32_T + +/* Define to 1 if the system has the type `uint64_t'. */ +#cmakedefine EVENT__HAVE_UINT64_T + +/* Define to 1 if the system has the type `uintptr_t'. */ +#cmakedefine EVENT__HAVE_UINTPTR_T + +/* Define to 1 if you have the `umask' function. */ +#cmakedefine EVENT__HAVE_UMASK + +/* Define to 1 if you have the <unistd.h> header file. */ +#cmakedefine EVENT__HAVE_UNISTD_H + +/* Define to 1 if you have the `unsetenv' function. */ +#cmakedefine EVENT__HAVE_UNSETENV + +/* Define to 1 if you have the `vasprintf' function. */ +#cmakedefine EVENT__HAVE_VASPRINTF + +/* Define if kqueue works correctly with pipes */ +#cmakedefine EVENT__HAVE_WORKING_KQUEUE + +#ifdef __USE_UNUSED_DEFINITIONS__ +/* Define to necessary symbol if this constant uses a non-standard name on your system. */ +/* XXX: Hello, this isn't even used, nor is it defined anywhere... - Ellzey */ +#define EVENT__PTHREAD_CREATE_JOINABLE ${EVENT__PTHREAD_CREATE_JOINABLE} +#endif + +/* The size of `pthread_t', as computed by sizeof. */ +#define EVENT__SIZEOF_PTHREAD_T @EVENT__SIZEOF_PTHREAD_T@ + +/* The size of a `int', as computed by sizeof. */ +#define EVENT__SIZEOF_INT @EVENT__SIZEOF_INT@ + +/* The size of a `long', as computed by sizeof. */ +#define EVENT__SIZEOF_LONG @EVENT__SIZEOF_LONG@ + +/* The size of a `long long', as computed by sizeof. */ +#define EVENT__SIZEOF_LONG_LONG @EVENT__SIZEOF_LONG_LONG@ + +/* The size of `off_t', as computed by sizeof. */ +#define EVENT__SIZEOF_OFF_T @EVENT__SIZEOF_OFF_T@ + +#define EVENT__SIZEOF_SSIZE_T @EVENT__SIZEOF_SSIZE_T@ + + +/* The size of a `short', as computed by sizeof. */ +#define EVENT__SIZEOF_SHORT @EVENT__SIZEOF_SHORT@ + +/* The size of `size_t', as computed by sizeof. */ +#define EVENT__SIZEOF_SIZE_T @EVENT__SIZEOF_SIZE_T@ + +/* Define to 1 if you have the ANSI C header files. */ +#cmakedefine EVENT__STDC_HEADERS + +/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ +#cmakedefine EVENT__TIME_WITH_SYS_TIME + +/* The size of `socklen_t', as computed by sizeof. */ +#define EVENT__SIZEOF_SOCKLEN_T @EVENT__SIZEOF_SOCKLEN_T@ + +/* The size of 'void *', as computer by sizeof */ +#define EVENT__SIZEOF_VOID_P @EVENT__SIZEOF_VOID_P@ + +/* set an alias for whatever __func__ __FUNCTION__ is, what sillyness */ +#if defined (__func__) +#define EVENT____func__ __func__ +#elif defined(__FUNCTION__) +#define EVENT____func__ __FUNCTION__ +#else +#define EVENT____func__ __FILE__ +#endif + + +#ifdef __THESE_ARE_NOT_CONFIG_H_THINGS_THEY_ARE_DASH_D_THINGS__ +/* Number of bits in a file offset, on hosts where this is settable. */ +/* Ellzey is not satisfied */ +#define EVENT___FILE_OFFSET_BITS @EVENT___FILE_OFFSET_BITS@ + +/* Define for large files, on AIX-style hosts. */ +#define @_LARGE_FILES@ +#endif + +#ifdef _WhAT_DOES_THIS_EVEN_DO_ +/* Define to empty if `const' does not conform to ANSI C. */ +/* lolwut? - ellzey */ +#undef EVENT__const +#endif + + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* why not c++? + * + * and are we really expected to use EVENT__inline everywhere, + * shouldn't we just do: + * ifdef EVENT__inline + * define inline EVENT__inline + * + * - Ellzey + */ + +#define EVENT__inline @EVENT__inline@ +#endif + +/* Define to `int' if <sys/tyes.h> does not define. */ +#define EVENT__pid_t @EVENT__pid_t@ + +/* Define to `unsigned' if <sys/types.h> does not define. */ +#define EVENT__size_t @EVENT__size_t@ + +/* Define to unsigned int if you dont have it */ +#define EVENT__socklen_t @EVENT__socklen_t@ + +/* Define to `int' if <sys/types.h> does not define. */ +#define EVENT__ssize_t @EVENT__ssize_t@ + +#cmakedefine EVENT__NEED_DLLIMPORT + +/* Define to 1 if you have ERR_remove_thread_stat(). */ +#cmakedefine EVENT__HAVE_ERR_REMOVE_THREAD_STATE + +#endif diff --git a/libs/libevent/docs/event.3 b/libs/libevent/docs/event.3 new file mode 100644 index 0000000000..655a823efe --- /dev/null +++ b/libs/libevent/docs/event.3 @@ -0,0 +1,624 @@ +.\" $OpenBSD: event.3,v 1.4 2002/07/12 18:50:48 provos Exp $ +.\" +.\" Copyright (c) 2000 Artur Grabowski <art@openbsd.org> +.\" 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. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED ``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. +.\" +.Dd August 8, 2000 +.Dt EVENT 3 +.Os +.Sh NAME +.Nm event_init , +.Nm event_dispatch , +.Nm event_loop , +.Nm event_loopexit , +.Nm event_loopbreak , +.Nm event_set , +.Nm event_base_dispatch , +.Nm event_base_loop , +.Nm event_base_loopexit , +.Nm event_base_loopbreak , +.Nm event_base_set , +.Nm event_base_free , +.Nm event_add , +.Nm event_del , +.Nm event_once , +.Nm event_base_once , +.Nm event_pending , +.Nm event_initialized , +.Nm event_priority_init , +.Nm event_priority_set , +.Nm evtimer_set , +.Nm evtimer_add , +.Nm evtimer_del , +.Nm evtimer_pending , +.Nm evtimer_initialized , +.Nm signal_set , +.Nm signal_add , +.Nm signal_del , +.Nm signal_pending , +.Nm signal_initialized , +.Nm bufferevent_new , +.Nm bufferevent_free , +.Nm bufferevent_write , +.Nm bufferevent_write_buffer , +.Nm bufferevent_read , +.Nm bufferevent_enable , +.Nm bufferevent_disable , +.Nm bufferevent_settimeout , +.Nm bufferevent_base_set , +.Nm evbuffer_new , +.Nm evbuffer_free , +.Nm evbuffer_add , +.Nm evbuffer_add_buffer , +.Nm evbuffer_add_printf , +.Nm evbuffer_add_vprintf , +.Nm evbuffer_drain , +.Nm evbuffer_write , +.Nm evbuffer_read , +.Nm evbuffer_find , +.Nm evbuffer_readline , +.Nm evhttp_new , +.Nm evhttp_bind_socket , +.Nm evhttp_free +.Nd execute a function when a specific event occurs +.Sh SYNOPSIS +.Fd #include <sys/time.h> +.Fd #include <event.h> +.Ft "struct event_base *" +.Fn "event_init" "void" +.Ft int +.Fn "event_dispatch" "void" +.Ft int +.Fn "event_loop" "int flags" +.Ft int +.Fn "event_loopexit" "struct timeval *tv" +.Ft int +.Fn "event_loopbreak" "void" +.Ft void +.Fn "event_set" "struct event *ev" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg" +.Ft int +.Fn "event_base_dispatch" "struct event_base *base" +.Ft int +.Fn "event_base_loop" "struct event_base *base" "int flags" +.Ft int +.Fn "event_base_loopexit" "struct event_base *base" "struct timeval *tv" +.Ft int +.Fn "event_base_loopbreak" "struct event_base *base" +.Ft int +.Fn "event_base_set" "struct event_base *base" "struct event *" +.Ft void +.Fn "event_base_free" "struct event_base *base" +.Ft int +.Fn "event_add" "struct event *ev" "struct timeval *tv" +.Ft int +.Fn "event_del" "struct event *ev" +.Ft int +.Fn "event_once" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg" "struct timeval *tv" +.Ft int +.Fn "event_base_once" "struct event_base *base" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg" "struct timeval *tv" +.Ft int +.Fn "event_pending" "struct event *ev" "short event" "struct timeval *tv" +.Ft int +.Fn "event_initialized" "struct event *ev" +.Ft int +.Fn "event_priority_init" "int npriorities" +.Ft int +.Fn "event_priority_set" "struct event *ev" "int priority" +.Ft void +.Fn "evtimer_set" "struct event *ev" "void (*fn)(int, short, void *)" "void *arg" +.Ft void +.Fn "evtimer_add" "struct event *ev" "struct timeval *" +.Ft void +.Fn "evtimer_del" "struct event *ev" +.Ft int +.Fn "evtimer_pending" "struct event *ev" "struct timeval *tv" +.Ft int +.Fn "evtimer_initialized" "struct event *ev" +.Ft void +.Fn "signal_set" "struct event *ev" "int signal" "void (*fn)(int, short, void *)" "void *arg" +.Ft void +.Fn "signal_add" "struct event *ev" "struct timeval *" +.Ft void +.Fn "signal_del" "struct event *ev" +.Ft int +.Fn "signal_pending" "struct event *ev" "struct timeval *tv" +.Ft int +.Fn "signal_initialized" "struct event *ev" +.Ft "struct bufferevent *" +.Fn "bufferevent_new" "int fd" "evbuffercb readcb" "evbuffercb writecb" "everrorcb" "void *cbarg" +.Ft void +.Fn "bufferevent_free" "struct bufferevent *bufev" +.Ft int +.Fn "bufferevent_write" "struct bufferevent *bufev" "void *data" "size_t size" +.Ft int +.Fn "bufferevent_write_buffer" "struct bufferevent *bufev" "struct evbuffer *buf" +.Ft size_t +.Fn "bufferevent_read" "struct bufferevent *bufev" "void *data" "size_t size" +.Ft int +.Fn "bufferevent_enable" "struct bufferevent *bufev" "short event" +.Ft int +.Fn "bufferevent_disable" "struct bufferevent *bufev" "short event" +.Ft void +.Fn "bufferevent_settimeout" "struct bufferevent *bufev" "int timeout_read" "int timeout_write" +.Ft int +.Fn "bufferevent_base_set" "struct event_base *base" "struct bufferevent *bufev" +.Ft "struct evbuffer *" +.Fn "evbuffer_new" "void" +.Ft void +.Fn "evbuffer_free" "struct evbuffer *buf" +.Ft int +.Fn "evbuffer_add" "struct evbuffer *buf" "const void *data" "size_t size" +.Ft int +.Fn "evbuffer_add_buffer" "struct evbuffer *dst" "struct evbuffer *src" +.Ft int +.Fn "evbuffer_add_printf" "struct evbuffer *buf" "const char *fmt" "..." +.Ft int +.Fn "evbuffer_add_vprintf" "struct evbuffer *buf" "const char *fmt" "va_list ap" +.Ft void +.Fn "evbuffer_drain" "struct evbuffer *buf" "size_t size" +.Ft int +.Fn "evbuffer_write" "struct evbuffer *buf" "int fd" +.Ft int +.Fn "evbuffer_read" "struct evbuffer *buf" "int fd" "int size" +.Ft "unsigned char *" +.Fn "evbuffer_find" "struct evbuffer *buf" "const unsigned char *data" "size_t size" +.Ft "char *" +.Fn "evbuffer_readline" "struct evbuffer *buf" +.Ft "struct evhttp *" +.Fn "evhttp_new" "struct event_base *base" +.Ft int +.Fn "evhttp_bind_socket" "struct evhttp *http" "const char *address" "unsigned short port" +.Ft "void" +.Fn "evhttp_free" "struct evhttp *http" +.Ft int +.Fa (*event_sigcb)(void) ; +.Ft volatile sig_atomic_t +.Fa event_gotsig ; +.Sh DESCRIPTION +The +.Nm event +API provides a mechanism to execute a function when a specific event +on a file descriptor occurs or after a given time has passed. +.Pp +The +.Nm event +API needs to be initialized with +.Fn event_init +before it can be used. +.Pp +In order to process events, an application needs to call +.Fn event_dispatch . +This function only returns on error, and should replace the event core +of the application program. +.Pp +The function +.Fn event_set +prepares the event structure +.Fa ev +to be used in future calls to +.Fn event_add +and +.Fn event_del . +The event will be prepared to call the function specified by the +.Fa fn +argument with an +.Fa int +argument indicating the file descriptor, a +.Fa short +argument indicating the type of event, and a +.Fa void * +argument given in the +.Fa arg +argument. +The +.Fa fd +indicates the file descriptor that should be monitored for events. +The events can be either +.Va EV_READ , +.Va EV_WRITE , +or both, +indicating that an application can read or write from the file descriptor +respectively without blocking. +.Pp +The function +.Fa fn +will be called with the file descriptor that triggered the event and +the type of event which will be either +.Va EV_TIMEOUT , +.Va EV_SIGNAL , +.Va EV_READ , +or +.Va EV_WRITE . +Additionally, an event which has registered interest in more than one of the +preceeding events, via bitwise-OR to +.Fn event_set , +can provide its callback function with a bitwise-OR of more than one triggered +event. +The additional flag +.Va EV_PERSIST +makes an +.Fn event_add +persistent until +.Fn event_del +has been called. +.Pp +Once initialized, the +.Fa ev +structure can be used repeatedly with +.Fn event_add +and +.Fn event_del +and does not need to be reinitialized unless the function called and/or +the argument to it are to be changed. +However, when an +.Fa ev +structure has been added to libevent using +.Fn event_add +the structure must persist until the event occurs (assuming +.Fa EV_PERSIST +is not set) or is removed +using +.Fn event_del . +You may not reuse the same +.Fa ev +structure for multiple monitored descriptors; each descriptor +needs its own +.Fa ev . +.Pp +The function +.Fn event_add +schedules the execution of the +.Fa ev +event when the event specified in +.Fn event_set +occurs or in at least the time specified in the +.Fa tv . +If +.Fa tv +is +.Dv NULL , +no timeout occurs and the function will only be called +if a matching event occurs on the file descriptor. +The event in the +.Fa ev +argument must be already initialized by +.Fn event_set +and may not be used in calls to +.Fn event_set +until it has timed out or been removed with +.Fn event_del . +If the event in the +.Fa ev +argument already has a scheduled timeout, the old timeout will be +replaced by the new one. +.Pp +The function +.Fn event_del +will cancel the event in the argument +.Fa ev . +If the event has already executed or has never been added +the call will have no effect. +.Pp +The functions +.Fn evtimer_set , +.Fn evtimer_add , +.Fn evtimer_del , +.Fn evtimer_initialized , +and +.Fn evtimer_pending +are abbreviations for common situations where only a timeout is required. +The file descriptor passed will be \-1, and the event type will be +.Va EV_TIMEOUT . +.Pp +The functions +.Fn signal_set , +.Fn signal_add , +.Fn signal_del , +.Fn signal_initialized , +and +.Fn signal_pending +are abbreviations. +The event type will be a persistent +.Va EV_SIGNAL . +That means +.Fn signal_set +adds +.Va EV_PERSIST . +.Pp +In order to avoid races in signal handlers, the +.Nm event +API provides two variables: +.Va event_sigcb +and +.Va event_gotsig . +A signal handler +sets +.Va event_gotsig +to indicate that a signal has been received. +The application sets +.Va event_sigcb +to a callback function. +After the signal handler sets +.Va event_gotsig , +.Nm event_dispatch +will execute the callback function to process received signals. +The callback returns 1 when no events are registered any more. +It can return \-1 to indicate an error to the +.Nm event +library, causing +.Fn event_dispatch +to terminate with +.Va errno +set to +.Er EINTR . +.Pp +The function +.Fn event_once +is similar to +.Fn event_set . +However, it schedules a callback to be called exactly once and does not +require the caller to prepare an +.Fa event +structure. +This function supports +.Fa EV_TIMEOUT , +.Fa EV_READ , +and +.Fa EV_WRITE . +.Pp +The +.Fn event_pending +function can be used to check if the event specified by +.Fa event +is pending to run. +If +.Va EV_TIMEOUT +was specified and +.Fa tv +is not +.Dv NULL , +the expiration time of the event will be returned in +.Fa tv . +.Pp +The +.Fn event_initialized +macro can be used to check if an event has been initialized. +.Pp +The +.Nm event_loop +function provides an interface for single pass execution of pending +events. +The flags +.Va EVLOOP_ONCE +and +.Va EVLOOP_NONBLOCK +are recognized. +The +.Nm event_loopexit +function exits from the event loop. The next +.Fn event_loop +iteration after the +given timer expires will complete normally (handling all queued events) then +exit without blocking for events again. Subsequent invocations of +.Fn event_loop +will proceed normally. +The +.Nm event_loopbreak +function exits from the event loop immediately. +.Fn event_loop +will abort after the next event is completed; +.Fn event_loopbreak +is typically invoked from this event's callback. This behavior is analogous +to the "break;" statement. Subsequent invocations of +.Fn event_loop +will proceed normally. +.Pp +It is the responsibility of the caller to provide these functions with +pre-allocated event structures. +.Pp +.Sh EVENT PRIORITIES +By default +.Nm libevent +schedules all active events with the same priority. +However, sometimes it is desirable to process some events with a higher +priority than others. +For that reason, +.Nm libevent +supports strict priority queues. +Active events with a lower priority are always processed before events +with a higher priority. +.Pp +The number of different priorities can be set initially with the +.Fn event_priority_init +function. +This function should be called before the first call to +.Fn event_dispatch . +The +.Fn event_priority_set +function can be used to assign a priority to an event. +By default, +.Nm libevent +assigns the middle priority to all events unless their priority +is explicitly set. +.Sh THREAD SAFE EVENTS +.Nm Libevent +has experimental support for thread-safe events. +When initializing the library via +.Fn event_init , +an event base is returned. +This event base can be used in conjunction with calls to +.Fn event_base_set , +.Fn event_base_dispatch , +.Fn event_base_loop , +.Fn event_base_loopexit , +.Fn bufferevent_base_set +and +.Fn event_base_free . +.Fn event_base_set +should be called after preparing an event with +.Fn event_set , +as +.Fn event_set +assigns the provided event to the most recently created event base. +.Fn bufferevent_base_set +should be called after preparing a bufferevent with +.Fn bufferevent_new . +.Fn event_base_free +should be used to free memory associated with the event base +when it is no longer needed. +.Sh BUFFERED EVENTS +.Nm libevent +provides an abstraction on top of the regular event callbacks. +This abstraction is called a +.Va "buffered event" . +A buffered event provides input and output buffers that get filled +and drained automatically. +The user of a buffered event no longer deals directly with the IO, +but instead is reading from input and writing to output buffers. +.Pp +A new bufferevent is created by +.Fn bufferevent_new . +The parameter +.Fa fd +specifies the file descriptor from which data is read and written to. +This file descriptor is not allowed to be a +.Xr pipe 2 . +The next three parameters are callbacks. +The read and write callback have the following form: +.Ft void +.Fn "(*cb)" "struct bufferevent *bufev" "void *arg" . +The error callback has the following form: +.Ft void +.Fn "(*cb)" "struct bufferevent *bufev" "short what" "void *arg" . +The argument is specified by the fourth parameter +.Fa "cbarg" . +A +.Fa bufferevent struct +pointer is returned on success, NULL on error. +Both the read and the write callback may be NULL. +The error callback has to be always provided. +.Pp +Once initialized, the bufferevent structure can be used repeatedly with +bufferevent_enable() and bufferevent_disable(). +The flags parameter can be a combination of +.Va EV_READ +and +.Va EV_WRITE . +When read enabled the bufferevent will try to read from the file +descriptor and call the read callback. +The write callback is executed +whenever the output buffer is drained below the write low watermark, +which is +.Va 0 +by default. +.Pp +The +.Fn bufferevent_write +function can be used to write data to the file descriptor. +The data is appended to the output buffer and written to the descriptor +automatically as it becomes available for writing. +.Fn bufferevent_write +returns 0 on success or \-1 on failure. +The +.Fn bufferevent_read +function is used to read data from the input buffer, +returning the amount of data read. +.Pp +If multiple bases are in use, bufferevent_base_set() must be called before +enabling the bufferevent for the first time. +.Sh NON-BLOCKING HTTP SUPPORT +.Nm libevent +provides a very thin HTTP layer that can be used both to host an HTTP +server and also to make HTTP requests. +An HTTP server can be created by calling +.Fn evhttp_new . +It can be bound to any port and address with the +.Fn evhttp_bind_socket +function. +When the HTTP server is no longer used, it can be freed via +.Fn evhttp_free . +.Pp +To be notified of HTTP requests, a user needs to register callbacks with the +HTTP server. +This can be done by calling +.Fn evhttp_set_cb . +The second argument is the URI for which a callback is being registered. +The corresponding callback will receive an +.Va struct evhttp_request +object that contains all information about the request. +.Pp +This section does not document all the possible function calls; please +check +.Va event.h +for the public interfaces. +.Sh ADDITIONAL NOTES +It is possible to disable support for +.Va epoll , kqueue , devpoll , poll +or +.Va select +by setting the environment variable +.Va EVENT_NOEPOLL , EVENT_NOKQUEUE , EVENT_NODEVPOLL , EVENT_NOPOLL +or +.Va EVENT_NOSELECT , +respectively. +By setting the environment variable +.Va EVENT_SHOW_METHOD , +.Nm libevent +displays the kernel notification method that it uses. +.Sh RETURN VALUES +Upon successful completion +.Fn event_add +and +.Fn event_del +return 0. +Otherwise, \-1 is returned and the global variable errno is +set to indicate the error. +.Sh SEE ALSO +.Xr kqueue 2 , +.Xr poll 2 , +.Xr select 2 , +.Xr evdns 3 , +.Xr timeout 9 +.Sh HISTORY +The +.Nm event +API manpage is based on the +.Xr timeout 9 +manpage by Artur Grabowski. +The port of +.Nm libevent +to Windows is due to Michael A. Davis. +Support for real-time signals is due to Taral. +.Sh AUTHORS +The +.Nm event +library was written by Niels Provos. +.Sh BUGS +This documentation is neither complete nor authoritative. +If you are in doubt about the usage of this API then +check the source code to find out how it works, write +up the missing piece of documentation and send it to +me for inclusion in this man page. diff --git a/libs/libevent/docs/event_rpcgen.py b/libs/libevent/docs/event_rpcgen.py new file mode 100644 index 0000000000..c537565696 --- /dev/null +++ b/libs/libevent/docs/event_rpcgen.py @@ -0,0 +1,1728 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2005-2007 Niels Provos <provos@citi.umich.edu> +# Copyright (c) 2007-2012 Niels Provos and Nick Mathewson +# All rights reserved. +# +# Generates marshaling code based on libevent. + +# TODO: +# 1) use optparse to allow the strategy shell to parse options, and +# to allow the instantiated factory (for the specific output language) +# to parse remaining options +# 2) move the globals into a class that manages execution (including the +# progress outputs that space stderr at the moment) +# 3) emit other languages + +import sys +import re + +_NAME = "event_rpcgen.py" +_VERSION = "0.1" + +# Globals +line_count = 0 + +white = re.compile(r'\s+') +cppcomment = re.compile(r'\/\/.*$') +nonident = re.compile(r'[^a-zA-Z0-9_]') +structref = re.compile(r'^struct\[([a-zA-Z_][a-zA-Z0-9_]*)\]$') +structdef = re.compile(r'^struct +[a-zA-Z_][a-zA-Z0-9_]* *{$') + +headerdirect = [] +cppdirect = [] + +QUIETLY = 0 + +def declare(s): + if not QUIETLY: + print s + +def TranslateList(mylist, mydict): + return map(lambda x: x % mydict, mylist) + +# Exception class for parse errors +class RpcGenError(Exception): + def __init__(self, why): + self.why = why + def __str__(self): + return str(self.why) + +# Holds everything that makes a struct +class Struct: + def __init__(self, name): + self._name = name + self._entries = [] + self._tags = {} + declare(' Created struct: %s' % name) + + def AddEntry(self, entry): + if self._tags.has_key(entry.Tag()): + raise RpcGenError( + 'Entry "%s" duplicates tag number %d from "%s" ' + 'around line %d' % (entry.Name(), entry.Tag(), + self._tags[entry.Tag()], line_count)) + self._entries.append(entry) + self._tags[entry.Tag()] = entry.Name() + declare(' Added entry: %s' % entry.Name()) + + def Name(self): + return self._name + + def EntryTagName(self, entry): + """Creates the name inside an enumeration for distinguishing data + types.""" + name = "%s_%s" % (self._name, entry.Name()) + return name.upper() + + def PrintIndented(self, file, ident, code): + """Takes an array, add indentation to each entry and prints it.""" + for entry in code: + print >>file, '%s%s' % (ident, entry) + +class StructCCode(Struct): + """ Knows how to generate C code for a struct """ + + def __init__(self, name): + Struct.__init__(self, name) + + def PrintTags(self, file): + """Prints the tag definitions for a structure.""" + print >>file, '/* Tag definition for %s */' % self._name + print >>file, 'enum %s_ {' % self._name.lower() + for entry in self._entries: + print >>file, ' %s=%d,' % (self.EntryTagName(entry), + entry.Tag()) + print >>file, ' %s_MAX_TAGS' % (self._name.upper()) + print >>file, '};\n' + + def PrintForwardDeclaration(self, file): + print >>file, 'struct %s;' % self._name + + def PrintDeclaration(self, file): + print >>file, '/* Structure declaration for %s */' % self._name + print >>file, 'struct %s_access_ {' % self._name + for entry in self._entries: + dcl = entry.AssignDeclaration('(*%s_assign)' % entry.Name()) + dcl.extend( + entry.GetDeclaration('(*%s_get)' % entry.Name())) + if entry.Array(): + dcl.extend( + entry.AddDeclaration('(*%s_add)' % entry.Name())) + self.PrintIndented(file, ' ', dcl) + print >>file, '};\n' + + print >>file, 'struct %s {' % self._name + print >>file, ' struct %s_access_ *base;\n' % self._name + for entry in self._entries: + dcl = entry.Declaration() + self.PrintIndented(file, ' ', dcl) + print >>file, '' + for entry in self._entries: + print >>file, ' ev_uint8_t %s_set;' % entry.Name() + print >>file, '};\n' + + print >>file, \ +"""struct %(name)s *%(name)s_new(void); +struct %(name)s *%(name)s_new_with_arg(void *); +void %(name)s_free(struct %(name)s *); +void %(name)s_clear(struct %(name)s *); +void %(name)s_marshal(struct evbuffer *, const struct %(name)s *); +int %(name)s_unmarshal(struct %(name)s *, struct evbuffer *); +int %(name)s_complete(struct %(name)s *); +void evtag_marshal_%(name)s(struct evbuffer *, ev_uint32_t, + const struct %(name)s *); +int evtag_unmarshal_%(name)s(struct evbuffer *, ev_uint32_t, + struct %(name)s *);""" % { 'name' : self._name } + + + # Write a setting function of every variable + for entry in self._entries: + self.PrintIndented(file, '', entry.AssignDeclaration( + entry.AssignFuncName())) + self.PrintIndented(file, '', entry.GetDeclaration( + entry.GetFuncName())) + if entry.Array(): + self.PrintIndented(file, '', entry.AddDeclaration( + entry.AddFuncName())) + + print >>file, '/* --- %s done --- */\n' % self._name + + def PrintCode(self, file): + print >>file, ('/*\n' + ' * Implementation of %s\n' + ' */\n') % self._name + + print >>file, \ + 'static struct %(name)s_access_ %(name)s_base__ = {' % \ + { 'name' : self._name } + for entry in self._entries: + self.PrintIndented(file, ' ', entry.CodeBase()) + print >>file, '};\n' + + # Creation + print >>file, ( + 'struct %(name)s *\n' + '%(name)s_new(void)\n' + '{\n' + ' return %(name)s_new_with_arg(NULL);\n' + '}\n' + '\n' + 'struct %(name)s *\n' + '%(name)s_new_with_arg(void *unused)\n' + '{\n' + ' struct %(name)s *tmp;\n' + ' if ((tmp = malloc(sizeof(struct %(name)s))) == NULL) {\n' + ' event_warn("%%s: malloc", __func__);\n' + ' return (NULL);\n' + ' }\n' + ' tmp->base = &%(name)s_base__;\n') % { 'name' : self._name } + + for entry in self._entries: + self.PrintIndented(file, ' ', entry.CodeInitialize('tmp')) + print >>file, ' tmp->%s_set = 0;\n' % entry.Name() + + print >>file, ( + ' return (tmp);\n' + '}\n') + + # Adding + for entry in self._entries: + if entry.Array(): + self.PrintIndented(file, '', entry.CodeAdd()) + print >>file, '' + + # Assigning + for entry in self._entries: + self.PrintIndented(file, '', entry.CodeAssign()) + print >>file, '' + + # Getting + for entry in self._entries: + self.PrintIndented(file, '', entry.CodeGet()) + print >>file, '' + + # Clearing + print >>file, ( 'void\n' + '%(name)s_clear(struct %(name)s *tmp)\n' + '{' + ) % { 'name' : self._name } + for entry in self._entries: + self.PrintIndented(file, ' ', entry.CodeClear('tmp')) + + print >>file, '}\n' + + # Freeing + print >>file, ( 'void\n' + '%(name)s_free(struct %(name)s *tmp)\n' + '{' + ) % { 'name' : self._name } + + for entry in self._entries: + self.PrintIndented(file, ' ', entry.CodeFree('tmp')) + + print >>file, (' free(tmp);\n' + '}\n') + + # Marshaling + print >>file, ('void\n' + '%(name)s_marshal(struct evbuffer *evbuf, ' + 'const struct %(name)s *tmp)' + '{') % { 'name' : self._name } + for entry in self._entries: + indent = ' ' + # Optional entries do not have to be set + if entry.Optional(): + indent += ' ' + print >>file, ' if (tmp->%s_set) {' % entry.Name() + self.PrintIndented( + file, indent, + entry.CodeMarshal('evbuf', self.EntryTagName(entry), + entry.GetVarName('tmp'), + entry.GetVarLen('tmp'))) + if entry.Optional(): + print >>file, ' }' + + print >>file, '}\n' + + # Unmarshaling + print >>file, ('int\n' + '%(name)s_unmarshal(struct %(name)s *tmp, ' + ' struct evbuffer *evbuf)\n' + '{\n' + ' ev_uint32_t tag;\n' + ' while (evbuffer_get_length(evbuf) > 0) {\n' + ' if (evtag_peek(evbuf, &tag) == -1)\n' + ' return (-1);\n' + ' switch (tag) {\n' + ) % { 'name' : self._name } + for entry in self._entries: + print >>file, ' case %s:\n' % self.EntryTagName(entry) + if not entry.Array(): + print >>file, ( + ' if (tmp->%s_set)\n' + ' return (-1);' + ) % (entry.Name()) + + self.PrintIndented( + file, ' ', + entry.CodeUnmarshal('evbuf', + self.EntryTagName(entry), + entry.GetVarName('tmp'), + entry.GetVarLen('tmp'))) + + print >>file, ( ' tmp->%s_set = 1;\n' % entry.Name() + + ' break;\n' ) + print >>file, ( ' default:\n' + ' return -1;\n' + ' }\n' + ' }\n' ) + # Check if it was decoded completely + print >>file, ( ' if (%(name)s_complete(tmp) == -1)\n' + ' return (-1);' + ) % { 'name' : self._name } + + # Successfully decoded + print >>file, ( ' return (0);\n' + '}\n') + + # Checking if a structure has all the required data + print >>file, ( + 'int\n' + '%(name)s_complete(struct %(name)s *msg)\n' + '{' ) % { 'name' : self._name } + for entry in self._entries: + if not entry.Optional(): + code = [ + 'if (!msg->%(name)s_set)', + ' return (-1);' ] + code = TranslateList(code, entry.GetTranslation()) + self.PrintIndented( + file, ' ', code) + + self.PrintIndented( + file, ' ', + entry.CodeComplete('msg', entry.GetVarName('msg'))) + print >>file, ( + ' return (0);\n' + '}\n' ) + + # Complete message unmarshaling + print >>file, ( + 'int\n' + 'evtag_unmarshal_%(name)s(struct evbuffer *evbuf, ' + 'ev_uint32_t need_tag, struct %(name)s *msg)\n' + '{\n' + ' ev_uint32_t tag;\n' + ' int res = -1;\n' + '\n' + ' struct evbuffer *tmp = evbuffer_new();\n' + '\n' + ' if (evtag_unmarshal(evbuf, &tag, tmp) == -1' + ' || tag != need_tag)\n' + ' goto error;\n' + '\n' + ' if (%(name)s_unmarshal(msg, tmp) == -1)\n' + ' goto error;\n' + '\n' + ' res = 0;\n' + '\n' + ' error:\n' + ' evbuffer_free(tmp);\n' + ' return (res);\n' + '}\n' ) % { 'name' : self._name } + + # Complete message marshaling + print >>file, ( + 'void\n' + 'evtag_marshal_%(name)s(struct evbuffer *evbuf, ev_uint32_t tag, ' + 'const struct %(name)s *msg)\n' + '{\n' + ' struct evbuffer *buf_ = evbuffer_new();\n' + ' assert(buf_ != NULL);\n' + ' %(name)s_marshal(buf_, msg);\n' + ' evtag_marshal_buffer(evbuf, tag, buf_);\n ' + ' evbuffer_free(buf_);\n' + '}\n' ) % { 'name' : self._name } + +class Entry: + def __init__(self, type, name, tag): + self._type = type + self._name = name + self._tag = int(tag) + self._ctype = type + self._optional = 0 + self._can_be_array = 0 + self._array = 0 + self._line_count = -1 + self._struct = None + self._refname = None + + self._optpointer = True + self._optaddarg = True + + def GetInitializer(self): + assert 0, "Entry does not provide initializer" + + def SetStruct(self, struct): + self._struct = struct + + def LineCount(self): + assert self._line_count != -1 + return self._line_count + + def SetLineCount(self, number): + self._line_count = number + + def Array(self): + return self._array + + def Optional(self): + return self._optional + + def Tag(self): + return self._tag + + def Name(self): + return self._name + + def Type(self): + return self._type + + def MakeArray(self, yes=1): + self._array = yes + + def MakeOptional(self): + self._optional = 1 + + def Verify(self): + if self.Array() and not self._can_be_array: + raise RpcGenError( + 'Entry "%s" cannot be created as an array ' + 'around line %d' % (self._name, self.LineCount())) + if not self._struct: + raise RpcGenError( + 'Entry "%s" does not know which struct it belongs to ' + 'around line %d' % (self._name, self.LineCount())) + if self._optional and self._array: + raise RpcGenError( + 'Entry "%s" has illegal combination of optional and array ' + 'around line %d' % (self._name, self.LineCount())) + + def GetTranslation(self, extradict = {}): + mapping = { + "parent_name" : self._struct.Name(), + "name" : self._name, + "ctype" : self._ctype, + "refname" : self._refname, + "optpointer" : self._optpointer and "*" or "", + "optreference" : self._optpointer and "&" or "", + "optaddarg" : + self._optaddarg and ", const %s value" % self._ctype or "" + } + for (k, v) in extradict.items(): + mapping[k] = v + + return mapping + + def GetVarName(self, var): + return '%(var)s->%(name)s_data' % self.GetTranslation({ 'var' : var }) + + def GetVarLen(self, var): + return 'sizeof(%s)' % self._ctype + + def GetFuncName(self): + return '%s_%s_get' % (self._struct.Name(), self._name) + + def GetDeclaration(self, funcname): + code = [ 'int %s(struct %s *, %s *);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def CodeGet(self): + code = ( + 'int', + '%(parent_name)s_%(name)s_get(struct %(parent_name)s *msg, ' + '%(ctype)s *value)', + '{', + ' if (msg->%(name)s_set != 1)', + ' return (-1);', + ' *value = msg->%(name)s_data;', + ' return (0);', + '}' ) + code = '\n'.join(code) + code = code % self.GetTranslation() + return code.split('\n') + + def AssignFuncName(self): + return '%s_%s_assign' % (self._struct.Name(), self._name) + + def AddFuncName(self): + return '%s_%s_add' % (self._struct.Name(), self._name) + + def AssignDeclaration(self, funcname): + code = [ 'int %s(struct %s *, const %s);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def CodeAssign(self): + code = [ 'int', + '%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg,' + ' const %(ctype)s value)', + '{', + ' msg->%(name)s_set = 1;', + ' msg->%(name)s_data = value;', + ' return (0);', + '}' ] + code = '\n'.join(code) + code = code % self.GetTranslation() + return code.split('\n') + + def CodeClear(self, structname): + code = [ '%s->%s_set = 0;' % (structname, self.Name()) ] + + return code + + def CodeComplete(self, structname, var_name): + return [] + + def CodeFree(self, name): + return [] + + def CodeBase(self): + code = [ + '%(parent_name)s_%(name)s_assign,', + '%(parent_name)s_%(name)s_get,' + ] + if self.Array(): + code.append('%(parent_name)s_%(name)s_add,') + + code = '\n'.join(code) + code = code % self.GetTranslation() + return code.split('\n') + +class EntryBytes(Entry): + def __init__(self, type, name, tag, length): + # Init base class + Entry.__init__(self, type, name, tag) + + self._length = length + self._ctype = 'ev_uint8_t' + + def GetInitializer(self): + return "NULL" + + def GetVarLen(self, var): + return '(%s)' % self._length + + def CodeArrayAdd(self, varname, value): + # XXX: copy here + return [ '%(varname)s = NULL;' % { 'varname' : varname } ] + + def GetDeclaration(self, funcname): + code = [ 'int %s(struct %s *, %s **);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def AssignDeclaration(self, funcname): + code = [ 'int %s(struct %s *, const %s *);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def Declaration(self): + dcl = ['ev_uint8_t %s_data[%s];' % (self._name, self._length)] + + return dcl + + def CodeGet(self): + name = self._name + code = [ 'int', + '%s_%s_get(struct %s *msg, %s **value)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' if (msg->%s_set != 1)' % name, + ' return (-1);', + ' *value = msg->%s_data;' % name, + ' return (0);', + '}' ] + return code + + def CodeAssign(self): + name = self._name + code = [ 'int', + '%s_%s_assign(struct %s *msg, const %s *value)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' msg->%s_set = 1;' % name, + ' memcpy(msg->%s_data, value, %s);' % ( + name, self._length), + ' return (0);', + '}' ] + return code + + def CodeUnmarshal(self, buf, tag_name, var_name, var_len): + code = [ 'if (evtag_unmarshal_fixed(%(buf)s, %(tag)s, ' + '%(var)s, %(varlen)s) == -1) {', + ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', + ' return (-1);', + '}' + ] + return TranslateList(code, + self.GetTranslation({ + 'var' : var_name, + 'varlen' : var_len, + 'buf' : buf, + 'tag' : tag_name })) + + def CodeMarshal(self, buf, tag_name, var_name, var_len): + code = ['evtag_marshal(%s, %s, %s, %s);' % ( + buf, tag_name, var_name, var_len)] + return code + + def CodeClear(self, structname): + code = [ '%s->%s_set = 0;' % (structname, self.Name()), + 'memset(%s->%s_data, 0, sizeof(%s->%s_data));' % ( + structname, self._name, structname, self._name)] + + return code + + def CodeInitialize(self, name): + code = ['memset(%s->%s_data, 0, sizeof(%s->%s_data));' % ( + name, self._name, name, self._name)] + return code + + def Verify(self): + if not self._length: + raise RpcGenError( + 'Entry "%s" needs a length ' + 'around line %d' % (self._name, self.LineCount())) + + Entry.Verify(self) + +class EntryInt(Entry): + def __init__(self, type, name, tag, bits=32): + # Init base class + Entry.__init__(self, type, name, tag) + + self._can_be_array = 1 + if bits == 32: + self._ctype = 'ev_uint32_t' + self._marshal_type = 'int' + if bits == 64: + self._ctype = 'ev_uint64_t' + self._marshal_type = 'int64' + + def GetInitializer(self): + return "0" + + def CodeArrayFree(self, var): + return [] + + def CodeArrayAssign(self, varname, srcvar): + return [ '%(varname)s = %(srcvar)s;' % { 'varname' : varname, + 'srcvar' : srcvar } ] + + def CodeArrayAdd(self, varname, value): + """Returns a new entry of this type.""" + return [ '%(varname)s = %(value)s;' % { 'varname' : varname, + 'value' : value } ] + + def CodeUnmarshal(self, buf, tag_name, var_name, var_len): + code = [ + 'if (evtag_unmarshal_%(ma)s(%(buf)s, %(tag)s, &%(var)s) == -1) {', + ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', + ' return (-1);', + '}' ] + code = '\n'.join(code) % self.GetTranslation({ + 'ma' : self._marshal_type, + 'buf' : buf, + 'tag' : tag_name, + 'var' : var_name }) + return code.split('\n') + + def CodeMarshal(self, buf, tag_name, var_name, var_len): + code = [ + 'evtag_marshal_%s(%s, %s, %s);' % ( + self._marshal_type, buf, tag_name, var_name)] + return code + + def Declaration(self): + dcl = ['%s %s_data;' % (self._ctype, self._name)] + + return dcl + + def CodeInitialize(self, name): + code = ['%s->%s_data = 0;' % (name, self._name)] + return code + +class EntryString(Entry): + def __init__(self, type, name, tag): + # Init base class + Entry.__init__(self, type, name, tag) + + self._can_be_array = 1 + self._ctype = 'char *' + + def GetInitializer(self): + return "NULL" + + def CodeArrayFree(self, varname): + code = [ + 'if (%(var)s != NULL) free(%(var)s);' ] + + return TranslateList(code, { 'var' : varname }) + + def CodeArrayAssign(self, varname, srcvar): + code = [ + 'if (%(var)s != NULL)', + ' free(%(var)s);', + '%(var)s = strdup(%(srcvar)s);', + 'if (%(var)s == NULL) {', + ' event_warnx("%%s: strdup", __func__);', + ' return (-1);', + '}' ] + + return TranslateList(code, { 'var' : varname, + 'srcvar' : srcvar }) + + def CodeArrayAdd(self, varname, value): + code = [ + 'if (%(value)s != NULL) {', + ' %(var)s = strdup(%(value)s);', + ' if (%(var)s == NULL) {', + ' goto error;', + ' }', + '} else {', + ' %(var)s = NULL;', + '}' ] + + return TranslateList(code, { 'var' : varname, + 'value' : value }) + + def GetVarLen(self, var): + return 'strlen(%s)' % self.GetVarName(var) + + def CodeMakeInitalize(self, varname): + return '%(varname)s = NULL;' % { 'varname' : varname } + + def CodeAssign(self): + name = self._name + code = """int +%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, + const %(ctype)s value) +{ + if (msg->%(name)s_data != NULL) + free(msg->%(name)s_data); + if ((msg->%(name)s_data = strdup(value)) == NULL) + return (-1); + msg->%(name)s_set = 1; + return (0); +}""" % self.GetTranslation() + + return code.split('\n') + + def CodeUnmarshal(self, buf, tag_name, var_name, var_len): + code = ['if (evtag_unmarshal_string(%(buf)s, %(tag)s, &%(var)s) == -1) {', + ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', + ' return (-1);', + '}' + ] + code = '\n'.join(code) % self.GetTranslation({ + 'buf' : buf, + 'tag' : tag_name, + 'var' : var_name }) + return code.split('\n') + + def CodeMarshal(self, buf, tag_name, var_name, var_len): + code = ['evtag_marshal_string(%s, %s, %s);' % ( + buf, tag_name, var_name)] + return code + + def CodeClear(self, structname): + code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), + ' free(%s->%s_data);' % (structname, self.Name()), + ' %s->%s_data = NULL;' % (structname, self.Name()), + ' %s->%s_set = 0;' % (structname, self.Name()), + '}' + ] + + return code + + def CodeInitialize(self, name): + code = ['%s->%s_data = NULL;' % (name, self._name)] + return code + + def CodeFree(self, name): + code = ['if (%s->%s_data != NULL)' % (name, self._name), + ' free (%s->%s_data);' % (name, self._name)] + + return code + + def Declaration(self): + dcl = ['char *%s_data;' % self._name] + + return dcl + +class EntryStruct(Entry): + def __init__(self, type, name, tag, refname): + # Init base class + Entry.__init__(self, type, name, tag) + + self._optpointer = False + self._can_be_array = 1 + self._refname = refname + self._ctype = 'struct %s*' % refname + self._optaddarg = False + + def GetInitializer(self): + return "NULL" + + def GetVarLen(self, var): + return '-1' + + def CodeArrayAdd(self, varname, value): + code = [ + '%(varname)s = %(refname)s_new();', + 'if (%(varname)s == NULL)', + ' goto error;' ] + + return TranslateList(code, self.GetTranslation({ 'varname' : varname })) + + def CodeArrayFree(self, var): + code = [ '%(refname)s_free(%(var)s);' % self.GetTranslation( + { 'var' : var }) ] + return code + + def CodeArrayAssign(self, var, srcvar): + code = [ + 'int had_error = 0;', + 'struct evbuffer *tmp = NULL;', + '%(refname)s_clear(%(var)s);', + 'if ((tmp = evbuffer_new()) == NULL) {', + ' event_warn("%%s: evbuffer_new()", __func__);', + ' had_error = 1;', + ' goto done;', + '}', + '%(refname)s_marshal(tmp, %(srcvar)s);', + 'if (%(refname)s_unmarshal(%(var)s, tmp) == -1) {', + ' event_warnx("%%s: %(refname)s_unmarshal", __func__);', + ' had_error = 1;', + ' goto done;', + '}', + 'done:' + 'if (tmp != NULL)', + ' evbuffer_free(tmp);', + 'if (had_error) {', + ' %(refname)s_clear(%(var)s);', + ' return (-1);', + '}' ] + + return TranslateList(code, self.GetTranslation({ + 'var' : var, + 'srcvar' : srcvar})) + + def CodeGet(self): + name = self._name + code = [ 'int', + '%s_%s_get(struct %s *msg, %s *value)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' if (msg->%s_set != 1) {' % name, + ' msg->%s_data = %s_new();' % (name, self._refname), + ' if (msg->%s_data == NULL)' % name, + ' return (-1);', + ' msg->%s_set = 1;' % name, + ' }', + ' *value = msg->%s_data;' % name, + ' return (0);', + '}' ] + return code + + def CodeAssign(self): + name = self._name + code = """int +%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, + const %(ctype)s value) +{ + struct evbuffer *tmp = NULL; + if (msg->%(name)s_set) { + %(refname)s_clear(msg->%(name)s_data); + msg->%(name)s_set = 0; + } else { + msg->%(name)s_data = %(refname)s_new(); + if (msg->%(name)s_data == NULL) { + event_warn("%%s: %(refname)s_new()", __func__); + goto error; + } + } + if ((tmp = evbuffer_new()) == NULL) { + event_warn("%%s: evbuffer_new()", __func__); + goto error; + } + %(refname)s_marshal(tmp, value); + if (%(refname)s_unmarshal(msg->%(name)s_data, tmp) == -1) { + event_warnx("%%s: %(refname)s_unmarshal", __func__); + goto error; + } + msg->%(name)s_set = 1; + evbuffer_free(tmp); + return (0); + error: + if (tmp != NULL) + evbuffer_free(tmp); + if (msg->%(name)s_data != NULL) { + %(refname)s_free(msg->%(name)s_data); + msg->%(name)s_data = NULL; + } + return (-1); +}""" % self.GetTranslation() + return code.split('\n') + + def CodeComplete(self, structname, var_name): + code = [ 'if (%(structname)s->%(name)s_set && ' + '%(refname)s_complete(%(var)s) == -1)', + ' return (-1);' ] + + return TranslateList(code, self.GetTranslation({ + 'structname' : structname, + 'var' : var_name })) + + def CodeUnmarshal(self, buf, tag_name, var_name, var_len): + code = ['%(var)s = %(refname)s_new();', + 'if (%(var)s == NULL)', + ' return (-1);', + 'if (evtag_unmarshal_%(refname)s(%(buf)s, %(tag)s, ' + '%(var)s) == -1) {', + ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', + ' return (-1);', + '}' + ] + code = '\n'.join(code) % self.GetTranslation({ + 'buf' : buf, + 'tag' : tag_name, + 'var' : var_name }) + return code.split('\n') + + def CodeMarshal(self, buf, tag_name, var_name, var_len): + code = ['evtag_marshal_%s(%s, %s, %s);' % ( + self._refname, buf, tag_name, var_name)] + return code + + def CodeClear(self, structname): + code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), + ' %s_free(%s->%s_data);' % ( + self._refname, structname, self.Name()), + ' %s->%s_data = NULL;' % (structname, self.Name()), + ' %s->%s_set = 0;' % (structname, self.Name()), + '}' + ] + + return code + + def CodeInitialize(self, name): + code = ['%s->%s_data = NULL;' % (name, self._name)] + return code + + def CodeFree(self, name): + code = ['if (%s->%s_data != NULL)' % (name, self._name), + ' %s_free(%s->%s_data);' % ( + self._refname, name, self._name)] + + return code + + def Declaration(self): + dcl = ['%s %s_data;' % (self._ctype, self._name)] + + return dcl + +class EntryVarBytes(Entry): + def __init__(self, type, name, tag): + # Init base class + Entry.__init__(self, type, name, tag) + + self._ctype = 'ev_uint8_t *' + + def GetInitializer(self): + return "NULL" + + def GetVarLen(self, var): + return '%(var)s->%(name)s_length' % self.GetTranslation({ 'var' : var }) + + def CodeArrayAdd(self, varname, value): + # xxx: copy + return [ '%(varname)s = NULL;' % { 'varname' : varname } ] + + def GetDeclaration(self, funcname): + code = [ 'int %s(struct %s *, %s *, ev_uint32_t *);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def AssignDeclaration(self, funcname): + code = [ 'int %s(struct %s *, const %s, ev_uint32_t);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def CodeAssign(self): + name = self._name + code = [ 'int', + '%s_%s_assign(struct %s *msg, ' + 'const %s value, ev_uint32_t len)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' if (msg->%s_data != NULL)' % name, + ' free (msg->%s_data);' % name, + ' msg->%s_data = malloc(len);' % name, + ' if (msg->%s_data == NULL)' % name, + ' return (-1);', + ' msg->%s_set = 1;' % name, + ' msg->%s_length = len;' % name, + ' memcpy(msg->%s_data, value, len);' % name, + ' return (0);', + '}' ] + return code + + def CodeGet(self): + name = self._name + code = [ 'int', + '%s_%s_get(struct %s *msg, %s *value, ev_uint32_t *plen)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' if (msg->%s_set != 1)' % name, + ' return (-1);', + ' *value = msg->%s_data;' % name, + ' *plen = msg->%s_length;' % name, + ' return (0);', + '}' ] + return code + + def CodeUnmarshal(self, buf, tag_name, var_name, var_len): + code = ['if (evtag_payload_length(%(buf)s, &%(varlen)s) == -1)', + ' return (-1);', + # We do not want DoS opportunities + 'if (%(varlen)s > evbuffer_get_length(%(buf)s))', + ' return (-1);', + 'if ((%(var)s = malloc(%(varlen)s)) == NULL)', + ' return (-1);', + 'if (evtag_unmarshal_fixed(%(buf)s, %(tag)s, %(var)s, ' + '%(varlen)s) == -1) {', + ' event_warnx("%%s: failed to unmarshal %(name)s", __func__);', + ' return (-1);', + '}' + ] + code = '\n'.join(code) % self.GetTranslation({ + 'buf' : buf, + 'tag' : tag_name, + 'var' : var_name, + 'varlen' : var_len }) + return code.split('\n') + + def CodeMarshal(self, buf, tag_name, var_name, var_len): + code = ['evtag_marshal(%s, %s, %s, %s);' % ( + buf, tag_name, var_name, var_len)] + return code + + def CodeClear(self, structname): + code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), + ' free (%s->%s_data);' % (structname, self.Name()), + ' %s->%s_data = NULL;' % (structname, self.Name()), + ' %s->%s_length = 0;' % (structname, self.Name()), + ' %s->%s_set = 0;' % (structname, self.Name()), + '}' + ] + + return code + + def CodeInitialize(self, name): + code = ['%s->%s_data = NULL;' % (name, self._name), + '%s->%s_length = 0;' % (name, self._name) ] + return code + + def CodeFree(self, name): + code = ['if (%s->%s_data != NULL)' % (name, self._name), + ' free(%s->%s_data);' % (name, self._name)] + + return code + + def Declaration(self): + dcl = ['ev_uint8_t *%s_data;' % self._name, + 'ev_uint32_t %s_length;' % self._name] + + return dcl + +class EntryArray(Entry): + def __init__(self, entry): + # Init base class + Entry.__init__(self, entry._type, entry._name, entry._tag) + + self._entry = entry + self._refname = entry._refname + self._ctype = self._entry._ctype + self._optional = True + self._optpointer = self._entry._optpointer + self._optaddarg = self._entry._optaddarg + + # provide a new function for accessing the variable name + def GetVarName(var_name): + return '%(var)s->%(name)s_data[%(index)s]' % \ + self._entry.GetTranslation({'var' : var_name, + 'index' : self._index}) + self._entry.GetVarName = GetVarName + + def GetInitializer(self): + return "NULL" + + def GetVarName(self, var_name): + return var_name + + def GetVarLen(self, var_name): + return '-1' + + def GetDeclaration(self, funcname): + """Allows direct access to elements of the array.""" + code = [ + 'int %(funcname)s(struct %(parent_name)s *, int, %(ctype)s *);' % + self.GetTranslation({ 'funcname' : funcname }) ] + return code + + def AssignDeclaration(self, funcname): + code = [ 'int %s(struct %s *, int, const %s);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def AddDeclaration(self, funcname): + code = [ + '%(ctype)s %(optpointer)s ' + '%(funcname)s(struct %(parent_name)s *msg%(optaddarg)s);' % \ + self.GetTranslation({ 'funcname' : funcname }) ] + return code + + def CodeGet(self): + code = """int +%(parent_name)s_%(name)s_get(struct %(parent_name)s *msg, int offset, + %(ctype)s *value) +{ + if (!msg->%(name)s_set || offset < 0 || offset >= msg->%(name)s_length) + return (-1); + *value = msg->%(name)s_data[offset]; + return (0); +}""" % self.GetTranslation() + + return code.split('\n') + + def CodeAssign(self): + code = [ + 'int', + '%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, int off,', + ' const %(ctype)s value)', + '{', + ' if (!msg->%(name)s_set || off < 0 || off >= msg->%(name)s_length)', + ' return (-1);\n', + ' {' ] + code = TranslateList(code, self.GetTranslation()) + + codearrayassign = self._entry.CodeArrayAssign( + 'msg->%(name)s_data[off]' % self.GetTranslation(), 'value') + code += map(lambda x: ' ' + x, codearrayassign) + + code += TranslateList([ + ' }', + ' return (0);', + '}' ], self.GetTranslation()) + + return code + + def CodeAdd(self): + codearrayadd = self._entry.CodeArrayAdd( + 'msg->%(name)s_data[msg->%(name)s_length - 1]' % self.GetTranslation(), + 'value') + code = [ + 'static int', + '%(parent_name)s_%(name)s_expand_to_hold_more(' + 'struct %(parent_name)s *msg)', + '{', + ' int tobe_allocated = msg->%(name)s_num_allocated;', + ' %(ctype)s* new_data = NULL;', + ' tobe_allocated = !tobe_allocated ? 1 : tobe_allocated << 1;', + ' new_data = (%(ctype)s*) realloc(msg->%(name)s_data,', + ' tobe_allocated * sizeof(%(ctype)s));', + ' if (new_data == NULL)', + ' return -1;', + ' msg->%(name)s_data = new_data;', + ' msg->%(name)s_num_allocated = tobe_allocated;', + ' return 0;' + '}', + '', + '%(ctype)s %(optpointer)s', + '%(parent_name)s_%(name)s_add(' + 'struct %(parent_name)s *msg%(optaddarg)s)', + '{', + ' if (++msg->%(name)s_length >= msg->%(name)s_num_allocated) {', + ' if (%(parent_name)s_%(name)s_expand_to_hold_more(msg)<0)', + ' goto error;', + ' }' ] + + code = TranslateList(code, self.GetTranslation()) + + code += map(lambda x: ' ' + x, codearrayadd) + + code += TranslateList([ + ' msg->%(name)s_set = 1;', + ' return %(optreference)s(msg->%(name)s_data[' + 'msg->%(name)s_length - 1]);', + 'error:', + ' --msg->%(name)s_length;', + ' return (NULL);', + '}' ], self.GetTranslation()) + + return code + + def CodeComplete(self, structname, var_name): + self._index = 'i' + tmp = self._entry.CodeComplete(structname, self._entry.GetVarName(var_name)) + # skip the whole loop if there is nothing to check + if not tmp: + return [] + + translate = self.GetTranslation({ 'structname' : structname }) + code = [ + '{', + ' int i;', + ' for (i = 0; i < %(structname)s->%(name)s_length; ++i) {' ] + + code = TranslateList(code, translate) + + code += map(lambda x: ' ' + x, tmp) + + code += [ + ' }', + '}' ] + + return code + + def CodeUnmarshal(self, buf, tag_name, var_name, var_len): + translate = self.GetTranslation({ 'var' : var_name, + 'buf' : buf, + 'tag' : tag_name, + 'init' : self._entry.GetInitializer()}) + code = [ + 'if (%(var)s->%(name)s_length >= %(var)s->%(name)s_num_allocated &&', + ' %(parent_name)s_%(name)s_expand_to_hold_more(%(var)s) < 0) {', + ' puts("HEY NOW");', + ' return (-1);', + '}'] + + # the unmarshal code directly returns + code = TranslateList(code, translate) + + self._index = '%(var)s->%(name)s_length' % translate + code += self._entry.CodeUnmarshal(buf, tag_name, + self._entry.GetVarName(var_name), + self._entry.GetVarLen(var_name)) + + code += [ '++%(var)s->%(name)s_length;' % translate ] + + return code + + def CodeMarshal(self, buf, tag_name, var_name, var_len): + code = ['{', + ' int i;', + ' for (i = 0; i < %(var)s->%(name)s_length; ++i) {' ] + + self._index = 'i' + code += self._entry.CodeMarshal(buf, tag_name, + self._entry.GetVarName(var_name), + self._entry.GetVarLen(var_name)) + code += [' }', + '}' + ] + + code = "\n".join(code) % self.GetTranslation({ 'var' : var_name }) + + return code.split('\n') + + def CodeClear(self, structname): + translate = self.GetTranslation({ 'structname' : structname }) + codearrayfree = self._entry.CodeArrayFree( + '%(structname)s->%(name)s_data[i]' % self.GetTranslation( + { 'structname' : structname } )) + + code = [ 'if (%(structname)s->%(name)s_set == 1) {' ] + + if codearrayfree: + code += [ + ' int i;', + ' for (i = 0; i < %(structname)s->%(name)s_length; ++i) {' ] + + code = TranslateList(code, translate) + + if codearrayfree: + code += map(lambda x: ' ' + x, codearrayfree) + code += [ + ' }' ] + + code += TranslateList([ + ' free(%(structname)s->%(name)s_data);', + ' %(structname)s->%(name)s_data = NULL;', + ' %(structname)s->%(name)s_set = 0;', + ' %(structname)s->%(name)s_length = 0;', + ' %(structname)s->%(name)s_num_allocated = 0;', + '}' + ], translate) + + return code + + def CodeInitialize(self, name): + code = ['%s->%s_data = NULL;' % (name, self._name), + '%s->%s_length = 0;' % (name, self._name), + '%s->%s_num_allocated = 0;' % (name, self._name)] + return code + + def CodeFree(self, structname): + code = self.CodeClear(structname); + + code += TranslateList([ + 'free(%(structname)s->%(name)s_data);' ], + self.GetTranslation({'structname' : structname })) + + return code + + def Declaration(self): + dcl = ['%s *%s_data;' % (self._ctype, self._name), + 'int %s_length;' % self._name, + 'int %s_num_allocated;' % self._name ] + + return dcl + +def NormalizeLine(line): + global white + global cppcomment + + line = cppcomment.sub('', line) + line = line.strip() + line = white.sub(' ', line) + + return line + +def ProcessOneEntry(factory, newstruct, entry): + optional = 0 + array = 0 + entry_type = '' + name = '' + tag = '' + tag_set = None + separator = '' + fixed_length = '' + + tokens = entry.split(' ') + while tokens: + token = tokens[0] + tokens = tokens[1:] + + if not entry_type: + if not optional and token == 'optional': + optional = 1 + continue + + if not array and token == 'array': + array = 1 + continue + + if not entry_type: + entry_type = token + continue + + if not name: + res = re.match(r'^([^\[\]]+)(\[.*\])?$', token) + if not res: + raise RpcGenError( + 'Cannot parse name: \"%s\" ' + 'around line %d' % (entry, line_count)) + name = res.group(1) + fixed_length = res.group(2) + if fixed_length: + fixed_length = fixed_length[1:-1] + continue + + if not separator: + separator = token + if separator != '=': + raise RpcGenError('Expected "=" after name \"%s\" got %s' + % (name, token)) + continue + + if not tag_set: + tag_set = 1 + if not re.match(r'^(0x)?[0-9]+$', token): + raise RpcGenError('Expected tag number: \"%s\"' % entry) + tag = int(token, 0) + continue + + raise RpcGenError('Cannot parse \"%s\"' % entry) + + if not tag_set: + raise RpcGenError('Need tag number: \"%s\"' % entry) + + # Create the right entry + if entry_type == 'bytes': + if fixed_length: + newentry = factory.EntryBytes(entry_type, name, tag, fixed_length) + else: + newentry = factory.EntryVarBytes(entry_type, name, tag) + elif entry_type == 'int' and not fixed_length: + newentry = factory.EntryInt(entry_type, name, tag) + elif entry_type == 'int64' and not fixed_length: + newentry = factory.EntryInt(entry_type, name, tag, bits=64) + elif entry_type == 'string' and not fixed_length: + newentry = factory.EntryString(entry_type, name, tag) + else: + res = structref.match(entry_type) + if res: + # References another struct defined in our file + newentry = factory.EntryStruct(entry_type, name, tag, res.group(1)) + else: + raise RpcGenError('Bad type: "%s" in "%s"' % (entry_type, entry)) + + structs = [] + + if optional: + newentry.MakeOptional() + if array: + newentry.MakeArray() + + newentry.SetStruct(newstruct) + newentry.SetLineCount(line_count) + newentry.Verify() + + if array: + # We need to encapsulate this entry into a struct + newname = newentry.Name()+ '_array' + + # Now borgify the new entry. + newentry = factory.EntryArray(newentry) + newentry.SetStruct(newstruct) + newentry.SetLineCount(line_count) + newentry.MakeArray() + + newstruct.AddEntry(newentry) + + return structs + +def ProcessStruct(factory, data): + tokens = data.split(' ') + + # First three tokens are: 'struct' 'name' '{' + newstruct = factory.Struct(tokens[1]) + + inside = ' '.join(tokens[3:-1]) + + tokens = inside.split(';') + + structs = [] + + for entry in tokens: + entry = NormalizeLine(entry) + if not entry: + continue + + # It's possible that new structs get defined in here + structs.extend(ProcessOneEntry(factory, newstruct, entry)) + + structs.append(newstruct) + return structs + +def GetNextStruct(file): + global line_count + global cppdirect + + got_struct = 0 + + processed_lines = [] + + have_c_comment = 0 + data = '' + while 1: + line = file.readline() + if not line: + break + + line_count += 1 + line = line[:-1] + + if not have_c_comment and re.search(r'/\*', line): + if re.search(r'/\*.*?\*/', line): + line = re.sub(r'/\*.*?\*/', '', line) + else: + line = re.sub(r'/\*.*$', '', line) + have_c_comment = 1 + + if have_c_comment: + if not re.search(r'\*/', line): + continue + have_c_comment = 0 + line = re.sub(r'^.*\*/', '', line) + + line = NormalizeLine(line) + + if not line: + continue + + if not got_struct: + if re.match(r'#include ["<].*[>"]', line): + cppdirect.append(line) + continue + + if re.match(r'^#(if( |def)|endif)', line): + cppdirect.append(line) + continue + + if re.match(r'^#define', line): + headerdirect.append(line) + continue + + if not structdef.match(line): + raise RpcGenError('Missing struct on line %d: %s' + % (line_count, line)) + else: + got_struct = 1 + data += line + continue + + # We are inside the struct + tokens = line.split('}') + if len(tokens) == 1: + data += ' ' + line + continue + + if len(tokens[1]): + raise RpcGenError('Trailing garbage after struct on line %d' + % line_count) + + # We found the end of the struct + data += ' %s}' % tokens[0] + break + + # Remove any comments, that might be in there + data = re.sub(r'/\*.*\*/', '', data) + + return data + + +def Parse(factory, file): + """ + Parses the input file and returns C code and corresponding header file. + """ + + entities = [] + + while 1: + # Just gets the whole struct nicely formatted + data = GetNextStruct(file) + + if not data: + break + + entities.extend(ProcessStruct(factory, data)) + + return entities + +class CCodeGenerator: + def __init__(self): + pass + + def GuardName(self, name): + # Use the complete provided path to the input file, with all + # non-identifier characters replaced with underscores, to + # reduce the chance of a collision between guard macros. + return 'EVENT_RPCOUT_' + nonident.sub('_', name).upper() + '_' + + def HeaderPreamble(self, name): + guard = self.GuardName(name) + pre = ( + '/*\n' + ' * Automatically generated from %s\n' + ' */\n\n' + '#ifndef %s\n' + '#define %s\n\n' ) % ( + name, guard, guard) + + for statement in headerdirect: + pre += '%s\n' % statement + if headerdirect: + pre += '\n' + + pre += ( + '#include <event2/util.h> /* for ev_uint*_t */\n' + '#include <event2/rpc.h>\n' + ) + + return pre + + def HeaderPostamble(self, name): + guard = self.GuardName(name) + return '#endif /* %s */' % guard + + def BodyPreamble(self, name, header_file): + global _NAME + global _VERSION + + slash = header_file.rfind('/') + if slash != -1: + header_file = header_file[slash+1:] + + pre = ( '/*\n' + ' * Automatically generated from %s\n' + ' * by %s/%s. DO NOT EDIT THIS FILE.\n' + ' */\n\n' ) % (name, _NAME, _VERSION) + pre += ( '#include <stdlib.h>\n' + '#include <string.h>\n' + '#include <assert.h>\n' + '#include <event2/event-config.h>\n' + '#include <event2/event.h>\n' + '#include <event2/buffer.h>\n' + '#include <event2/tag.h>\n\n' + '#ifdef EVENT____func__\n' + '#define __func__ EVENT____func__\n' + '#endif\n\n' + ) + + for statement in cppdirect: + pre += '%s\n' % statement + + pre += '\n#include "%s"\n\n' % header_file + + pre += 'void event_warn(const char *fmt, ...);\n' + pre += 'void event_warnx(const char *fmt, ...);\n\n' + + return pre + + def HeaderFilename(self, filename): + return '.'.join(filename.split('.')[:-1]) + '.h' + + def CodeFilename(self, filename): + return '.'.join(filename.split('.')[:-1]) + '.gen.c' + + def Struct(self, name): + return StructCCode(name) + + def EntryBytes(self, entry_type, name, tag, fixed_length): + return EntryBytes(entry_type, name, tag, fixed_length) + + def EntryVarBytes(self, entry_type, name, tag): + return EntryVarBytes(entry_type, name, tag) + + def EntryInt(self, entry_type, name, tag, bits=32): + return EntryInt(entry_type, name, tag, bits) + + def EntryString(self, entry_type, name, tag): + return EntryString(entry_type, name, tag) + + def EntryStruct(self, entry_type, name, tag, struct_name): + return EntryStruct(entry_type, name, tag, struct_name) + + def EntryArray(self, entry): + return EntryArray(entry) + +class Usage(RpcGenError): + def __init__(self, argv0): + RpcGenError.__init__("usage: %s input.rpc [[output.h] output.c]" + % argv0) + +class CommandLine: + def __init__(self, argv): + """Initialize a command-line to launch event_rpcgen, as if + from a command-line with CommandLine(sys.argv). If you're + calling this directly, remember to provide a dummy value + for sys.argv[0] + """ + self.filename = None + self.header_file = None + self.impl_file = None + self.factory = CCodeGenerator() + + if len(argv) >= 2 and argv[1] == '--quiet': + global QUIETLY + QUIETLY = 1 + del argv[1] + + if len(argv) < 2 or len(argv) > 4: + raise Usage(argv[0]) + + self.filename = argv[1].replace('\\', '/') + if len(argv) == 3: + self.impl_file = argv[2].replace('\\', '/') + if len(argv) == 4: + self.header_file = argv[2].replace('\\', '/') + self.impl_file = argv[3].replace('\\', '/') + + if not self.filename: + raise Usage(argv[0]) + + if not self.impl_file: + self.impl_file = self.factory.CodeFilename(self.filename) + + if not self.header_file: + self.header_file = self.factory.HeaderFilename(self.impl_file) + + if not self.impl_file.endswith('.c'): + raise RpcGenError("can only generate C implementation files") + if not self.header_file.endswith('.h'): + raise RpcGenError("can only generate C header files") + + def run(self): + filename = self.filename + header_file = self.header_file + impl_file = self.impl_file + factory = self.factory + + declare('Reading \"%s\"' % filename) + + fp = open(filename, 'r') + entities = Parse(factory, fp) + fp.close() + + declare('... creating "%s"' % header_file) + header_fp = open(header_file, 'w') + print >>header_fp, factory.HeaderPreamble(filename) + + # Create forward declarations: allows other structs to reference + # each other + for entry in entities: + entry.PrintForwardDeclaration(header_fp) + print >>header_fp, '' + + for entry in entities: + entry.PrintTags(header_fp) + entry.PrintDeclaration(header_fp) + print >>header_fp, factory.HeaderPostamble(filename) + header_fp.close() + + declare('... creating "%s"' % impl_file) + impl_fp = open(impl_file, 'w') + print >>impl_fp, factory.BodyPreamble(filename, header_file) + for entry in entities: + entry.PrintCode(impl_fp) + impl_fp.close() + +if __name__ == '__main__': + try: + CommandLine(sys.argv).run() + sys.exit(0) + + except RpcGenError, e: + print >>sys.stderr, e + sys.exit(1) + + except EnvironmentError, e: + if e.filename and e.strerror: + print >>sys.stderr, "%s: %s" % (e.filename, e.strerror) + sys.exit(1) + elif e.strerror: + print >> sys.stderr, e.strerror + sys.exit(1) + else: + raise diff --git a/libs/libevent/docs/evport.c b/libs/libevent/docs/evport.c new file mode 100644 index 0000000000..a014386bfe --- /dev/null +++ b/libs/libevent/docs/evport.c @@ -0,0 +1,451 @@ +/* + * Submitted by David Pacheco (dp.spambait@gmail.com) + * + * Copyright 2006-2007 Niels Provos + * Copyright 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY SUN MICROSYSTEMS, INC. ``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 SUN MICROSYSTEMS, INC. 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. + */ + +/* + * Copyright (c) 2007 Sun Microsystems. All rights reserved. + * Use is subject to license terms. + */ + +/* + * evport.c: event backend using Solaris 10 event ports. See port_create(3C). + * This implementation is loosely modeled after the one used for select(2) (in + * select.c). + * + * The outstanding events are tracked in a data structure called evport_data. + * Each entry in the ed_fds array corresponds to a file descriptor, and contains + * pointers to the read and write events that correspond to that fd. (That is, + * when the file is readable, the "read" event should handle it, etc.) + * + * evport_add and evport_del update this data structure. evport_dispatch uses it + * to determine where to callback when an event occurs (which it gets from + * port_getn). + * + * Helper functions are used: grow() grows the file descriptor array as + * necessary when large fd's come in. reassociate() takes care of maintaining + * the proper file-descriptor/event-port associations. + * + * As in the select(2) implementation, signals are handled by evsignal. + */ + +#include "event2/event-config.h" +#include "evconfig-private.h" + +#ifdef EVENT__HAVE_EVENT_PORTS + +#include <sys/time.h> +#include <sys/queue.h> +#include <errno.h> +#include <poll.h> +#include <port.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "event2/thread.h" + +#include "evthread-internal.h" +#include "event-internal.h" +#include "log-internal.h" +#include "evsignal-internal.h" +#include "evmap-internal.h" + +#define INITIAL_EVENTS_PER_GETN 8 +#define MAX_EVENTS_PER_GETN 4096 + +/* + * Per-file-descriptor information about what events we're subscribed to. These + * fields are NULL if no event is subscribed to either of them. + */ + +struct fd_info { + /* combinations of EV_READ and EV_WRITE */ + short fdi_what; + /* Index of this fd within ed_pending, plus 1. Zero if this fd is + * not in ed_pending. (The +1 is a hack so that memset(0) will set + * it to a nil index. */ + int pending_idx_plus_1; +}; + +#define FDI_HAS_READ(fdi) ((fdi)->fdi_what & EV_READ) +#define FDI_HAS_WRITE(fdi) ((fdi)->fdi_what & EV_WRITE) +#define FDI_HAS_EVENTS(fdi) (FDI_HAS_READ(fdi) || FDI_HAS_WRITE(fdi)) +#define FDI_TO_SYSEVENTS(fdi) (FDI_HAS_READ(fdi) ? POLLIN : 0) | \ + (FDI_HAS_WRITE(fdi) ? POLLOUT : 0) + +struct evport_data { + int ed_port; /* event port for system events */ + /* How many elements of ed_pending should we look at? */ + int ed_npending; + /* How many elements are allocated in ed_pending and pevtlist? */ + int ed_maxevents; + /* fdi's that we need to reassoc */ + int *ed_pending; + /* storage space for incoming events. */ + port_event_t *ed_pevtlist; + +}; + +static void* evport_init(struct event_base *); +static int evport_add(struct event_base *, int fd, short old, short events, void *); +static int evport_del(struct event_base *, int fd, short old, short events, void *); +static int evport_dispatch(struct event_base *, struct timeval *); +static void evport_dealloc(struct event_base *); +static int grow(struct evport_data *, int min_events); + +const struct eventop evportops = { + "evport", + evport_init, + evport_add, + evport_del, + evport_dispatch, + evport_dealloc, + 1, /* need reinit */ + 0, /* features */ + sizeof(struct fd_info), /* fdinfo length */ +}; + +/* + * Initialize the event port implementation. + */ + +static void* +evport_init(struct event_base *base) +{ + struct evport_data *evpd; + + if (!(evpd = mm_calloc(1, sizeof(struct evport_data)))) + return (NULL); + + if ((evpd->ed_port = port_create()) == -1) { + mm_free(evpd); + return (NULL); + } + + if (grow(evpd, INITIAL_EVENTS_PER_GETN) < 0) { + close(evpd->ed_port); + mm_free(evpd); + return NULL; + } + + evpd->ed_npending = 0; + + evsig_init_(base); + + return (evpd); +} + +static int +grow(struct evport_data *data, int min_events) +{ + int newsize; + int *new_pending; + port_event_t *new_pevtlist; + if (data->ed_maxevents) { + newsize = data->ed_maxevents; + do { + newsize *= 2; + } while (newsize < min_events); + } else { + newsize = min_events; + } + + new_pending = mm_realloc(data->ed_pending, sizeof(int)*newsize); + if (new_pending == NULL) + return -1; + data->ed_pending = new_pending; + new_pevtlist = mm_realloc(data->ed_pevtlist, sizeof(port_event_t)*newsize); + if (new_pevtlist == NULL) + return -1; + data->ed_pevtlist = new_pevtlist; + + data->ed_maxevents = newsize; + return 0; +} + +#ifdef CHECK_INVARIANTS +/* + * Checks some basic properties about the evport_data structure. Because it + * checks all file descriptors, this function can be expensive when the maximum + * file descriptor ever used is rather large. + */ + +static void +check_evportop(struct evport_data *evpd) +{ + EVUTIL_ASSERT(evpd); + EVUTIL_ASSERT(evpd->ed_port > 0); +} + +/* + * Verifies very basic integrity of a given port_event. + */ +static void +check_event(port_event_t* pevt) +{ + /* + * We've only registered for PORT_SOURCE_FD events. The only + * other thing we can legitimately receive is PORT_SOURCE_ALERT, + * but since we're not using port_alert either, we can assume + * PORT_SOURCE_FD. + */ + EVUTIL_ASSERT(pevt->portev_source == PORT_SOURCE_FD); +} + +#else +#define check_evportop(epop) +#define check_event(pevt) +#endif /* CHECK_INVARIANTS */ + +/* + * (Re)associates the given file descriptor with the event port. The OS events + * are specified (implicitly) from the fd_info struct. + */ +static int +reassociate(struct evport_data *epdp, struct fd_info *fdip, int fd) +{ + int sysevents = FDI_TO_SYSEVENTS(fdip); + + if (sysevents != 0) { + if (port_associate(epdp->ed_port, PORT_SOURCE_FD, + fd, sysevents, fdip) == -1) { + event_warn("port_associate"); + return (-1); + } + } + + check_evportop(epdp); + + return (0); +} + +/* + * Main event loop - polls port_getn for some number of events, and processes + * them. + */ + +static int +evport_dispatch(struct event_base *base, struct timeval *tv) +{ + int i, res; + struct evport_data *epdp = base->evbase; + port_event_t *pevtlist = epdp->ed_pevtlist; + + /* + * port_getn will block until it has at least nevents events. It will + * also return how many it's given us (which may be more than we asked + * for, as long as it's less than our maximum (ed_maxevents)) in + * nevents. + */ + int nevents = 1; + + /* + * We have to convert a struct timeval to a struct timespec + * (only difference is nanoseconds vs. microseconds). If no time-based + * events are active, we should wait for I/O (and tv == NULL). + */ + struct timespec ts; + struct timespec *ts_p = NULL; + if (tv != NULL) { + ts.tv_sec = tv->tv_sec; + ts.tv_nsec = tv->tv_usec * 1000; + ts_p = &ts; + } + + /* + * Before doing anything else, we need to reassociate the events we hit + * last time which need reassociation. See comment at the end of the + * loop below. + */ + for (i = 0; i < epdp->ed_npending; ++i) { + struct fd_info *fdi = NULL; + const int fd = epdp->ed_pending[i]; + if (fd != -1) { + /* We might have cleared out this event; we need + * to be sure that it's still set. */ + fdi = evmap_io_get_fdinfo_(&base->io, fd); + } + + if (fdi != NULL && FDI_HAS_EVENTS(fdi)) { + reassociate(epdp, fdi, fd); + /* epdp->ed_pending[i] = -1; */ + fdi->pending_idx_plus_1 = 0; + } + } + + EVBASE_RELEASE_LOCK(base, th_base_lock); + + res = port_getn(epdp->ed_port, pevtlist, epdp->ed_maxevents, + (unsigned int *) &nevents, ts_p); + + EVBASE_ACQUIRE_LOCK(base, th_base_lock); + + if (res == -1) { + if (errno == EINTR || errno == EAGAIN) { + return (0); + } else if (errno == ETIME) { + if (nevents == 0) + return (0); + } else { + event_warn("port_getn"); + return (-1); + } + } + + event_debug(("%s: port_getn reports %d events", __func__, nevents)); + + for (i = 0; i < nevents; ++i) { + port_event_t *pevt = &pevtlist[i]; + int fd = (int) pevt->portev_object; + struct fd_info *fdi = pevt->portev_user; + /*EVUTIL_ASSERT(evmap_io_get_fdinfo_(&base->io, fd) == fdi);*/ + + check_evportop(epdp); + check_event(pevt); + epdp->ed_pending[i] = fd; + fdi->pending_idx_plus_1 = i + 1; + + /* + * Figure out what kind of event it was + * (because we have to pass this to the callback) + */ + res = 0; + if (pevt->portev_events & (POLLERR|POLLHUP)) { + res = EV_READ | EV_WRITE; + } else { + if (pevt->portev_events & POLLIN) + res |= EV_READ; + if (pevt->portev_events & POLLOUT) + res |= EV_WRITE; + } + + /* + * Check for the error situations or a hangup situation + */ + if (pevt->portev_events & (POLLERR|POLLHUP|POLLNVAL)) + res |= EV_READ|EV_WRITE; + + evmap_io_active_(base, fd, res); + } /* end of all events gotten */ + epdp->ed_npending = nevents; + + if (nevents == epdp->ed_maxevents && + epdp->ed_maxevents < MAX_EVENTS_PER_GETN) { + /* we used all the space this time. We should be ready + * for more events next time around. */ + grow(epdp, epdp->ed_maxevents * 2); + } + + check_evportop(epdp); + + return (0); +} + + +/* + * Adds the given event (so that you will be notified when it happens via + * the callback function). + */ + +static int +evport_add(struct event_base *base, int fd, short old, short events, void *p) +{ + struct evport_data *evpd = base->evbase; + struct fd_info *fdi = p; + + check_evportop(evpd); + + fdi->fdi_what |= events; + + return reassociate(evpd, fdi, fd); +} + +/* + * Removes the given event from the list of events to wait for. + */ + +static int +evport_del(struct event_base *base, int fd, short old, short events, void *p) +{ + struct evport_data *evpd = base->evbase; + struct fd_info *fdi = p; + int associated = ! fdi->pending_idx_plus_1; + + check_evportop(evpd); + + fdi->fdi_what &= ~(events &(EV_READ|EV_WRITE)); + + if (associated) { + if (!FDI_HAS_EVENTS(fdi) && + port_dissociate(evpd->ed_port, PORT_SOURCE_FD, fd) == -1) { + /* + * Ignore EBADFD error the fd could have been closed + * before event_del() was called. + */ + if (errno != EBADFD) { + event_warn("port_dissociate"); + return (-1); + } + } else { + if (FDI_HAS_EVENTS(fdi)) { + return (reassociate(evpd, fdi, fd)); + } + } + } else { + if ((fdi->fdi_what & (EV_READ|EV_WRITE)) == 0) { + const int i = fdi->pending_idx_plus_1 - 1; + EVUTIL_ASSERT(evpd->ed_pending[i] == fd); + evpd->ed_pending[i] = -1; + fdi->pending_idx_plus_1 = 0; + } + } + return 0; +} + + +static void +evport_dealloc(struct event_base *base) +{ + struct evport_data *evpd = base->evbase; + + evsig_dealloc_(base); + + close(evpd->ed_port); + + if (evpd->ed_pending) + mm_free(evpd->ed_pending); + if (evpd->ed_pevtlist) + mm_free(evpd->ed_pevtlist); + + mm_free(evpd); +} + +#endif /* EVENT__HAVE_EVENT_PORTS */ diff --git a/libs/libevent/docs/evthread_pthread.c b/libs/libevent/docs/evthread_pthread.c new file mode 100644 index 0000000000..4e11f74970 --- /dev/null +++ b/libs/libevent/docs/evthread_pthread.c @@ -0,0 +1,191 @@ +/* + * Copyright 2009-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "event2/event-config.h" +#include "evconfig-private.h" + +/* With glibc we need to define _GNU_SOURCE to get PTHREAD_MUTEX_RECURSIVE. + * This comes from evconfig-private.h + */ +#include <pthread.h> + +struct event_base; +#include "event2/thread.h" + +#include <stdlib.h> +#include <string.h> +#include "mm-internal.h" +#include "evthread-internal.h" + +static pthread_mutexattr_t attr_recursive; + +static void * +evthread_posix_lock_alloc(unsigned locktype) +{ + pthread_mutexattr_t *attr = NULL; + pthread_mutex_t *lock = mm_malloc(sizeof(pthread_mutex_t)); + if (!lock) + return NULL; + if (locktype & EVTHREAD_LOCKTYPE_RECURSIVE) + attr = &attr_recursive; + if (pthread_mutex_init(lock, attr)) { + mm_free(lock); + return NULL; + } + return lock; +} + +static void +evthread_posix_lock_free(void *lock_, unsigned locktype) +{ + pthread_mutex_t *lock = lock_; + pthread_mutex_destroy(lock); + mm_free(lock); +} + +static int +evthread_posix_lock(unsigned mode, void *lock_) +{ + pthread_mutex_t *lock = lock_; + if (mode & EVTHREAD_TRY) + return pthread_mutex_trylock(lock); + else + return pthread_mutex_lock(lock); +} + +static int +evthread_posix_unlock(unsigned mode, void *lock_) +{ + pthread_mutex_t *lock = lock_; + return pthread_mutex_unlock(lock); +} + +static unsigned long +evthread_posix_get_id(void) +{ + union { + pthread_t thr; +#if EVENT__SIZEOF_PTHREAD_T > EVENT__SIZEOF_LONG + ev_uint64_t id; +#else + unsigned long id; +#endif + } r; +#if EVENT__SIZEOF_PTHREAD_T < EVENT__SIZEOF_LONG + memset(&r, 0, sizeof(r)); +#endif + r.thr = pthread_self(); + return (unsigned long)r.id; +} + +static void * +evthread_posix_cond_alloc(unsigned condflags) +{ + pthread_cond_t *cond = mm_malloc(sizeof(pthread_cond_t)); + if (!cond) + return NULL; + if (pthread_cond_init(cond, NULL)) { + mm_free(cond); + return NULL; + } + return cond; +} + +static void +evthread_posix_cond_free(void *cond_) +{ + pthread_cond_t *cond = cond_; + pthread_cond_destroy(cond); + mm_free(cond); +} + +static int +evthread_posix_cond_signal(void *cond_, int broadcast) +{ + pthread_cond_t *cond = cond_; + int r; + if (broadcast) + r = pthread_cond_broadcast(cond); + else + r = pthread_cond_signal(cond); + return r ? -1 : 0; +} + +static int +evthread_posix_cond_wait(void *cond_, void *lock_, const struct timeval *tv) +{ + int r; + pthread_cond_t *cond = cond_; + pthread_mutex_t *lock = lock_; + + if (tv) { + struct timeval now, abstime; + struct timespec ts; + evutil_gettimeofday(&now, NULL); + evutil_timeradd(&now, tv, &abstime); + ts.tv_sec = abstime.tv_sec; + ts.tv_nsec = abstime.tv_usec*1000; + r = pthread_cond_timedwait(cond, lock, &ts); + if (r == ETIMEDOUT) + return 1; + else if (r) + return -1; + else + return 0; + } else { + r = pthread_cond_wait(cond, lock); + return r ? -1 : 0; + } +} + +int +evthread_use_pthreads(void) +{ + struct evthread_lock_callbacks cbs = { + EVTHREAD_LOCK_API_VERSION, + EVTHREAD_LOCKTYPE_RECURSIVE, + evthread_posix_lock_alloc, + evthread_posix_lock_free, + evthread_posix_lock, + evthread_posix_unlock + }; + struct evthread_condition_callbacks cond_cbs = { + EVTHREAD_CONDITION_API_VERSION, + evthread_posix_cond_alloc, + evthread_posix_cond_free, + evthread_posix_cond_signal, + evthread_posix_cond_wait + }; + /* Set ourselves up to get recursive locks. */ + if (pthread_mutexattr_init(&attr_recursive)) + return -1; + if (pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE)) + return -1; + + evthread_set_lock_callbacks(&cbs); + evthread_set_condition_callbacks(&cond_cbs); + evthread_set_id_callback(evthread_posix_get_id); + return 0; +} diff --git a/libs/libevent/docs/kqueue-internal.h b/libs/libevent/docs/kqueue-internal.h new file mode 100644 index 0000000000..02c5a3606c --- /dev/null +++ b/libs/libevent/docs/kqueue-internal.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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 KQUEUE_INTERNAL_H_INCLUDED_ +#define KQUEUE_INTERNAL_H_INCLUDED_ + +/** Notification function, used to tell an event base to wake up from another + * thread. Only works when event_kq_add_notify_event_() has previously been + * called successfully on that base. */ +int event_kq_notify_base_(struct event_base *base); + +/** Prepare a kqueue-using event base to receive notifications via an internal + * EVFILT_USER event. Return 0 on sucess, -1 on failure. + */ +int event_kq_add_notify_event_(struct event_base *base); + +#endif diff --git a/libs/libevent/docs/kqueue.c b/libs/libevent/docs/kqueue.c new file mode 100644 index 0000000000..1f41b5a768 --- /dev/null +++ b/libs/libevent/docs/kqueue.c @@ -0,0 +1,567 @@ +/* $OpenBSD: kqueue.c,v 1.5 2002/07/10 14:41:31 art Exp $ */ + +/* + * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "event2/event-config.h" +#include "evconfig-private.h" + +#ifdef EVENT__HAVE_KQUEUE + +#include <sys/types.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/queue.h> +#include <sys/event.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#ifdef EVENT__HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +/* Some platforms apparently define the udata field of struct kevent as + * intptr_t, whereas others define it as void*. There doesn't seem to be an + * easy way to tell them apart via autoconf, so we need to use OS macros. */ +#if defined(EVENT__HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__darwin__) && !defined(__APPLE__) && !defined(__CloudABI__) +#define PTR_TO_UDATA(x) ((intptr_t)(x)) +#define INT_TO_UDATA(x) ((intptr_t)(x)) +#else +#define PTR_TO_UDATA(x) (x) +#define INT_TO_UDATA(x) ((void*)(x)) +#endif + +#include "event-internal.h" +#include "log-internal.h" +#include "evmap-internal.h" +#include "event2/thread.h" +#include "evthread-internal.h" +#include "changelist-internal.h" + +#include "kqueue-internal.h" + +#define NEVENT 64 + +struct kqop { + struct kevent *changes; + int changes_size; + + struct kevent *events; + int events_size; + int kq; + int notify_event_added; + pid_t pid; +}; + +static void kqop_free(struct kqop *kqop); + +static void *kq_init(struct event_base *); +static int kq_sig_add(struct event_base *, int, short, short, void *); +static int kq_sig_del(struct event_base *, int, short, short, void *); +static int kq_dispatch(struct event_base *, struct timeval *); +static void kq_dealloc(struct event_base *); + +const struct eventop kqops = { + "kqueue", + kq_init, + event_changelist_add_, + event_changelist_del_, + kq_dispatch, + kq_dealloc, + 1 /* need reinit */, + EV_FEATURE_ET|EV_FEATURE_O1|EV_FEATURE_FDS, + EVENT_CHANGELIST_FDINFO_SIZE +}; + +static const struct eventop kqsigops = { + "kqueue_signal", + NULL, + kq_sig_add, + kq_sig_del, + NULL, + NULL, + 1 /* need reinit */, + 0, + 0 +}; + +static void * +kq_init(struct event_base *base) +{ + int kq = -1; + struct kqop *kqueueop = NULL; + + if (!(kqueueop = mm_calloc(1, sizeof(struct kqop)))) + return (NULL); + +/* Initialize the kernel queue */ + + if ((kq = kqueue()) == -1) { + event_warn("kqueue"); + goto err; + } + + kqueueop->kq = kq; + + kqueueop->pid = getpid(); + + /* Initialize fields */ + kqueueop->changes = mm_calloc(NEVENT, sizeof(struct kevent)); + if (kqueueop->changes == NULL) + goto err; + kqueueop->events = mm_calloc(NEVENT, sizeof(struct kevent)); + if (kqueueop->events == NULL) + goto err; + kqueueop->events_size = kqueueop->changes_size = NEVENT; + + /* Check for Mac OS X kqueue bug. */ + memset(&kqueueop->changes[0], 0, sizeof kqueueop->changes[0]); + kqueueop->changes[0].ident = -1; + kqueueop->changes[0].filter = EVFILT_READ; + kqueueop->changes[0].flags = EV_ADD; + /* + * If kqueue works, then kevent will succeed, and it will + * stick an error in events[0]. If kqueue is broken, then + * kevent will fail. + */ + if (kevent(kq, + kqueueop->changes, 1, kqueueop->events, NEVENT, NULL) != 1 || + (int)kqueueop->events[0].ident != -1 || + kqueueop->events[0].flags != EV_ERROR) { + event_warn("%s: detected broken kqueue; not using.", __func__); + goto err; + } + + base->evsigsel = &kqsigops; + + return (kqueueop); +err: + if (kqueueop) + kqop_free(kqueueop); + + return (NULL); +} + +#define ADD_UDATA 0x30303 + +static void +kq_setup_kevent(struct kevent *out, evutil_socket_t fd, int filter, short change) +{ + memset(out, 0, sizeof(struct kevent)); + out->ident = fd; + out->filter = filter; + + if (change & EV_CHANGE_ADD) { + out->flags = EV_ADD; + /* We set a magic number here so that we can tell 'add' + * errors from 'del' errors. */ + out->udata = INT_TO_UDATA(ADD_UDATA); + if (change & EV_ET) + out->flags |= EV_CLEAR; +#ifdef NOTE_EOF + /* Make it behave like select() and poll() */ + if (filter == EVFILT_READ) + out->fflags = NOTE_EOF; +#endif + } else { + EVUTIL_ASSERT(change & EV_CHANGE_DEL); + out->flags = EV_DELETE; + } +} + +static int +kq_build_changes_list(const struct event_changelist *changelist, + struct kqop *kqop) +{ + int i; + int n_changes = 0; + + for (i = 0; i < changelist->n_changes; ++i) { + struct event_change *in_ch = &changelist->changes[i]; + struct kevent *out_ch; + if (n_changes >= kqop->changes_size - 1) { + int newsize = kqop->changes_size * 2; + struct kevent *newchanges; + + newchanges = mm_realloc(kqop->changes, + newsize * sizeof(struct kevent)); + if (newchanges == NULL) { + event_warn("%s: realloc", __func__); + return (-1); + } + kqop->changes = newchanges; + kqop->changes_size = newsize; + } + if (in_ch->read_change) { + out_ch = &kqop->changes[n_changes++]; + kq_setup_kevent(out_ch, in_ch->fd, EVFILT_READ, + in_ch->read_change); + } + if (in_ch->write_change) { + out_ch = &kqop->changes[n_changes++]; + kq_setup_kevent(out_ch, in_ch->fd, EVFILT_WRITE, + in_ch->write_change); + } + } + return n_changes; +} + +static int +kq_grow_events(struct kqop *kqop, size_t new_size) +{ + struct kevent *newresult; + + newresult = mm_realloc(kqop->events, + new_size * sizeof(struct kevent)); + + if (newresult) { + kqop->events = newresult; + kqop->events_size = new_size; + return 0; + } else { + return -1; + } +} + +static int +kq_dispatch(struct event_base *base, struct timeval *tv) +{ + struct kqop *kqop = base->evbase; + struct kevent *events = kqop->events; + struct kevent *changes; + struct timespec ts, *ts_p = NULL; + int i, n_changes, res; + + if (tv != NULL) { + ts.tv_sec = tv->tv_sec; + ts.tv_nsec = tv->tv_usec * 1000; + ts_p = &ts; + } + + /* Build "changes" from "base->changes" */ + EVUTIL_ASSERT(kqop->changes); + n_changes = kq_build_changes_list(&base->changelist, kqop); + if (n_changes < 0) + return -1; + + event_changelist_remove_all_(&base->changelist, base); + + /* steal the changes array in case some broken code tries to call + * dispatch twice at once. */ + changes = kqop->changes; + kqop->changes = NULL; + + /* Make sure that 'events' is at least as long as the list of changes: + * otherwise errors in the changes can get reported as a -1 return + * value from kevent() rather than as EV_ERROR events in the events + * array. + * + * (We could instead handle -1 return values from kevent() by + * retrying with a smaller changes array or a larger events array, + * but this approach seems less risky for now.) + */ + if (kqop->events_size < n_changes) { + int new_size = kqop->events_size; + do { + new_size *= 2; + } while (new_size < n_changes); + + kq_grow_events(kqop, new_size); + events = kqop->events; + } + + EVBASE_RELEASE_LOCK(base, th_base_lock); + + res = kevent(kqop->kq, changes, n_changes, + events, kqop->events_size, ts_p); + + EVBASE_ACQUIRE_LOCK(base, th_base_lock); + + EVUTIL_ASSERT(kqop->changes == NULL); + kqop->changes = changes; + + if (res == -1) { + if (errno != EINTR) { + event_warn("kevent"); + return (-1); + } + + return (0); + } + + event_debug(("%s: kevent reports %d", __func__, res)); + + for (i = 0; i < res; i++) { + int which = 0; + + if (events[i].flags & EV_ERROR) { + switch (events[i].data) { + + /* Can occur on delete if we are not currently + * watching any events on this fd. That can + * happen when the fd was closed and another + * file was opened with that fd. */ + case ENOENT: + /* Can occur for reasons not fully understood + * on FreeBSD. */ + case EINVAL: + continue; +#if defined(__FreeBSD__) + /* + * This currently occurs if an FD is closed + * before the EV_DELETE makes it out via kevent(). + * The FreeBSD capabilities code sees the blank + * capability set and rejects the request to + * modify an event. + * + * To be strictly correct - when an FD is closed, + * all the registered events are also removed. + * Queuing EV_DELETE to a closed FD is wrong. + * The event(s) should just be deleted from + * the pending changelist. + */ + case ENOTCAPABLE: + continue; +#endif + + /* Can occur on a delete if the fd is closed. */ + case EBADF: + /* XXXX On NetBSD, we can also get EBADF if we + * try to add the write side of a pipe, but + * the read side has already been closed. + * Other BSDs call this situation 'EPIPE'. It + * would be good if we had a way to report + * this situation. */ + continue; + /* These two can occur on an add if the fd was one side + * of a pipe, and the other side was closed. */ + case EPERM: + case EPIPE: + /* Report read events, if we're listening for + * them, so that the user can learn about any + * add errors. (If the operation was a + * delete, then udata should be cleared.) */ + if (events[i].udata) { + /* The operation was an add: + * report the error as a read. */ + which |= EV_READ; + break; + } else { + /* The operation was a del: + * report nothing. */ + continue; + } + + /* Other errors shouldn't occur. */ + default: + errno = events[i].data; + return (-1); + } + } else if (events[i].filter == EVFILT_READ) { + which |= EV_READ; + } else if (events[i].filter == EVFILT_WRITE) { + which |= EV_WRITE; + } else if (events[i].filter == EVFILT_SIGNAL) { + which |= EV_SIGNAL; +#ifdef EVFILT_USER + } else if (events[i].filter == EVFILT_USER) { + base->is_notify_pending = 0; +#endif + } + + if (!which) + continue; + + if (events[i].filter == EVFILT_SIGNAL) { + evmap_signal_active_(base, events[i].ident, 1); + } else { + evmap_io_active_(base, events[i].ident, which | EV_ET); + } + } + + if (res == kqop->events_size) { + /* We used all the events space that we have. Maybe we should + make it bigger. */ + kq_grow_events(kqop, kqop->events_size * 2); + } + + return (0); +} + +static void +kqop_free(struct kqop *kqop) +{ + if (kqop->changes) + mm_free(kqop->changes); + if (kqop->events) + mm_free(kqop->events); + if (kqop->kq >= 0 && kqop->pid == getpid()) + close(kqop->kq); + memset(kqop, 0, sizeof(struct kqop)); + mm_free(kqop); +} + +static void +kq_dealloc(struct event_base *base) +{ + struct kqop *kqop = base->evbase; + evsig_dealloc_(base); + kqop_free(kqop); +} + +/* signal handling */ +static int +kq_sig_add(struct event_base *base, int nsignal, short old, short events, void *p) +{ + struct kqop *kqop = base->evbase; + struct kevent kev; + struct timespec timeout = { 0, 0 }; + (void)p; + + EVUTIL_ASSERT(nsignal >= 0 && nsignal < NSIG); + + memset(&kev, 0, sizeof(kev)); + kev.ident = nsignal; + kev.filter = EVFILT_SIGNAL; + kev.flags = EV_ADD; + + /* Be ready for the signal if it is sent any + * time between now and the next call to + * kq_dispatch. */ + if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) + return (-1); + + /* We can set the handler for most signals to SIG_IGN and + * still have them reported to us in the queue. However, + * if the handler for SIGCHLD is SIG_IGN, the system reaps + * zombie processes for us, and we don't get any notification. + * This appears to be the only signal with this quirk. */ + if (evsig_set_handler_(base, nsignal, + nsignal == SIGCHLD ? SIG_DFL : SIG_IGN) == -1) + return (-1); + + return (0); +} + +static int +kq_sig_del(struct event_base *base, int nsignal, short old, short events, void *p) +{ + struct kqop *kqop = base->evbase; + struct kevent kev; + + struct timespec timeout = { 0, 0 }; + (void)p; + + EVUTIL_ASSERT(nsignal >= 0 && nsignal < NSIG); + + memset(&kev, 0, sizeof(kev)); + kev.ident = nsignal; + kev.filter = EVFILT_SIGNAL; + kev.flags = EV_DELETE; + + /* Because we insert signal events + * immediately, we need to delete them + * immediately, too */ + if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) + return (-1); + + if (evsig_restore_handler_(base, nsignal) == -1) + return (-1); + + return (0); +} + + +/* OSX 10.6 and FreeBSD 8.1 add support for EVFILT_USER, which we can use + * to wake up the event loop from another thread. */ + +/* Magic number we use for our filter ID. */ +#define NOTIFY_IDENT 42 + +int +event_kq_add_notify_event_(struct event_base *base) +{ + struct kqop *kqop = base->evbase; +#if defined(EVFILT_USER) && defined(NOTE_TRIGGER) + struct kevent kev; + struct timespec timeout = { 0, 0 }; +#endif + + if (kqop->notify_event_added) + return 0; + +#if defined(EVFILT_USER) && defined(NOTE_TRIGGER) + memset(&kev, 0, sizeof(kev)); + kev.ident = NOTIFY_IDENT; + kev.filter = EVFILT_USER; + kev.flags = EV_ADD | EV_CLEAR; + + if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) { + event_warn("kevent: adding EVFILT_USER event"); + return -1; + } + + kqop->notify_event_added = 1; + + return 0; +#else + return -1; +#endif +} + +int +event_kq_notify_base_(struct event_base *base) +{ + struct kqop *kqop = base->evbase; +#if defined(EVFILT_USER) && defined(NOTE_TRIGGER) + struct kevent kev; + struct timespec timeout = { 0, 0 }; +#endif + if (! kqop->notify_event_added) + return -1; + +#if defined(EVFILT_USER) && defined(NOTE_TRIGGER) + memset(&kev, 0, sizeof(kev)); + kev.ident = NOTIFY_IDENT; + kev.filter = EVFILT_USER; + kev.fflags = NOTE_TRIGGER; + + if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) { + event_warn("kevent: triggering EVFILT_USER event"); + return -1; + } + + return 0; +#else + return -1; +#endif +} + +#endif /* EVENT__HAVE_KQUEUE */ diff --git a/libs/libevent/docs/libevent.pc.in b/libs/libevent/docs/libevent.pc.in new file mode 100644 index 0000000000..7030884eeb --- /dev/null +++ b/libs/libevent/docs/libevent.pc.in @@ -0,0 +1,16 @@ +#libevent pkg-config source file + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libevent +Description: libevent is an asynchronous notification event loop library +Version: @VERSION@ +Requires: +Conflicts: +Libs: -L${libdir} -levent +Libs.private: @LIBS@ +Cflags: -I${includedir} + diff --git a/libs/libevent/docs/libevent_openssl.pc.in b/libs/libevent/docs/libevent_openssl.pc.in new file mode 100644 index 0000000000..a65d1e0668 --- /dev/null +++ b/libs/libevent/docs/libevent_openssl.pc.in @@ -0,0 +1,16 @@ +#libevent pkg-config source file + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libevent_openssl +Description: libevent_openssl adds openssl-based TLS support to libevent +Version: @VERSION@ +Requires: libevent +Conflicts: +Libs: -L${libdir} -levent_openssl +Libs.private: @LIBS@ @OPENSSL_LIBS@ +Cflags: -I${includedir} @OPENSSL_INCS@ + diff --git a/libs/libevent/docs/libevent_pthreads.pc.in b/libs/libevent/docs/libevent_pthreads.pc.in new file mode 100644 index 0000000000..9bc2392b34 --- /dev/null +++ b/libs/libevent/docs/libevent_pthreads.pc.in @@ -0,0 +1,16 @@ +#libevent pkg-config source file + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libevent_pthreads +Description: libevent_pthreads adds pthreads-based threading support to libevent +Version: @VERSION@ +Requires: libevent +Conflicts: +Libs: -L${libdir} -levent_pthreads +Libs.private: @LIBS@ @PTHREAD_LIBS@ +Cflags: -I${includedir} @PTHREAD_CFLAGS@ + diff --git a/libs/libevent/docs/m4/ac_backport_259_ssizet.m4 b/libs/libevent/docs/m4/ac_backport_259_ssizet.m4 new file mode 100644 index 0000000000..75fde386cb --- /dev/null +++ b/libs/libevent/docs/m4/ac_backport_259_ssizet.m4 @@ -0,0 +1,3 @@ +AN_IDENTIFIER([ssize_t], [AC_TYPE_SSIZE_T]) +AC_DEFUN([AC_TYPE_SSIZE_T], [AC_CHECK_TYPE(ssize_t, int)]) + diff --git a/libs/libevent/docs/m4/acx_pthread.m4 b/libs/libevent/docs/m4/acx_pthread.m4 new file mode 100644 index 0000000000..d2b116945f --- /dev/null +++ b/libs/libevent/docs/m4/acx_pthread.m4 @@ -0,0 +1,279 @@ +##### http://autoconf-archive.cryp.to/acx_pthread.html +# +# SYNOPSIS +# +# ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. +# It sets the PTHREAD_LIBS output variable to the threads library and +# linker flags, and the PTHREAD_CFLAGS output variable to any special +# C compiler flags that are needed. (The user can also force certain +# compiler flags/libs to be tested by setting these environment +# variables.) +# +# Also sets PTHREAD_CC to any special C compiler that is needed for +# multi-threaded programs (defaults to the value of CC otherwise). +# (This is necessary on AIX to use the special cc_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these +# flags, but also link it with them as well. e.g. you should link +# with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS +# $LIBS +# +# If you are only building threads programs, you may wish to use +# these variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute +# constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to +# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads +# library is found, and ACTION-IF-NOT-FOUND is a list of commands to +# run it if it is not found. If ACTION-IF-FOUND is not specified, the +# default action will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or +# if you have any other suggestions or comments. This macro was based +# on work by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) +# (with help from M. Frigo), as well as ac_pthread and hb_pthread +# macros posted by Alejandro Forero Cuervo to the autoconf macro +# repository. We are also grateful for the helpful feedback of +# numerous users. +# +# LAST MODIFICATION +# +# 2007-07-29 +# +# COPYLEFT +# +# Copyright (c) 2007 Steven G. Johnson <stevenj@alum.mit.edu> +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see +# <http://www.gnu.org/licenses/>. +# +# As a special exception, the respective Autoconf Macro's copyright +# owner gives unlimited permission to copy, distribute and modify the +# configure scripts that are the output of Autoconf when processing +# the Macro. You need not follow the terms of the GNU General Public +# License when using or distributing such scripts, even though +# portions of the text of the Macro appear in them. The GNU General +# Public License (GPL) does govern all other use of the material that +# constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the +# Autoconf Macro released by the Autoconf Macro Archive. When you +# make and distribute a modified version of the Autoconf Macro, you +# may extend this special exception to the GPL to apply to your +# modified version as well. + +AC_DEFUN([ACX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_SAVE +AC_LANG_C +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) + if test x"$acx_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include <pthread.h>], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_MSG_CHECKING([for joinable pthread attribute]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;], + [attr_name=$attr; break]) + done + AC_MSG_RESULT($attr_name) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: must compile with xlc_r or cc_r + if test x"$GCC" != xyes; then + AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) + else + PTHREAD_CC=$CC + fi +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + acx_pthread_ok=no + $2 +fi +AC_LANG_RESTORE +])dnl ACX_PTHREAD diff --git a/libs/libevent/docs/m4/libevent_openssl.m4 b/libs/libevent/docs/m4/libevent_openssl.m4 new file mode 100644 index 0000000000..3cb064a0bd --- /dev/null +++ b/libs/libevent/docs/m4/libevent_openssl.m4 @@ -0,0 +1,47 @@ +dnl ###################################################################### +dnl OpenSSL support +AC_DEFUN([LIBEVENT_OPENSSL], [ +AC_REQUIRE([NTP_PKG_CONFIG])dnl + +case "$enable_openssl" in + yes) + have_openssl=no + case "$PKG_CONFIG" in + '') + ;; + *) + OPENSSL_LIBS=`$PKG_CONFIG --libs openssl 2>/dev/null` + case "$OPENSSL_LIBS" in + '') ;; + *) OPENSSL_LIBS="$OPENSSL_LIBS $EV_LIB_GDI $EV_LIB_WS32 $OPENSSL_LIBADD" + have_openssl=yes + ;; + esac + OPENSSL_INCS=`$PKG_CONFIG --cflags openssl 2>/dev/null` + ;; + esac + case "$have_openssl" in + yes) ;; + *) + save_LIBS="$LIBS" + LIBS="" + OPENSSL_LIBS="" + AC_SEARCH_LIBS([SSL_new], [ssl], + [have_openssl=yes + OPENSSL_LIBS="$LIBS -lcrypto $EV_LIB_GDI $EV_LIB_WS32 $OPENSSL_LIBADD"], + [have_openssl=no], + [-lcrypto $EV_LIB_GDI $EV_LIB_WS32 $OPENSSL_LIBADD]) + LIBS="$save_LIBS" + ;; + esac + AC_SUBST(OPENSSL_INCS) + AC_SUBST(OPENSSL_LIBS) + case "$have_openssl" in + yes) AC_DEFINE(HAVE_OPENSSL, 1, [Define if the system has openssl]) ;; + esac + ;; +esac + +# check if we have and should use openssl +AM_CONDITIONAL(OPENSSL, [test "$enable_openssl" != "no" && test "$have_openssl" = "yes"]) +]) diff --git a/libs/libevent/docs/m4/ntp_pkg_config.m4 b/libs/libevent/docs/m4/ntp_pkg_config.m4 new file mode 100644 index 0000000000..1bce8a6e4d --- /dev/null +++ b/libs/libevent/docs/m4/ntp_pkg_config.m4 @@ -0,0 +1,27 @@ +dnl NTP_PKG_CONFIG -*- Autoconf -*- +dnl +dnl Look for pkg-config, which must be at least +dnl $ntp_pkgconfig_min_version. +dnl +AC_DEFUN([NTP_PKG_CONFIG], [ + +dnl lower the minimum version if you find an earlier one works +ntp_pkgconfig_min_version='0.15.0' +AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +AS_UNSET([ac_cv_path_PKG_CONFIG]) +AS_UNSET([ac_cv_path_ac_pt_PKG_CONFIG]) + +case "$PKG_CONFIG" in + /*) + AC_MSG_CHECKING([if pkg-config is at least version $ntp_pkgconfig_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $ntp_pkgconfig_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi + ;; +esac + +]) dnl NTP_PKG_CONFIG + diff --git a/libs/libevent/docs/make-event-config.sed b/libs/libevent/docs/make-event-config.sed new file mode 100644 index 0000000000..e31018a2dd --- /dev/null +++ b/libs/libevent/docs/make-event-config.sed @@ -0,0 +1,23 @@ +# Sed script to postprocess config.h into event-config.h. + +1i\ +/* event2/event-config.h\ + *\ + * This file was generated by autoconf when libevent was built, and post-\ + * processed by Libevent so that its macros would have a uniform prefix.\ + *\ + * DO NOT EDIT THIS FILE.\ + *\ + * Do not rely on macros in this file existing in later versions.\ + */\ +\ +#ifndef EVENT2_EVENT_CONFIG_H_INCLUDED_\ +#define EVENT2_EVENT_CONFIG_H_INCLUDED_\ + +$a\ +\ +#endif /* event2/event-config.h */ + +s/#\( *\)define /#\1define EVENT__/ +s/#\( *\)undef /#\1undef EVENT__/ +s/#\( *\)if\(n*\)def /#\1if\2def EVENT__/ diff --git a/libs/libevent/docs/make_epoll_table.py b/libs/libevent/docs/make_epoll_table.py new file mode 100644 index 0000000000..1b15a91a67 --- /dev/null +++ b/libs/libevent/docs/make_epoll_table.py @@ -0,0 +1,63 @@ +#!/usr/bin/python2 + +def get(old,wc,rc,cc): + if ('xxx' in (rc, wc, cc)): + return "0",255 + + if ('add' in (rc, wc, cc)): + events = [] + if rc == 'add' or (rc != 'del' and 'r' in old): + events.append("EPOLLIN") + if wc == 'add' or (wc != 'del' and 'w' in old): + events.append("EPOLLOUT") + if cc == 'add' or (cc != 'del' and 'c' in old): + events.append("EPOLLRDHUP") + + if old == "0": + op = "EPOLL_CTL_ADD" + else: + op = "EPOLL_CTL_MOD" + return "|".join(events), op + + if ('del' in (rc, wc, cc)): + delevents = [] + modevents = [] + op = "EPOLL_CTL_DEL" + + if 'r' in old: + modevents.append("EPOLLIN") + if 'w' in old: + modevents.append("EPOLLOUT") + if 'c' in old: + modevents.append("EPOLLRDHUP") + + for item, event in [(rc,"EPOLLIN"), + (wc,"EPOLLOUT"), + (cc,"EPOLLRDHUP")]: + if item == 'del': + delevents.append(event) + if event in modevents: + modevents.remove(event) + + if modevents: + return "|".join(modevents), "EPOLL_CTL_MOD" + else: + return "|".join(delevents), "EPOLL_CTL_DEL" + + return 0, 0 + + +def fmt(op, ev, old, wc, rc, cc): + entry = "{ %s, %s },"%(op, ev) + print "\t/* old=%3s, write:%3s, read:%3s, close:%3s */\n\t%s" % ( + old, wc, rc, cc, entry) + return len(entry) + +for old in ('0','r','w','rw','c','cr','cw','crw'): + for wc in ('0', 'add', 'del', 'xxx'): + for rc in ('0', 'add', 'del', 'xxx'): + for cc in ('0', 'add', 'del', 'xxx'): + + op,ev = get(old,wc,rc,cc) + + fmt(op, ev, old, wc, rc, cc) diff --git a/libs/libevent/docs/poll.c b/libs/libevent/docs/poll.c new file mode 100644 index 0000000000..51475934b3 --- /dev/null +++ b/libs/libevent/docs/poll.c @@ -0,0 +1,341 @@ +/* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ + +/* + * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "event2/event-config.h" +#include "evconfig-private.h" + +#ifdef EVENT__HAVE_POLL + +#include <sys/types.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/queue.h> +#include <poll.h> +#include <signal.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include "event-internal.h" +#include "evsignal-internal.h" +#include "log-internal.h" +#include "evmap-internal.h" +#include "event2/thread.h" +#include "evthread-internal.h" +#include "time-internal.h" + +struct pollidx { + int idxplus1; +}; + +struct pollop { + int event_count; /* Highest number alloc */ + int nfds; /* Highest number used */ + int realloc_copy; /* True iff we must realloc + * event_set_copy */ + struct pollfd *event_set; + struct pollfd *event_set_copy; +}; + +static void *poll_init(struct event_base *); +static int poll_add(struct event_base *, int, short old, short events, void *idx); +static int poll_del(struct event_base *, int, short old, short events, void *idx); +static int poll_dispatch(struct event_base *, struct timeval *); +static void poll_dealloc(struct event_base *); + +const struct eventop pollops = { + "poll", + poll_init, + poll_add, + poll_del, + poll_dispatch, + poll_dealloc, + 0, /* doesn't need_reinit */ + EV_FEATURE_FDS, + sizeof(struct pollidx), +}; + +static void * +poll_init(struct event_base *base) +{ + struct pollop *pollop; + + if (!(pollop = mm_calloc(1, sizeof(struct pollop)))) + return (NULL); + + evsig_init_(base); + + evutil_weakrand_seed_(&base->weakrand_seed, 0); + + return (pollop); +} + +#ifdef CHECK_INVARIANTS +static void +poll_check_ok(struct pollop *pop) +{ + int i, idx; + struct event *ev; + + for (i = 0; i < pop->fd_count; ++i) { + idx = pop->idxplus1_by_fd[i]-1; + if (idx < 0) + continue; + EVUTIL_ASSERT(pop->event_set[idx].fd == i); + } + for (i = 0; i < pop->nfds; ++i) { + struct pollfd *pfd = &pop->event_set[i]; + EVUTIL_ASSERT(pop->idxplus1_by_fd[pfd->fd] == i+1); + } +} +#else +#define poll_check_ok(pop) +#endif + +static int +poll_dispatch(struct event_base *base, struct timeval *tv) +{ + int res, i, j, nfds; + long msec = -1; + struct pollop *pop = base->evbase; + struct pollfd *event_set; + + poll_check_ok(pop); + + nfds = pop->nfds; + +#ifndef EVENT__DISABLE_THREAD_SUPPORT + if (base->th_base_lock) { + /* If we're using this backend in a multithreaded setting, + * then we need to work on a copy of event_set, so that we can + * let other threads modify the main event_set while we're + * polling. If we're not multithreaded, then we'll skip the + * copy step here to save memory and time. */ + if (pop->realloc_copy) { + struct pollfd *tmp = mm_realloc(pop->event_set_copy, + pop->event_count * sizeof(struct pollfd)); + if (tmp == NULL) { + event_warn("realloc"); + return -1; + } + pop->event_set_copy = tmp; + pop->realloc_copy = 0; + } + memcpy(pop->event_set_copy, pop->event_set, + sizeof(struct pollfd)*nfds); + event_set = pop->event_set_copy; + } else { + event_set = pop->event_set; + } +#else + event_set = pop->event_set; +#endif + + if (tv != NULL) { + msec = evutil_tv_to_msec_(tv); + if (msec < 0 || msec > INT_MAX) + msec = INT_MAX; + } + + EVBASE_RELEASE_LOCK(base, th_base_lock); + + res = poll(event_set, nfds, msec); + + EVBASE_ACQUIRE_LOCK(base, th_base_lock); + + if (res == -1) { + if (errno != EINTR) { + event_warn("poll"); + return (-1); + } + + return (0); + } + + event_debug(("%s: poll reports %d", __func__, res)); + + if (res == 0 || nfds == 0) + return (0); + + i = evutil_weakrand_range_(&base->weakrand_seed, nfds); + for (j = 0; j < nfds; j++) { + int what; + if (++i == nfds) + i = 0; + what = event_set[i].revents; + if (!what) + continue; + + res = 0; + + /* If the file gets closed notify */ + if (what & (POLLHUP|POLLERR)) + what |= POLLIN|POLLOUT; + if (what & POLLIN) + res |= EV_READ; + if (what & POLLOUT) + res |= EV_WRITE; + if (res == 0) + continue; + + evmap_io_active_(base, event_set[i].fd, res); + } + + return (0); +} + +static int +poll_add(struct event_base *base, int fd, short old, short events, void *idx_) +{ + struct pollop *pop = base->evbase; + struct pollfd *pfd = NULL; + struct pollidx *idx = idx_; + int i; + + EVUTIL_ASSERT((events & EV_SIGNAL) == 0); + if (!(events & (EV_READ|EV_WRITE))) + return (0); + + poll_check_ok(pop); + if (pop->nfds + 1 >= pop->event_count) { + struct pollfd *tmp_event_set; + int tmp_event_count; + + if (pop->event_count < 32) + tmp_event_count = 32; + else + tmp_event_count = pop->event_count * 2; + + /* We need more file descriptors */ + tmp_event_set = mm_realloc(pop->event_set, + tmp_event_count * sizeof(struct pollfd)); + if (tmp_event_set == NULL) { + event_warn("realloc"); + return (-1); + } + pop->event_set = tmp_event_set; + + pop->event_count = tmp_event_count; + pop->realloc_copy = 1; + } + + i = idx->idxplus1 - 1; + + if (i >= 0) { + pfd = &pop->event_set[i]; + } else { + i = pop->nfds++; + pfd = &pop->event_set[i]; + pfd->events = 0; + pfd->fd = fd; + idx->idxplus1 = i + 1; + } + + pfd->revents = 0; + if (events & EV_WRITE) + pfd->events |= POLLOUT; + if (events & EV_READ) + pfd->events |= POLLIN; + poll_check_ok(pop); + + return (0); +} + +/* + * Nothing to be done here. + */ + +static int +poll_del(struct event_base *base, int fd, short old, short events, void *idx_) +{ + struct pollop *pop = base->evbase; + struct pollfd *pfd = NULL; + struct pollidx *idx = idx_; + int i; + + EVUTIL_ASSERT((events & EV_SIGNAL) == 0); + if (!(events & (EV_READ|EV_WRITE))) + return (0); + + poll_check_ok(pop); + i = idx->idxplus1 - 1; + if (i < 0) + return (-1); + + /* Do we still want to read or write? */ + pfd = &pop->event_set[i]; + if (events & EV_READ) + pfd->events &= ~POLLIN; + if (events & EV_WRITE) + pfd->events &= ~POLLOUT; + poll_check_ok(pop); + if (pfd->events) + /* Another event cares about that fd. */ + return (0); + + /* Okay, so we aren't interested in that fd anymore. */ + idx->idxplus1 = 0; + + --pop->nfds; + if (i != pop->nfds) { + /* + * Shift the last pollfd down into the now-unoccupied + * position. + */ + memcpy(&pop->event_set[i], &pop->event_set[pop->nfds], + sizeof(struct pollfd)); + idx = evmap_io_get_fdinfo_(&base->io, pop->event_set[i].fd); + EVUTIL_ASSERT(idx); + EVUTIL_ASSERT(idx->idxplus1 == pop->nfds + 1); + idx->idxplus1 = i + 1; + } + + poll_check_ok(pop); + return (0); +} + +static void +poll_dealloc(struct event_base *base) +{ + struct pollop *pop = base->evbase; + + evsig_dealloc_(base); + if (pop->event_set) + mm_free(pop->event_set); + if (pop->event_set_copy) + mm_free(pop->event_set_copy); + + memset(pop, 0, sizeof(struct pollop)); + mm_free(pop); +} + +#endif /* EVENT__HAVE_POLL */ diff --git a/libs/libevent/docs/sample/dns-example.c b/libs/libevent/docs/sample/dns-example.c new file mode 100644 index 0000000000..fb705664aa --- /dev/null +++ b/libs/libevent/docs/sample/dns-example.c @@ -0,0 +1,257 @@ +/* + This example code shows how to use the high-level, low-level, and + server-level interfaces of evdns. + + XXX It's pretty ugly and should probably be cleaned up. + */ + +#include <event2/event-config.h> + +/* Compatibility for possible missing IPv6 declarations */ +#include "../ipv6-internal.h" + +#include <sys/types.h> + +#ifdef EVENT__HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef _WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#include <getopt.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif + +#include <event2/event.h> +#include <event2/dns.h> +#include <event2/dns_struct.h> +#include <event2/util.h> + +#ifdef EVENT__HAVE_NETINET_IN6_H +#include <netinet/in6.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define u32 ev_uint32_t +#define u8 ev_uint8_t + +static const char * +debug_ntoa(u32 address) +{ + static char buf[32]; + u32 a = ntohl(address); + evutil_snprintf(buf, sizeof(buf), "%d.%d.%d.%d", + (int)(u8)((a>>24)&0xff), + (int)(u8)((a>>16)&0xff), + (int)(u8)((a>>8 )&0xff), + (int)(u8)((a )&0xff)); + return buf; +} + +static void +main_callback(int result, char type, int count, int ttl, + void *addrs, void *orig) { + char *n = (char*)orig; + int i; + for (i = 0; i < count; ++i) { + if (type == DNS_IPv4_A) { + printf("%s: %s\n", n, debug_ntoa(((u32*)addrs)[i])); + } else if (type == DNS_PTR) { + printf("%s: %s\n", n, ((char**)addrs)[i]); + } + } + if (!count) { + printf("%s: No answer (%d)\n", n, result); + } + fflush(stdout); +} + +static void +gai_callback(int err, struct evutil_addrinfo *ai, void *arg) +{ + const char *name = arg; + int i; + if (err) { + printf("%s: %s\n", name, evutil_gai_strerror(err)); + } + if (ai && ai->ai_canonname) + printf(" %s ==> %s\n", name, ai->ai_canonname); + for (i=0; ai; ai = ai->ai_next, ++i) { + char buf[128]; + if (ai->ai_family == PF_INET) { + struct sockaddr_in *sin = + (struct sockaddr_in*)ai->ai_addr; + evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, + sizeof(buf)); + printf("[%d] %s: %s\n",i,name,buf); + } else { + struct sockaddr_in6 *sin6 = + (struct sockaddr_in6*)ai->ai_addr; + evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, + sizeof(buf)); + printf("[%d] %s: %s\n",i,name,buf); + } + } +} + +static void +evdns_server_callback(struct evdns_server_request *req, void *data) +{ + int i, r; + (void)data; + /* dummy; give 192.168.11.11 as an answer for all A questions, + * give foo.bar.example.com as an answer for all PTR questions. */ + for (i = 0; i < req->nquestions; ++i) { + u32 ans = htonl(0xc0a80b0bUL); + if (req->questions[i]->type == EVDNS_TYPE_A && + req->questions[i]->dns_question_class == EVDNS_CLASS_INET) { + printf(" -- replying for %s (A)\n", req->questions[i]->name); + r = evdns_server_request_add_a_reply(req, req->questions[i]->name, + 1, &ans, 10); + if (r<0) + printf("eeep, didn't work.\n"); + } else if (req->questions[i]->type == EVDNS_TYPE_PTR && + req->questions[i]->dns_question_class == EVDNS_CLASS_INET) { + printf(" -- replying for %s (PTR)\n", req->questions[i]->name); + r = evdns_server_request_add_ptr_reply(req, NULL, req->questions[i]->name, + "foo.bar.example.com", 10); + if (r<0) + printf("ugh, no luck"); + } else { + printf(" -- skipping %s [%d %d]\n", req->questions[i]->name, + req->questions[i]->type, req->questions[i]->dns_question_class); + } + } + + r = evdns_server_request_respond(req, 0); + if (r<0) + printf("eeek, couldn't send reply.\n"); +} + +static int verbose = 0; + +static void +logfn(int is_warn, const char *msg) { + if (!is_warn && !verbose) + return; + fprintf(stderr, "%s: %s\n", is_warn?"WARN":"INFO", msg); +} + +int +main(int c, char **v) { + struct options { + int reverse; + int use_getaddrinfo; + int servertest; + const char *resolv_conf; + const char *ns; + }; + struct options o; + char opt; + struct event_base *event_base = NULL; + struct evdns_base *evdns_base = NULL; + + memset(&o, 0, sizeof(o)); + + if (c < 2) { + fprintf(stderr, "syntax: %s [-x] [-v] [-c resolv.conf] [-s ns] hostname\n", v[0]); + fprintf(stderr, "syntax: %s [-T]\n", v[0]); + return 1; + } + + while ((opt = getopt(c, v, "xvc:Ts:")) != -1) { + switch (opt) { + case 'x': o.reverse = 1; break; + case 'v': ++verbose; break; + case 'g': o.use_getaddrinfo = 1; break; + case 'T': o.servertest = 1; break; + case 'c': o.resolv_conf = optarg; break; + case 's': o.ns = optarg; break; + default : fprintf(stderr, "Unknown option %c\n", opt); break; + } + } + +#ifdef _WIN32 + { + WSADATA WSAData; + WSAStartup(0x101, &WSAData); + } +#endif + + event_base = event_base_new(); + evdns_base = evdns_base_new(event_base, EVDNS_BASE_DISABLE_WHEN_INACTIVE); + evdns_set_log_fn(logfn); + + if (o.servertest) { + evutil_socket_t sock; + struct sockaddr_in my_addr; + sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock == -1) { + perror("socket"); + exit(1); + } + evutil_make_socket_nonblocking(sock); + my_addr.sin_family = AF_INET; + my_addr.sin_port = htons(10053); + my_addr.sin_addr.s_addr = INADDR_ANY; + if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr))<0) { + perror("bind"); + exit(1); + } + evdns_add_server_port_with_base(event_base, sock, 0, evdns_server_callback, NULL); + } + if (optind < c) { + int res; +#ifdef _WIN32 + if (o.resolv_conf == NULL && !o.ns) + res = evdns_base_config_windows_nameservers(evdns_base); + else +#endif + if (o.ns) + res = evdns_base_nameserver_ip_add(evdns_base, o.ns); + else + res = evdns_base_resolv_conf_parse(evdns_base, + DNS_OPTION_NAMESERVERS, o.resolv_conf); + + if (res < 0) { + fprintf(stderr, "Couldn't configure nameservers"); + return 1; + } + } + + printf("EVUTIL_AI_CANONNAME in example = %d\n", EVUTIL_AI_CANONNAME); + for (; optind < c; ++optind) { + if (o.reverse) { + struct in_addr addr; + if (evutil_inet_pton(AF_INET, v[optind], &addr)!=1) { + fprintf(stderr, "Skipping non-IP %s\n", v[optind]); + continue; + } + fprintf(stderr, "resolving %s...\n",v[optind]); + evdns_base_resolve_reverse(evdns_base, &addr, 0, main_callback, v[optind]); + } else if (o.use_getaddrinfo) { + struct evutil_addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = EVUTIL_AI_CANONNAME; + fprintf(stderr, "resolving (fwd) %s...\n",v[optind]); + evdns_getaddrinfo(evdns_base, v[optind], NULL, &hints, + gai_callback, v[optind]); + } else { + fprintf(stderr, "resolving (fwd) %s...\n",v[optind]); + evdns_base_resolve_ipv4(evdns_base, v[optind], 0, main_callback, v[optind]); + } + } + fflush(stdout); + event_base_dispatch(event_base); + return 0; +} + diff --git a/libs/libevent/docs/sample/event-read-fifo.c b/libs/libevent/docs/sample/event-read-fifo.c new file mode 100644 index 0000000000..27b0b530d5 --- /dev/null +++ b/libs/libevent/docs/sample/event-read-fifo.c @@ -0,0 +1,162 @@ +/* + * This sample code shows how to use Libevent to read from a named pipe. + * XXX This code could make better use of the Libevent interfaces. + * + * XXX This does not work on Windows; ignore everything inside the _WIN32 block. + * + * On UNIX, compile with: + * cc -I/usr/local/include -o event-read-fifo event-read-fifo.c \ + * -L/usr/local/lib -levent + */ + +#include <event2/event-config.h> + +#include <sys/types.h> +#include <sys/stat.h> +#ifndef _WIN32 +#include <sys/queue.h> +#include <unistd.h> +#include <sys/time.h> +#include <signal.h> +#else +#include <winsock2.h> +#include <windows.h> +#endif +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include <event2/event.h> + +static void +fifo_read(evutil_socket_t fd, short event, void *arg) +{ + char buf[255]; + int len; + struct event *ev = arg; +#ifdef _WIN32 + DWORD dwBytesRead; +#endif + + fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n", + (int)fd, event, arg); +#ifdef _WIN32 + len = ReadFile((HANDLE)fd, buf, sizeof(buf) - 1, &dwBytesRead, NULL); + + /* Check for end of file. */ + if (len && dwBytesRead == 0) { + fprintf(stderr, "End Of File"); + event_del(ev); + return; + } + + buf[dwBytesRead] = '\0'; +#else + len = read(fd, buf, sizeof(buf) - 1); + + if (len <= 0) { + if (len == -1) + perror("read"); + else if (len == 0) + fprintf(stderr, "Connection closed\n"); + event_del(ev); + event_base_loopbreak(event_get_base(ev)); + return; + } + + buf[len] = '\0'; +#endif + fprintf(stdout, "Read: %s\n", buf); +} + +/* On Unix, cleanup event.fifo if SIGINT is received. */ +#ifndef _WIN32 +static void +signal_cb(evutil_socket_t fd, short event, void *arg) +{ + struct event_base *base = arg; + event_base_loopbreak(base); +} +#endif + +int +main(int argc, char **argv) +{ + struct event *evfifo; + struct event_base* base; +#ifdef _WIN32 + HANDLE socket; + /* Open a file. */ + socket = CreateFileA("test.txt", /* open File */ + GENERIC_READ, /* open for reading */ + 0, /* do not share */ + NULL, /* no security */ + OPEN_EXISTING, /* existing file only */ + FILE_ATTRIBUTE_NORMAL, /* normal file */ + NULL); /* no attr. template */ + + if (socket == INVALID_HANDLE_VALUE) + return 1; + +#else + struct event *signal_int; + struct stat st; + const char *fifo = "event.fifo"; + int socket; + + if (lstat(fifo, &st) == 0) { + if ((st.st_mode & S_IFMT) == S_IFREG) { + errno = EEXIST; + perror("lstat"); + exit(1); + } + } + + unlink(fifo); + if (mkfifo(fifo, 0600) == -1) { + perror("mkfifo"); + exit(1); + } + + socket = open(fifo, O_RDONLY | O_NONBLOCK, 0); + + if (socket == -1) { + perror("open"); + exit(1); + } + + fprintf(stderr, "Write data to %s\n", fifo); +#endif + /* Initalize the event library */ + base = event_base_new(); + + /* Initalize one event */ +#ifdef _WIN32 + evfifo = event_new(base, (evutil_socket_t)socket, EV_READ|EV_PERSIST, fifo_read, + event_self_cbarg()); +#else + /* catch SIGINT so that event.fifo can be cleaned up */ + signal_int = evsignal_new(base, SIGINT, signal_cb, base); + event_add(signal_int, NULL); + + evfifo = event_new(base, socket, EV_READ|EV_PERSIST, fifo_read, + event_self_cbarg()); +#endif + + /* Add it to the active events, without a timeout */ + event_add(evfifo, NULL); + + event_base_dispatch(base); + event_base_free(base); +#ifdef _WIN32 + CloseHandle(socket); +#else + close(socket); + unlink(fifo); +#endif + libevent_global_shutdown(); + return (0); +} + diff --git a/libs/libevent/docs/sample/hello-world.c b/libs/libevent/docs/sample/hello-world.c new file mode 100644 index 0000000000..d3cf058a8b --- /dev/null +++ b/libs/libevent/docs/sample/hello-world.c @@ -0,0 +1,141 @@ +/* + This exmple program provides a trivial server program that listens for TCP + connections on port 9995. When they arrive, it writes a short message to + each client connection, and closes each connection once it is flushed. + + Where possible, it exits cleanly in response to a SIGINT (ctrl-c). +*/ + + +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <signal.h> +#ifndef _WIN32 +#include <netinet/in.h> +# ifdef _XOPEN_SOURCE_EXTENDED +# include <arpa/inet.h> +# endif +#include <sys/socket.h> +#endif + +#include <event2/bufferevent.h> +#include <event2/buffer.h> +#include <event2/listener.h> +#include <event2/util.h> +#include <event2/event.h> + +static const char MESSAGE[] = "Hello, World!\n"; + +static const int PORT = 9995; + +static void listener_cb(struct evconnlistener *, evutil_socket_t, + struct sockaddr *, int socklen, void *); +static void conn_writecb(struct bufferevent *, void *); +static void conn_eventcb(struct bufferevent *, short, void *); +static void signal_cb(evutil_socket_t, short, void *); + +int +main(int argc, char **argv) +{ + struct event_base *base; + struct evconnlistener *listener; + struct event *signal_event; + + struct sockaddr_in sin; +#ifdef _WIN32 + WSADATA wsa_data; + WSAStartup(0x0201, &wsa_data); +#endif + + base = event_base_new(); + if (!base) { + fprintf(stderr, "Could not initialize libevent!\n"); + return 1; + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(PORT); + + listener = evconnlistener_new_bind(base, listener_cb, (void *)base, + LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1, + (struct sockaddr*)&sin, + sizeof(sin)); + + if (!listener) { + fprintf(stderr, "Could not create a listener!\n"); + return 1; + } + + signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base); + + if (!signal_event || event_add(signal_event, NULL)<0) { + fprintf(stderr, "Could not create/add a signal event!\n"); + return 1; + } + + event_base_dispatch(base); + + evconnlistener_free(listener); + event_free(signal_event); + event_base_free(base); + + printf("done\n"); + return 0; +} + +static void +listener_cb(struct evconnlistener *listener, evutil_socket_t fd, + struct sockaddr *sa, int socklen, void *user_data) +{ + struct event_base *base = user_data; + struct bufferevent *bev; + + bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); + if (!bev) { + fprintf(stderr, "Error constructing bufferevent!"); + event_base_loopbreak(base); + return; + } + bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL); + bufferevent_enable(bev, EV_WRITE); + bufferevent_disable(bev, EV_READ); + + bufferevent_write(bev, MESSAGE, strlen(MESSAGE)); +} + +static void +conn_writecb(struct bufferevent *bev, void *user_data) +{ + struct evbuffer *output = bufferevent_get_output(bev); + if (evbuffer_get_length(output) == 0) { + printf("flushed answer\n"); + bufferevent_free(bev); + } +} + +static void +conn_eventcb(struct bufferevent *bev, short events, void *user_data) +{ + if (events & BEV_EVENT_EOF) { + printf("Connection closed.\n"); + } else if (events & BEV_EVENT_ERROR) { + printf("Got an error on the connection: %s\n", + strerror(errno));/*XXX win32*/ + } + /* None of the other events can happen here, since we haven't enabled + * timeouts */ + bufferevent_free(bev); +} + +static void +signal_cb(evutil_socket_t sig, short events, void *user_data) +{ + struct event_base *base = user_data; + struct timeval delay = { 2, 0 }; + + printf("Caught an interrupt signal; exiting cleanly in two seconds.\n"); + + event_base_loopexit(base, &delay); +} diff --git a/libs/libevent/docs/sample/hostcheck.c b/libs/libevent/docs/sample/hostcheck.c new file mode 100644 index 0000000000..50709369c0 --- /dev/null +++ b/libs/libevent/docs/sample/hostcheck.c @@ -0,0 +1,217 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* This file is an amalgamation of hostcheck.c and most of rawstr.c + from cURL. The contents of the COPYING file mentioned above are: + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1996 - 2013, Daniel Stenberg, <daniel@haxx.se>. + +All rights reserved. + +Permission to use, copy, modify, and distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright +notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization of the copyright holder. +*/ + +#include "hostcheck.h" +#include <string.h> + +/* Portable, consistent toupper (remember EBCDIC). Do not use toupper() because + its behavior is altered by the current locale. */ +static char Curl_raw_toupper(char in) +{ + switch (in) { + case 'a': + return 'A'; + case 'b': + return 'B'; + case 'c': + return 'C'; + case 'd': + return 'D'; + case 'e': + return 'E'; + case 'f': + return 'F'; + case 'g': + return 'G'; + case 'h': + return 'H'; + case 'i': + return 'I'; + case 'j': + return 'J'; + case 'k': + return 'K'; + case 'l': + return 'L'; + case 'm': + return 'M'; + case 'n': + return 'N'; + case 'o': + return 'O'; + case 'p': + return 'P'; + case 'q': + return 'Q'; + case 'r': + return 'R'; + case 's': + return 'S'; + case 't': + return 'T'; + case 'u': + return 'U'; + case 'v': + return 'V'; + case 'w': + return 'W'; + case 'x': + return 'X'; + case 'y': + return 'Y'; + case 'z': + return 'Z'; + } + return in; +} + +/* + * Curl_raw_equal() is for doing "raw" case insensitive strings. This is meant + * to be locale independent and only compare strings we know are safe for + * this. See http://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for + * some further explanation to why this function is necessary. + * + * The function is capable of comparing a-z case insensitively even for + * non-ascii. + */ + +static int Curl_raw_equal(const char *first, const char *second) +{ + while(*first && *second) { + if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) + /* get out of the loop as soon as they don't match */ + break; + first++; + second++; + } + /* we do the comparison here (possibly again), just to make sure that if the + loop above is skipped because one of the strings reached zero, we must not + return this as a successful match */ + return (Curl_raw_toupper(*first) == Curl_raw_toupper(*second)); +} + +static int Curl_raw_nequal(const char *first, const char *second, size_t max) +{ + while(*first && *second && max) { + if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) { + break; + } + max--; + first++; + second++; + } + if(0 == max) + return 1; /* they are equal this far */ + + return Curl_raw_toupper(*first) == Curl_raw_toupper(*second); +} + +/* + * Match a hostname against a wildcard pattern. + * E.g. + * "foo.host.com" matches "*.host.com". + * + * We use the matching rule described in RFC6125, section 6.4.3. + * http://tools.ietf.org/html/rfc6125#section-6.4.3 + */ + +static int hostmatch(const char *hostname, const char *pattern) +{ + const char *pattern_label_end, *pattern_wildcard, *hostname_label_end; + int wildcard_enabled; + size_t prefixlen, suffixlen; + pattern_wildcard = strchr(pattern, '*'); + if(pattern_wildcard == NULL) + return Curl_raw_equal(pattern, hostname) ? + CURL_HOST_MATCH : CURL_HOST_NOMATCH; + + /* We require at least 2 dots in pattern to avoid too wide wildcard + match. */ + wildcard_enabled = 1; + pattern_label_end = strchr(pattern, '.'); + if(pattern_label_end == NULL || strchr(pattern_label_end+1, '.') == NULL || + pattern_wildcard > pattern_label_end || + Curl_raw_nequal(pattern, "xn--", 4)) { + wildcard_enabled = 0; + } + if(!wildcard_enabled) + return Curl_raw_equal(pattern, hostname) ? + CURL_HOST_MATCH : CURL_HOST_NOMATCH; + + hostname_label_end = strchr(hostname, '.'); + if(hostname_label_end == NULL || + !Curl_raw_equal(pattern_label_end, hostname_label_end)) + return CURL_HOST_NOMATCH; + + /* The wildcard must match at least one character, so the left-most + label of the hostname is at least as large as the left-most label + of the pattern. */ + if(hostname_label_end - hostname < pattern_label_end - pattern) + return CURL_HOST_NOMATCH; + + prefixlen = pattern_wildcard - pattern; + suffixlen = pattern_label_end - (pattern_wildcard+1); + return Curl_raw_nequal(pattern, hostname, prefixlen) && + Curl_raw_nequal(pattern_wildcard+1, hostname_label_end - suffixlen, + suffixlen) ? + CURL_HOST_MATCH : CURL_HOST_NOMATCH; +} + +int Curl_cert_hostcheck(const char *match_pattern, const char *hostname) +{ + if(!match_pattern || !*match_pattern || + !hostname || !*hostname) /* sanity check */ + return 0; + + if(Curl_raw_equal(hostname, match_pattern)) /* trivial case */ + return 1; + + if(hostmatch(hostname,match_pattern) == CURL_HOST_MATCH) + return 1; + return 0; +} diff --git a/libs/libevent/docs/sample/hostcheck.h b/libs/libevent/docs/sample/hostcheck.h new file mode 100644 index 0000000000..f40bc43435 --- /dev/null +++ b/libs/libevent/docs/sample/hostcheck.h @@ -0,0 +1,30 @@ +#ifndef HEADER_CURL_HOSTCHECK_H +#define HEADER_CURL_HOSTCHECK_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#define CURL_HOST_NOMATCH 0 +#define CURL_HOST_MATCH 1 +int Curl_cert_hostcheck(const char *match_pattern, const char *hostname); + +#endif /* HEADER_CURL_HOSTCHECK_H */ + diff --git a/libs/libevent/docs/sample/http-connect.c b/libs/libevent/docs/sample/http-connect.c new file mode 100644 index 0000000000..d6c7b5ea0b --- /dev/null +++ b/libs/libevent/docs/sample/http-connect.c @@ -0,0 +1,119 @@ +#include "event2/event-config.h" + +#include <event2/event.h> +#include <event2/http.h> +#include <event2/http_struct.h> +#include <event2/buffer.h> +#include <stdlib.h> +#include <stdio.h> +#include <limits.h> + +#define VERIFY(cond) do { \ + if (!(cond)) { \ + fprintf(stderr, "[error] %s\n", #cond); \ + } \ +} while (0); \ + +#define URL_MAX 4096 + +struct connect_base +{ + struct evhttp_connection *evcon; + struct evhttp_uri *location; +}; + +static void get_cb(struct evhttp_request *req, void *arg) +{ + ev_ssize_t len; + struct evbuffer *evbuf; + + VERIFY(req); + + evbuf = evhttp_request_get_input_buffer(req); + len = evbuffer_get_length(evbuf); + fwrite(evbuffer_pullup(evbuf, len), len, 1, stdout); + evbuffer_drain(evbuf, len); +} + +static void connect_cb(struct evhttp_request *proxy_req, void *arg) +{ + char buffer[URL_MAX]; + + struct connect_base *base = arg; + struct evhttp_connection *evcon = base->evcon; + struct evhttp_uri *location = base->location; + + VERIFY(proxy_req); + if (evcon) { + struct evhttp_request *req = evhttp_request_new(get_cb, NULL); + evhttp_add_header(req->output_headers, "Connection", "close"); + VERIFY(!evhttp_make_request(evcon, req, EVHTTP_REQ_GET, + evhttp_uri_join(location, buffer, URL_MAX))); + } +} + +int main(int argc, const char **argv) +{ + char buffer[URL_MAX]; + + struct evhttp_uri *host_port; + struct evhttp_uri *location; + struct evhttp_uri *proxy; + + struct event_base *base; + struct evhttp_connection *evcon; + struct evhttp_request *req; + + struct connect_base connect_base; + + if (argc != 3) { + printf("Usage: %s proxy url\n", argv[0]); + return 1; + } + + { + proxy = evhttp_uri_parse(argv[1]); + VERIFY(evhttp_uri_get_host(proxy)); + VERIFY(evhttp_uri_get_port(proxy) > 0); + } + { + host_port = evhttp_uri_parse(argv[2]); + evhttp_uri_set_scheme(host_port, NULL); + evhttp_uri_set_userinfo(host_port, NULL); + evhttp_uri_set_path(host_port, NULL); + evhttp_uri_set_query(host_port, NULL); + evhttp_uri_set_fragment(host_port, NULL); + VERIFY(evhttp_uri_get_host(host_port)); + VERIFY(evhttp_uri_get_port(host_port) > 0); + } + { + location = evhttp_uri_parse(argv[2]); + evhttp_uri_set_scheme(location, NULL); + evhttp_uri_set_userinfo(location, 0); + evhttp_uri_set_host(location, NULL); + evhttp_uri_set_port(location, -1); + } + + VERIFY(base = event_base_new()); + VERIFY(evcon = evhttp_connection_base_new(base, NULL, + evhttp_uri_get_host(proxy), evhttp_uri_get_port(proxy))); + connect_base = (struct connect_base){ + .evcon = evcon, + .location = location, + }; + VERIFY(req = evhttp_request_new(connect_cb, &connect_base)); + + evhttp_add_header(req->output_headers, "Connection", "keep-alive"); + evhttp_add_header(req->output_headers, "Proxy-Connection", "keep-alive"); + evutil_snprintf(buffer, URL_MAX, "%s:%d", + evhttp_uri_get_host(host_port), evhttp_uri_get_port(host_port)); + evhttp_make_request(evcon, req, EVHTTP_REQ_CONNECT, buffer); + + event_base_dispatch(base); + evhttp_connection_free(evcon); + event_base_free(base); + evhttp_uri_free(proxy); + evhttp_uri_free(host_port); + evhttp_uri_free(location); + return 0; +} diff --git a/libs/libevent/docs/sample/http-server.c b/libs/libevent/docs/sample/http-server.c new file mode 100644 index 0000000000..cbb9c914dd --- /dev/null +++ b/libs/libevent/docs/sample/http-server.c @@ -0,0 +1,418 @@ +/* + A trivial static http webserver using Libevent's evhttp. + + This is not the best code in the world, and it does some fairly stupid stuff + that you would never want to do in a production webserver. Caveat hackor! + + */ + +/* Compatibility for possible missing IPv6 declarations */ +#include "../util-internal.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef _WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#include <windows.h> +#include <io.h> +#include <fcntl.h> +#ifndef S_ISDIR +#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR) +#endif +#else +#include <sys/stat.h> +#include <sys/socket.h> +#include <signal.h> +#include <fcntl.h> +#include <unistd.h> +#include <dirent.h> +#endif + +#include <event2/event.h> +#include <event2/http.h> +#include <event2/buffer.h> +#include <event2/util.h> +#include <event2/keyvalq_struct.h> + +#ifdef EVENT__HAVE_NETINET_IN_H +#include <netinet/in.h> +# ifdef _XOPEN_SOURCE_EXTENDED +# include <arpa/inet.h> +# endif +#endif + +#ifdef _WIN32 +#ifndef stat +#define stat _stat +#endif +#ifndef fstat +#define fstat _fstat +#endif +#ifndef open +#define open _open +#endif +#ifndef close +#define close _close +#endif +#ifndef O_RDONLY +#define O_RDONLY _O_RDONLY +#endif +#endif + +char uri_root[512]; + +static const struct table_entry { + const char *extension; + const char *content_type; +} content_type_table[] = { + { "txt", "text/plain" }, + { "c", "text/plain" }, + { "h", "text/plain" }, + { "html", "text/html" }, + { "htm", "text/htm" }, + { "css", "text/css" }, + { "gif", "image/gif" }, + { "jpg", "image/jpeg" }, + { "jpeg", "image/jpeg" }, + { "png", "image/png" }, + { "pdf", "application/pdf" }, + { "ps", "application/postsript" }, + { NULL, NULL }, +}; + +/* Try to guess a good content-type for 'path' */ +static const char * +guess_content_type(const char *path) +{ + const char *last_period, *extension; + const struct table_entry *ent; + last_period = strrchr(path, '.'); + if (!last_period || strchr(last_period, '/')) + goto not_found; /* no exension */ + extension = last_period + 1; + for (ent = &content_type_table[0]; ent->extension; ++ent) { + if (!evutil_ascii_strcasecmp(ent->extension, extension)) + return ent->content_type; + } + +not_found: + return "application/misc"; +} + +/* Callback used for the /dump URI, and for every non-GET request: + * dumps all information to stdout and gives back a trivial 200 ok */ +static void +dump_request_cb(struct evhttp_request *req, void *arg) +{ + const char *cmdtype; + struct evkeyvalq *headers; + struct evkeyval *header; + struct evbuffer *buf; + + switch (evhttp_request_get_command(req)) { + case EVHTTP_REQ_GET: cmdtype = "GET"; break; + case EVHTTP_REQ_POST: cmdtype = "POST"; break; + case EVHTTP_REQ_HEAD: cmdtype = "HEAD"; break; + case EVHTTP_REQ_PUT: cmdtype = "PUT"; break; + case EVHTTP_REQ_DELETE: cmdtype = "DELETE"; break; + case EVHTTP_REQ_OPTIONS: cmdtype = "OPTIONS"; break; + case EVHTTP_REQ_TRACE: cmdtype = "TRACE"; break; + case EVHTTP_REQ_CONNECT: cmdtype = "CONNECT"; break; + case EVHTTP_REQ_PATCH: cmdtype = "PATCH"; break; + default: cmdtype = "unknown"; break; + } + + printf("Received a %s request for %s\nHeaders:\n", + cmdtype, evhttp_request_get_uri(req)); + + headers = evhttp_request_get_input_headers(req); + for (header = headers->tqh_first; header; + header = header->next.tqe_next) { + printf(" %s: %s\n", header->key, header->value); + } + + buf = evhttp_request_get_input_buffer(req); + puts("Input data: <<<"); + while (evbuffer_get_length(buf)) { + int n; + char cbuf[128]; + n = evbuffer_remove(buf, cbuf, sizeof(cbuf)); + if (n > 0) + (void) fwrite(cbuf, 1, n, stdout); + } + puts(">>>"); + + evhttp_send_reply(req, 200, "OK", NULL); +} + +/* This callback gets invoked when we get any http request that doesn't match + * any other callback. Like any evhttp server callback, it has a simple job: + * it must eventually call evhttp_send_error() or evhttp_send_reply(). + */ +static void +send_document_cb(struct evhttp_request *req, void *arg) +{ + struct evbuffer *evb = NULL; + const char *docroot = arg; + const char *uri = evhttp_request_get_uri(req); + struct evhttp_uri *decoded = NULL; + const char *path; + char *decoded_path; + char *whole_path = NULL; + size_t len; + int fd = -1; + struct stat st; + + if (evhttp_request_get_command(req) != EVHTTP_REQ_GET) { + dump_request_cb(req, arg); + return; + } + + printf("Got a GET request for <%s>\n", uri); + + /* Decode the URI */ + decoded = evhttp_uri_parse(uri); + if (!decoded) { + printf("It's not a good URI. Sending BADREQUEST\n"); + evhttp_send_error(req, HTTP_BADREQUEST, 0); + return; + } + + /* Let's see what path the user asked for. */ + path = evhttp_uri_get_path(decoded); + if (!path) path = "/"; + + /* We need to decode it, to see what path the user really wanted. */ + decoded_path = evhttp_uridecode(path, 0, NULL); + if (decoded_path == NULL) + goto err; + /* Don't allow any ".."s in the path, to avoid exposing stuff outside + * of the docroot. This test is both overzealous and underzealous: + * it forbids aceptable paths like "/this/one..here", but it doesn't + * do anything to prevent symlink following." */ + if (strstr(decoded_path, "..")) + goto err; + + len = strlen(decoded_path)+strlen(docroot)+2; + if (!(whole_path = malloc(len))) { + perror("malloc"); + goto err; + } + evutil_snprintf(whole_path, len, "%s/%s", docroot, decoded_path); + + if (stat(whole_path, &st)<0) { + goto err; + } + + /* This holds the content we're sending. */ + evb = evbuffer_new(); + + if (S_ISDIR(st.st_mode)) { + /* If it's a directory, read the comments and make a little + * index page */ +#ifdef _WIN32 + HANDLE d; + WIN32_FIND_DATAA ent; + char *pattern; + size_t dirlen; +#else + DIR *d; + struct dirent *ent; +#endif + const char *trailing_slash = ""; + + if (!strlen(path) || path[strlen(path)-1] != '/') + trailing_slash = "/"; + +#ifdef _WIN32 + dirlen = strlen(whole_path); + pattern = malloc(dirlen+3); + memcpy(pattern, whole_path, dirlen); + pattern[dirlen] = '\\'; + pattern[dirlen+1] = '*'; + pattern[dirlen+2] = '\0'; + d = FindFirstFileA(pattern, &ent); + free(pattern); + if (d == INVALID_HANDLE_VALUE) + goto err; +#else + if (!(d = opendir(whole_path))) + goto err; +#endif + + evbuffer_add_printf(evb, + "<!DOCTYPE html>\n" + "<html>\n <head>\n" + " <meta charset='utf-8'>\n" + " <title>%s</title>\n" + " <base href='%s%s'>\n" + " </head>\n" + " <body>\n" + " <h1>%s</h1>\n" + " <ul>\n", + decoded_path, /* XXX html-escape this. */ + path, /* XXX html-escape this? */ + trailing_slash, + decoded_path /* XXX html-escape this */); +#ifdef _WIN32 + do { + const char *name = ent.cFileName; +#else + while ((ent = readdir(d))) { + const char *name = ent->d_name; +#endif + evbuffer_add_printf(evb, + " <li><a href=\"%s\">%s</a>\n", + name, name);/* XXX escape this */ +#ifdef _WIN32 + } while (FindNextFileA(d, &ent)); +#else + } +#endif + evbuffer_add_printf(evb, "</ul></body></html>\n"); +#ifdef _WIN32 + FindClose(d); +#else + closedir(d); +#endif + evhttp_add_header(evhttp_request_get_output_headers(req), + "Content-Type", "text/html"); + } else { + /* Otherwise it's a file; add it to the buffer to get + * sent via sendfile */ + const char *type = guess_content_type(decoded_path); + if ((fd = open(whole_path, O_RDONLY)) < 0) { + perror("open"); + goto err; + } + + if (fstat(fd, &st)<0) { + /* Make sure the length still matches, now that we + * opened the file :/ */ + perror("fstat"); + goto err; + } + evhttp_add_header(evhttp_request_get_output_headers(req), + "Content-Type", type); + evbuffer_add_file(evb, fd, 0, st.st_size); + } + + evhttp_send_reply(req, 200, "OK", evb); + goto done; +err: + evhttp_send_error(req, 404, "Document was not found"); + if (fd>=0) + close(fd); +done: + if (decoded) + evhttp_uri_free(decoded); + if (decoded_path) + free(decoded_path); + if (whole_path) + free(whole_path); + if (evb) + evbuffer_free(evb); +} + +static void +syntax(void) +{ + fprintf(stdout, "Syntax: http-server <docroot>\n"); +} + +int +main(int argc, char **argv) +{ + struct event_base *base; + struct evhttp *http; + struct evhttp_bound_socket *handle; + + unsigned short port = 0; +#ifdef _WIN32 + WSADATA WSAData; + WSAStartup(0x101, &WSAData); +#else + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + return (1); +#endif + if (argc < 2) { + syntax(); + return 1; + } + + base = event_base_new(); + if (!base) { + fprintf(stderr, "Couldn't create an event_base: exiting\n"); + return 1; + } + + /* Create a new evhttp object to handle requests. */ + http = evhttp_new(base); + if (!http) { + fprintf(stderr, "couldn't create evhttp. Exiting.\n"); + return 1; + } + + /* The /dump URI will dump all requests to stdout and say 200 ok. */ + evhttp_set_cb(http, "/dump", dump_request_cb, NULL); + + /* We want to accept arbitrary requests, so we need to set a "generic" + * cb. We can also add callbacks for specific paths. */ + evhttp_set_gencb(http, send_document_cb, argv[1]); + + /* Now we tell the evhttp what port to listen on */ + handle = evhttp_bind_socket_with_handle(http, "0.0.0.0", port); + if (!handle) { + fprintf(stderr, "couldn't bind to port %d. Exiting.\n", + (int)port); + return 1; + } + + { + /* Extract and display the address we're listening on. */ + struct sockaddr_storage ss; + evutil_socket_t fd; + ev_socklen_t socklen = sizeof(ss); + char addrbuf[128]; + void *inaddr; + const char *addr; + int got_port = -1; + fd = evhttp_bound_socket_get_fd(handle); + memset(&ss, 0, sizeof(ss)); + if (getsockname(fd, (struct sockaddr *)&ss, &socklen)) { + perror("getsockname() failed"); + return 1; + } + if (ss.ss_family == AF_INET) { + got_port = ntohs(((struct sockaddr_in*)&ss)->sin_port); + inaddr = &((struct sockaddr_in*)&ss)->sin_addr; + } else if (ss.ss_family == AF_INET6) { + got_port = ntohs(((struct sockaddr_in6*)&ss)->sin6_port); + inaddr = &((struct sockaddr_in6*)&ss)->sin6_addr; + } else { + fprintf(stderr, "Weird address family %d\n", + ss.ss_family); + return 1; + } + addr = evutil_inet_ntop(ss.ss_family, inaddr, addrbuf, + sizeof(addrbuf)); + if (addr) { + printf("Listening on %s:%d\n", addr, got_port); + evutil_snprintf(uri_root, sizeof(uri_root), + "http://%s:%d",addr,got_port); + } else { + fprintf(stderr, "evutil_inet_ntop failed\n"); + return 1; + } + } + + event_base_dispatch(base); + + return 0; +} diff --git a/libs/libevent/docs/sample/https-client.c b/libs/libevent/docs/sample/https-client.c new file mode 100644 index 0000000000..029cd19c75 --- /dev/null +++ b/libs/libevent/docs/sample/https-client.c @@ -0,0 +1,494 @@ +/* + This is an example of how to hook up evhttp with bufferevent_ssl + + It just GETs an https URL given on the command-line and prints the response + body to stdout. + + Actually, it also accepts plain http URLs to make it easy to compare http vs + https code paths. + + Loosely based on le-proxy.c. + */ + +// Get rid of OSX 10.7 and greater deprecation warnings. +#if defined(__APPLE__) && defined(__clang__) +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#ifdef _WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> + +#define snprintf _snprintf +#define strcasecmp _stricmp +#else +#include <sys/socket.h> +#include <netinet/in.h> +#endif + +#include <event2/bufferevent_ssl.h> +#include <event2/bufferevent.h> +#include <event2/buffer.h> +#include <event2/listener.h> +#include <event2/util.h> +#include <event2/http.h> + +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/rand.h> + +#include "openssl_hostname_validation.h" + +static struct event_base *base; +static int ignore_cert = 0; + +static void +http_request_done(struct evhttp_request *req, void *ctx) +{ + char buffer[256]; + int nread; + + if (req == NULL) { + /* If req is NULL, it means an error occurred, but + * sadly we are mostly left guessing what the error + * might have been. We'll do our best... */ + struct bufferevent *bev = (struct bufferevent *) ctx; + unsigned long oslerr; + int printed_err = 0; + int errcode = EVUTIL_SOCKET_ERROR(); + fprintf(stderr, "some request failed - no idea which one though!\n"); + /* Print out the OpenSSL error queue that libevent + * squirreled away for us, if any. */ + while ((oslerr = bufferevent_get_openssl_error(bev))) { + ERR_error_string_n(oslerr, buffer, sizeof(buffer)); + fprintf(stderr, "%s\n", buffer); + printed_err = 1; + } + /* If the OpenSSL error queue was empty, maybe it was a + * socket error; let's try printing that. */ + if (! printed_err) + fprintf(stderr, "socket error = %s (%d)\n", + evutil_socket_error_to_string(errcode), + errcode); + return; + } + + fprintf(stderr, "Response line: %d %s\n", + evhttp_request_get_response_code(req), + evhttp_request_get_response_code_line(req)); + + while ((nread = evbuffer_remove(evhttp_request_get_input_buffer(req), + buffer, sizeof(buffer))) + > 0) { + /* These are just arbitrary chunks of 256 bytes. + * They are not lines, so we can't treat them as such. */ + fwrite(buffer, nread, 1, stdout); + } +} + +static void +syntax(void) +{ + fputs("Syntax:\n", stderr); + fputs(" https-client -url <https-url> [-data data-file.bin] [-ignore-cert] [-retries num] [-timeout sec] [-crt crt]\n", stderr); + fputs("Example:\n", stderr); + fputs(" https-client -url https://ip.appspot.com/\n", stderr); +} + +static void +err(const char *msg) +{ + fputs(msg, stderr); +} + +static void +err_openssl(const char *func) +{ + fprintf (stderr, "%s failed:\n", func); + + /* This is the OpenSSL function that prints the contents of the + * error stack to the specified file handle. */ + ERR_print_errors_fp (stderr); + + exit(1); +} + +/* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */ +static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg) +{ + char cert_str[256]; + const char *host = (const char *) arg; + const char *res_str = "X509_verify_cert failed"; + HostnameValidationResult res = Error; + + /* This is the function that OpenSSL would call if we hadn't called + * SSL_CTX_set_cert_verify_callback(). Therefore, we are "wrapping" + * the default functionality, rather than replacing it. */ + int ok_so_far = 0; + + X509 *server_cert = NULL; + + if (ignore_cert) { + return 1; + } + + ok_so_far = X509_verify_cert(x509_ctx); + + server_cert = X509_STORE_CTX_get_current_cert(x509_ctx); + + if (ok_so_far) { + res = validate_hostname(host, server_cert); + + switch (res) { + case MatchFound: + res_str = "MatchFound"; + break; + case MatchNotFound: + res_str = "MatchNotFound"; + break; + case NoSANPresent: + res_str = "NoSANPresent"; + break; + case MalformedCertificate: + res_str = "MalformedCertificate"; + break; + case Error: + res_str = "Error"; + break; + default: + res_str = "WTF!"; + break; + } + } + + X509_NAME_oneline(X509_get_subject_name (server_cert), + cert_str, sizeof (cert_str)); + + if (res == MatchFound) { + printf("https server '%s' has this certificate, " + "which looks good to me:\n%s\n", + host, cert_str); + return 1; + } else { + printf("Got '%s' for hostname '%s' and certificate:\n%s\n", + res_str, host, cert_str); + return 0; + } +} + +int +main(int argc, char **argv) +{ + int r; + + struct evhttp_uri *http_uri = NULL; + const char *url = NULL, *data_file = NULL; + const char *crt = "/etc/ssl/certs/ca-certificates.crt"; + const char *scheme, *host, *path, *query; + char uri[256]; + int port; + int retries = 0; + int timeout = -1; + + SSL_CTX *ssl_ctx = NULL; + SSL *ssl = NULL; + struct bufferevent *bev; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req; + struct evkeyvalq *output_headers; + struct evbuffer *output_buffer; + + int i; + int ret = 0; + enum { HTTP, HTTPS } type = HTTP; + + for (i = 1; i < argc; i++) { + if (!strcmp("-url", argv[i])) { + if (i < argc - 1) { + url = argv[i + 1]; + } else { + syntax(); + goto error; + } + } else if (!strcmp("-crt", argv[i])) { + if (i < argc - 1) { + crt = argv[i + 1]; + } else { + syntax(); + goto error; + } + } else if (!strcmp("-ignore-cert", argv[i])) { + ignore_cert = 1; + } else if (!strcmp("-data", argv[i])) { + if (i < argc - 1) { + data_file = argv[i + 1]; + } else { + syntax(); + goto error; + } + } else if (!strcmp("-retries", argv[i])) { + if (i < argc - 1) { + retries = atoi(argv[i + 1]); + } else { + syntax(); + goto error; + } + } else if (!strcmp("-timeout", argv[i])) { + if (i < argc - 1) { + timeout = atoi(argv[i + 1]); + } else { + syntax(); + goto error; + } + } else if (!strcmp("-help", argv[i])) { + syntax(); + goto error; + } + } + + if (!url) { + syntax(); + goto error; + } + +#ifdef _WIN32 + { + WORD wVersionRequested; + WSADATA wsaData; + int err; + + wVersionRequested = MAKEWORD(2, 2); + + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + printf("WSAStartup failed with error: %d\n", err); + goto error; + } + } +#endif // _WIN32 + + http_uri = evhttp_uri_parse(url); + if (http_uri == NULL) { + err("malformed url"); + goto error; + } + + scheme = evhttp_uri_get_scheme(http_uri); + if (scheme == NULL || (strcasecmp(scheme, "https") != 0 && + strcasecmp(scheme, "http") != 0)) { + err("url must be http or https"); + goto error; + } + + host = evhttp_uri_get_host(http_uri); + if (host == NULL) { + err("url must have a host"); + goto error; + } + + port = evhttp_uri_get_port(http_uri); + if (port == -1) { + port = (strcasecmp(scheme, "http") == 0) ? 80 : 443; + } + + path = evhttp_uri_get_path(http_uri); + if (strlen(path) == 0) { + path = "/"; + } + + query = evhttp_uri_get_query(http_uri); + if (query == NULL) { + snprintf(uri, sizeof(uri) - 1, "%s", path); + } else { + snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query); + } + uri[sizeof(uri) - 1] = '\0'; + + // Initialize OpenSSL + SSL_library_init(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + + /* This isn't strictly necessary... OpenSSL performs RAND_poll + * automatically on first use of random number generator. */ + r = RAND_poll(); + if (r == 0) { + err_openssl("RAND_poll"); + goto error; + } + + /* Create a new OpenSSL context */ + ssl_ctx = SSL_CTX_new(SSLv23_method()); + if (!ssl_ctx) { + err_openssl("SSL_CTX_new"); + goto error; + } + +#ifndef _WIN32 + /* TODO: Add certificate loading on Windows as well */ + + /* Attempt to use the system's trusted root certificates. + * (This path is only valid for Debian-based systems.) */ + if (1 != SSL_CTX_load_verify_locations(ssl_ctx, crt, NULL)) { + err_openssl("SSL_CTX_load_verify_locations"); + goto error; + } + /* Ask OpenSSL to verify the server certificate. Note that this + * does NOT include verifying that the hostname is correct. + * So, by itself, this means anyone with any legitimate + * CA-issued certificate for any website, can impersonate any + * other website in the world. This is not good. See "The + * Most Dangerous Code in the World" article at + * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html + */ + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); + /* This is how we solve the problem mentioned in the previous + * comment. We "wrap" OpenSSL's validation routine in our + * own routine, which also validates the hostname by calling + * the code provided by iSECPartners. Note that even though + * the "Everything You've Always Wanted to Know About + * Certificate Validation With OpenSSL (But Were Afraid to + * Ask)" paper from iSECPartners says very explicitly not to + * call SSL_CTX_set_cert_verify_callback (at the bottom of + * page 2), what we're doing here is safe because our + * cert_verify_callback() calls X509_verify_cert(), which is + * OpenSSL's built-in routine which would have been called if + * we hadn't set the callback. Therefore, we're just + * "wrapping" OpenSSL's routine, not replacing it. */ + SSL_CTX_set_cert_verify_callback(ssl_ctx, cert_verify_callback, + (void *) host); +#endif // not _WIN32 + + // Create event base + base = event_base_new(); + if (!base) { + perror("event_base_new()"); + goto error; + } + + // Create OpenSSL bufferevent and stack evhttp on top of it + ssl = SSL_new(ssl_ctx); + if (ssl == NULL) { + err_openssl("SSL_new()"); + goto error; + } + + #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + // Set hostname for SNI extension + SSL_set_tlsext_host_name(ssl, host); + #endif + + if (strcasecmp(scheme, "http") == 0) { + bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); + } else { + type = HTTPS; + bev = bufferevent_openssl_socket_new(base, -1, ssl, + BUFFEREVENT_SSL_CONNECTING, + BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); + } + + if (bev == NULL) { + fprintf(stderr, "bufferevent_openssl_socket_new() failed\n"); + goto error; + } + + bufferevent_openssl_set_allow_dirty_shutdown(bev, 1); + + // For simplicity, we let DNS resolution block. Everything else should be + // asynchronous though. + evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev, + host, port); + if (evcon == NULL) { + fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n"); + goto error; + } + + if (retries > 0) { + evhttp_connection_set_retries(evcon, retries); + } + if (timeout >= 0) { + evhttp_connection_set_timeout(evcon, timeout); + } + + // Fire off the request + req = evhttp_request_new(http_request_done, bev); + if (req == NULL) { + fprintf(stderr, "evhttp_request_new() failed\n"); + goto error; + } + + output_headers = evhttp_request_get_output_headers(req); + evhttp_add_header(output_headers, "Host", host); + evhttp_add_header(output_headers, "Connection", "close"); + + if (data_file) { + /* NOTE: In production code, you'd probably want to use + * evbuffer_add_file() or evbuffer_add_file_segment(), to + * avoid needless copying. */ + FILE * f = fopen(data_file, "rb"); + char buf[1024]; + size_t s; + size_t bytes = 0; + + if (!f) { + syntax(); + goto error; + } + + output_buffer = evhttp_request_get_output_buffer(req); + while ((s = fread(buf, 1, sizeof(buf), f)) > 0) { + evbuffer_add(output_buffer, buf, s); + bytes += s; + } + evutil_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long)bytes); + evhttp_add_header(output_headers, "Content-Length", buf); + fclose(f); + } + + r = evhttp_make_request(evcon, req, data_file ? EVHTTP_REQ_POST : EVHTTP_REQ_GET, uri); + if (r != 0) { + fprintf(stderr, "evhttp_make_request() failed\n"); + goto error; + } + + event_base_dispatch(base); + goto cleanup; + +error: + ret = 1; +cleanup: + if (evcon) + evhttp_connection_free(evcon); + if (http_uri) + evhttp_uri_free(http_uri); + event_base_free(base); + + if (ssl_ctx) + SSL_CTX_free(ssl_ctx); + if (type == HTTP && ssl) + SSL_free(ssl); + EVP_cleanup(); + ERR_free_strings(); + +#ifdef EVENT__HAVE_ERR_REMOVE_THREAD_STATE + ERR_remove_thread_state(NULL); +#else + ERR_remove_state(0); +#endif + CRYPTO_cleanup_all_ex_data(); + + sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); + +#ifdef _WIN32 + WSACleanup(); +#endif + + return ret; +} diff --git a/libs/libevent/docs/sample/include.am b/libs/libevent/docs/sample/include.am new file mode 100644 index 0000000000..d1a7242f7c --- /dev/null +++ b/libs/libevent/docs/sample/include.am @@ -0,0 +1,53 @@ +# sample/include.am for libevent +# Copyright 2000-2007 Niels Provos +# Copyright 2007-2012 Niels Provos and Nick Mathewson +# +# See LICENSE for copying information. + +SAMPLES = \ + sample/dns-example \ + sample/event-read-fifo \ + sample/hello-world \ + sample/http-server \ + sample/http-connect \ + sample/signal-test \ + sample/time-test + +if OPENSSL +SAMPLES += sample/le-proxy +sample_le_proxy_SOURCES = sample/le-proxy.c +sample_le_proxy_LDADD = libevent.la libevent_openssl.la ${OPENSSL_LIBS} ${OPENSSL_LIBADD} +sample_le_proxy_INCLUDES = $(OPENSSL_INCS) + +SAMPLES += sample/https-client +sample_https_client_SOURCES = \ + sample/https-client.c \ + sample/hostcheck.c \ + sample/openssl_hostname_validation.c +sample_https_client_LDADD = libevent.la libevent_openssl.la ${OPENSSL_LIBS} ${OPENSSL_LIBADD} +sample_https_client_INCLUDES = $(OPENSSL_INCS) +noinst_HEADERS += \ + sample/hostcheck.h \ + sample/openssl_hostname_validation.h +endif + +if BUILD_SAMPLES +noinst_PROGRAMS += $(SAMPLES) +endif + +$(SAMPLES) : libevent.la + +sample_event_read_fifo_SOURCES = sample/event-read-fifo.c +sample_event_read_fifo_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la +sample_time_test_SOURCES = sample/time-test.c +sample_time_test_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la +sample_signal_test_SOURCES = sample/signal-test.c +sample_signal_test_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la +sample_dns_example_SOURCES = sample/dns-example.c +sample_dns_example_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la +sample_hello_world_SOURCES = sample/hello-world.c +sample_hello_world_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la +sample_http_server_SOURCES = sample/http-server.c +sample_http_server_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la +sample_http_connect_SOURCES = sample/http-connect.c +sample_http_connect_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la diff --git a/libs/libevent/docs/sample/le-proxy.c b/libs/libevent/docs/sample/le-proxy.c new file mode 100644 index 0000000000..30e0a5f6b9 --- /dev/null +++ b/libs/libevent/docs/sample/le-proxy.c @@ -0,0 +1,288 @@ +/* + This example code shows how to write an (optionally encrypting) SSL proxy + with Libevent's bufferevent layer. + + XXX It's a little ugly and should probably be cleaned up. + */ + +// Get rid of OSX 10.7 and greater deprecation warnings. +#if defined(__APPLE__) && defined(__clang__) +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#ifdef _WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#endif + +#include <event2/bufferevent_ssl.h> +#include <event2/bufferevent.h> +#include <event2/buffer.h> +#include <event2/listener.h> +#include <event2/util.h> + +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/rand.h> + +static struct event_base *base; +static struct sockaddr_storage listen_on_addr; +static struct sockaddr_storage connect_to_addr; +static int connect_to_addrlen; +static int use_wrapper = 1; + +static SSL_CTX *ssl_ctx = NULL; + +#define MAX_OUTPUT (512*1024) + +static void drained_writecb(struct bufferevent *bev, void *ctx); +static void eventcb(struct bufferevent *bev, short what, void *ctx); + +static void +readcb(struct bufferevent *bev, void *ctx) +{ + struct bufferevent *partner = ctx; + struct evbuffer *src, *dst; + size_t len; + src = bufferevent_get_input(bev); + len = evbuffer_get_length(src); + if (!partner) { + evbuffer_drain(src, len); + return; + } + dst = bufferevent_get_output(partner); + evbuffer_add_buffer(dst, src); + + if (evbuffer_get_length(dst) >= MAX_OUTPUT) { + /* We're giving the other side data faster than it can + * pass it on. Stop reading here until we have drained the + * other side to MAX_OUTPUT/2 bytes. */ + bufferevent_setcb(partner, readcb, drained_writecb, + eventcb, bev); + bufferevent_setwatermark(partner, EV_WRITE, MAX_OUTPUT/2, + MAX_OUTPUT); + bufferevent_disable(bev, EV_READ); + } +} + +static void +drained_writecb(struct bufferevent *bev, void *ctx) +{ + struct bufferevent *partner = ctx; + + /* We were choking the other side until we drained our outbuf a bit. + * Now it seems drained. */ + bufferevent_setcb(bev, readcb, NULL, eventcb, partner); + bufferevent_setwatermark(bev, EV_WRITE, 0, 0); + if (partner) + bufferevent_enable(partner, EV_READ); +} + +static void +close_on_finished_writecb(struct bufferevent *bev, void *ctx) +{ + struct evbuffer *b = bufferevent_get_output(bev); + + if (evbuffer_get_length(b) == 0) { + bufferevent_free(bev); + } +} + +static void +eventcb(struct bufferevent *bev, short what, void *ctx) +{ + struct bufferevent *partner = ctx; + + if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { + if (what & BEV_EVENT_ERROR) { + unsigned long err; + while ((err = (bufferevent_get_openssl_error(bev)))) { + const char *msg = (const char*) + ERR_reason_error_string(err); + const char *lib = (const char*) + ERR_lib_error_string(err); + const char *func = (const char*) + ERR_func_error_string(err); + fprintf(stderr, + "%s in %s %s\n", msg, lib, func); + } + if (errno) + perror("connection error"); + } + + if (partner) { + /* Flush all pending data */ + readcb(bev, ctx); + + if (evbuffer_get_length( + bufferevent_get_output(partner))) { + /* We still have to flush data from the other + * side, but when that's done, close the other + * side. */ + bufferevent_setcb(partner, + NULL, close_on_finished_writecb, + eventcb, NULL); + bufferevent_disable(partner, EV_READ); + } else { + /* We have nothing left to say to the other + * side; close it. */ + bufferevent_free(partner); + } + } + bufferevent_free(bev); + } +} + +static void +syntax(void) +{ + fputs("Syntax:\n", stderr); + fputs(" le-proxy [-s] [-W] <listen-on-addr> <connect-to-addr>\n", stderr); + fputs("Example:\n", stderr); + fputs(" le-proxy 127.0.0.1:8888 1.2.3.4:80\n", stderr); + + exit(1); +} + +static void +accept_cb(struct evconnlistener *listener, evutil_socket_t fd, + struct sockaddr *a, int slen, void *p) +{ + struct bufferevent *b_out, *b_in; + /* Create two linked bufferevent objects: one to connect, one for the + * new connection */ + b_in = bufferevent_socket_new(base, fd, + BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); + + if (!ssl_ctx || use_wrapper) + b_out = bufferevent_socket_new(base, -1, + BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); + else { + SSL *ssl = SSL_new(ssl_ctx); + b_out = bufferevent_openssl_socket_new(base, -1, ssl, + BUFFEREVENT_SSL_CONNECTING, + BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); + } + + assert(b_in && b_out); + + if (bufferevent_socket_connect(b_out, + (struct sockaddr*)&connect_to_addr, connect_to_addrlen)<0) { + perror("bufferevent_socket_connect"); + bufferevent_free(b_out); + bufferevent_free(b_in); + return; + } + + if (ssl_ctx && use_wrapper) { + struct bufferevent *b_ssl; + SSL *ssl = SSL_new(ssl_ctx); + b_ssl = bufferevent_openssl_filter_new(base, + b_out, ssl, BUFFEREVENT_SSL_CONNECTING, + BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); + if (!b_ssl) { + perror("Bufferevent_openssl_new"); + bufferevent_free(b_out); + bufferevent_free(b_in); + } + b_out = b_ssl; + } + + bufferevent_setcb(b_in, readcb, NULL, eventcb, b_out); + bufferevent_setcb(b_out, readcb, NULL, eventcb, b_in); + + bufferevent_enable(b_in, EV_READ|EV_WRITE); + bufferevent_enable(b_out, EV_READ|EV_WRITE); +} + +int +main(int argc, char **argv) +{ + int i; + int socklen; + + int use_ssl = 0; + struct evconnlistener *listener; + + if (argc < 3) + syntax(); + + for (i=1; i < argc; ++i) { + if (!strcmp(argv[i], "-s")) { + use_ssl = 1; + } else if (!strcmp(argv[i], "-W")) { + use_wrapper = 0; + } else if (argv[i][0] == '-') { + syntax(); + } else + break; + } + + if (i+2 != argc) + syntax(); + + memset(&listen_on_addr, 0, sizeof(listen_on_addr)); + socklen = sizeof(listen_on_addr); + if (evutil_parse_sockaddr_port(argv[i], + (struct sockaddr*)&listen_on_addr, &socklen)<0) { + int p = atoi(argv[i]); + struct sockaddr_in *sin = (struct sockaddr_in*)&listen_on_addr; + if (p < 1 || p > 65535) + syntax(); + sin->sin_port = htons(p); + sin->sin_addr.s_addr = htonl(0x7f000001); + sin->sin_family = AF_INET; + socklen = sizeof(struct sockaddr_in); + } + + memset(&connect_to_addr, 0, sizeof(connect_to_addr)); + connect_to_addrlen = sizeof(connect_to_addr); + if (evutil_parse_sockaddr_port(argv[i+1], + (struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0) + syntax(); + + base = event_base_new(); + if (!base) { + perror("event_base_new()"); + return 1; + } + + if (use_ssl) { + int r; + SSL_library_init(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + r = RAND_poll(); + if (r == 0) { + fprintf(stderr, "RAND_poll() failed.\n"); + return 1; + } + ssl_ctx = SSL_CTX_new(SSLv23_method()); + } + + listener = evconnlistener_new_bind(base, accept_cb, NULL, + LEV_OPT_CLOSE_ON_FREE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_REUSEABLE, + -1, (struct sockaddr*)&listen_on_addr, socklen); + + if (! listener) { + fprintf(stderr, "Couldn't open listener.\n"); + event_base_free(base); + return 1; + } + event_base_dispatch(base); + + evconnlistener_free(listener); + event_base_free(base); + + return 0; +} diff --git a/libs/libevent/docs/sample/openssl_hostname_validation.c b/libs/libevent/docs/sample/openssl_hostname_validation.c new file mode 100644 index 0000000000..00e63d1e15 --- /dev/null +++ b/libs/libevent/docs/sample/openssl_hostname_validation.c @@ -0,0 +1,173 @@ +/* Obtained from: https://github.com/iSECPartners/ssl-conservatory */ + +/* +Copyright (C) 2012, iSEC Partners. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +/* + * Helper functions to perform basic hostname validation using OpenSSL. + * + * Please read "everything-you-wanted-to-know-about-openssl.pdf" before + * attempting to use this code. This whitepaper describes how the code works, + * how it should be used, and what its limitations are. + * + * Author: Alban Diquet + * License: See LICENSE + * + */ + +// Get rid of OSX 10.7 and greater deprecation warnings. +#if defined(__APPLE__) && defined(__clang__) +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + +#include <openssl/x509v3.h> +#include <openssl/ssl.h> +#include <string.h> + +#include "openssl_hostname_validation.h" +#include "hostcheck.h" + +#define HOSTNAME_MAX_SIZE 255 + +/** +* Tries to find a match for hostname in the certificate's Common Name field. +* +* Returns MatchFound if a match was found. +* Returns MatchNotFound if no matches were found. +* Returns MalformedCertificate if the Common Name had a NUL character embedded in it. +* Returns Error if the Common Name could not be extracted. +*/ +static HostnameValidationResult matches_common_name(const char *hostname, const X509 *server_cert) { + int common_name_loc = -1; + X509_NAME_ENTRY *common_name_entry = NULL; + ASN1_STRING *common_name_asn1 = NULL; + char *common_name_str = NULL; + + // Find the position of the CN field in the Subject field of the certificate + common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name((X509 *) server_cert), NID_commonName, -1); + if (common_name_loc < 0) { + return Error; + } + + // Extract the CN field + common_name_entry = X509_NAME_get_entry(X509_get_subject_name((X509 *) server_cert), common_name_loc); + if (common_name_entry == NULL) { + return Error; + } + + // Convert the CN field to a C string + common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry); + if (common_name_asn1 == NULL) { + return Error; + } + common_name_str = (char *) ASN1_STRING_data(common_name_asn1); + + // Make sure there isn't an embedded NUL character in the CN + if ((size_t)ASN1_STRING_length(common_name_asn1) != strlen(common_name_str)) { + return MalformedCertificate; + } + + // Compare expected hostname with the CN + if (Curl_cert_hostcheck(common_name_str, hostname) == CURL_HOST_MATCH) { + return MatchFound; + } + else { + return MatchNotFound; + } +} + + +/** +* Tries to find a match for hostname in the certificate's Subject Alternative Name extension. +* +* Returns MatchFound if a match was found. +* Returns MatchNotFound if no matches were found. +* Returns MalformedCertificate if any of the hostnames had a NUL character embedded in it. +* Returns NoSANPresent if the SAN extension was not present in the certificate. +*/ +static HostnameValidationResult matches_subject_alternative_name(const char *hostname, const X509 *server_cert) { + HostnameValidationResult result = MatchNotFound; + int i; + int san_names_nb = -1; + STACK_OF(GENERAL_NAME) *san_names = NULL; + + // Try to extract the names within the SAN extension from the certificate + san_names = X509_get_ext_d2i((X509 *) server_cert, NID_subject_alt_name, NULL, NULL); + if (san_names == NULL) { + return NoSANPresent; + } + san_names_nb = sk_GENERAL_NAME_num(san_names); + + // Check each name within the extension + for (i=0; i<san_names_nb; i++) { + const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i); + + if (current_name->type == GEN_DNS) { + // Current name is a DNS name, let's check it + char *dns_name = (char *) ASN1_STRING_data(current_name->d.dNSName); + + // Make sure there isn't an embedded NUL character in the DNS name + if ((size_t)ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) { + result = MalformedCertificate; + break; + } + else { // Compare expected hostname with the DNS name + if (Curl_cert_hostcheck(dns_name, hostname) + == CURL_HOST_MATCH) { + result = MatchFound; + break; + } + } + } + } + sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); + + return result; +} + + +/** +* Validates the server's identity by looking for the expected hostname in the +* server's certificate. As described in RFC 6125, it first tries to find a match +* in the Subject Alternative Name extension. If the extension is not present in +* the certificate, it checks the Common Name instead. +* +* Returns MatchFound if a match was found. +* Returns MatchNotFound if no matches were found. +* Returns MalformedCertificate if any of the hostnames had a NUL character embedded in it. +* Returns Error if there was an error. +*/ +HostnameValidationResult validate_hostname(const char *hostname, const X509 *server_cert) { + HostnameValidationResult result; + + if((hostname == NULL) || (server_cert == NULL)) + return Error; + + // First try the Subject Alternative Names extension + result = matches_subject_alternative_name(hostname, server_cert); + if (result == NoSANPresent) { + // Extension was not found: try the Common Name + result = matches_common_name(hostname, server_cert); + } + + return result; +} diff --git a/libs/libevent/docs/sample/openssl_hostname_validation.h b/libs/libevent/docs/sample/openssl_hostname_validation.h new file mode 100644 index 0000000000..54aa1c436d --- /dev/null +++ b/libs/libevent/docs/sample/openssl_hostname_validation.h @@ -0,0 +1,56 @@ +/* Obtained from: https://github.com/iSECPartners/ssl-conservatory */ + +/* +Copyright (C) 2012, iSEC Partners. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +/* + * Helper functions to perform basic hostname validation using OpenSSL. + * + * Please read "everything-you-wanted-to-know-about-openssl.pdf" before + * attempting to use this code. This whitepaper describes how the code works, + * how it should be used, and what its limitations are. + * + * Author: Alban Diquet + * License: See LICENSE + * + */ + +typedef enum { + MatchFound, + MatchNotFound, + NoSANPresent, + MalformedCertificate, + Error +} HostnameValidationResult; + +/** +* Validates the server's identity by looking for the expected hostname in the +* server's certificate. As described in RFC 6125, it first tries to find a match +* in the Subject Alternative Name extension. If the extension is not present in +* the certificate, it checks the Common Name instead. +* +* Returns MatchFound if a match was found. +* Returns MatchNotFound if no matches were found. +* Returns MalformedCertificate if any of the hostnames had a NUL character embedded in it. +* Returns Error if there was an error. +*/ +HostnameValidationResult validate_hostname(const char *hostname, const X509 *server_cert); diff --git a/libs/libevent/docs/sample/signal-test.c b/libs/libevent/docs/sample/signal-test.c new file mode 100644 index 0000000000..a61642f325 --- /dev/null +++ b/libs/libevent/docs/sample/signal-test.c @@ -0,0 +1,75 @@ +/* + * Compile with: + * cc -I/usr/local/include -o signal-test \ + * signal-test.c -L/usr/local/lib -levent + */ + +#include <sys/types.h> + +#include <event2/event-config.h> + +#include <sys/stat.h> +#ifndef _WIN32 +#include <sys/queue.h> +#include <unistd.h> +#include <sys/time.h> +#else +#include <winsock2.h> +#include <windows.h> +#endif +#include <signal.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include <event2/event.h> + +#ifdef EVENT____func__ +#define __func__ EVENT____func__ +#endif + +int called = 0; + +static void +signal_cb(evutil_socket_t fd, short event, void *arg) +{ + struct event *signal = arg; + + printf("%s: got signal %d\n", __func__, event_get_signal(signal)); + + if (called >= 2) + event_del(signal); + + called++; +} + +int +main(int argc, char **argv) +{ + struct event *signal_int; + struct event_base* base; +#ifdef _WIN32 + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 2); + + (void) WSAStartup(wVersionRequested, &wsaData); +#endif + + /* Initalize the event library */ + base = event_base_new(); + + /* Initalize one event */ + signal_int = evsignal_new(base, SIGINT, signal_cb, event_self_cbarg()); + + event_add(signal_int, NULL); + + event_base_dispatch(base); + event_base_free(base); + + return (0); +} + diff --git a/libs/libevent/docs/sample/time-test.c b/libs/libevent/docs/sample/time-test.c new file mode 100644 index 0000000000..c94c18a500 --- /dev/null +++ b/libs/libevent/docs/sample/time-test.c @@ -0,0 +1,107 @@ +/* + * XXX This sample code was once meant to show how to use the basic Libevent + * interfaces, but it never worked on non-Unix platforms, and some of the + * interfaces have changed since it was first written. It should probably + * be removed or replaced with something better. + * + * Compile with: + * cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent + */ + +#include <sys/types.h> + +#include <event2/event-config.h> + +#include <sys/stat.h> +#ifndef _WIN32 +#include <sys/queue.h> +#include <unistd.h> +#endif +#include <time.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include <event2/event.h> +#include <event2/event_struct.h> +#include <event2/util.h> + +#ifdef _WIN32 +#include <winsock2.h> +#endif + +struct timeval lasttime; + +int event_is_persistent; + +static void +timeout_cb(evutil_socket_t fd, short event, void *arg) +{ + struct timeval newtime, difference; + struct event *timeout = arg; + double elapsed; + + evutil_gettimeofday(&newtime, NULL); + evutil_timersub(&newtime, &lasttime, &difference); + elapsed = difference.tv_sec + + (difference.tv_usec / 1.0e6); + + printf("timeout_cb called at %d: %.3f seconds elapsed.\n", + (int)newtime.tv_sec, elapsed); + lasttime = newtime; + + if (! event_is_persistent) { + struct timeval tv; + evutil_timerclear(&tv); + tv.tv_sec = 2; + event_add(timeout, &tv); + } +} + +int +main(int argc, char **argv) +{ + struct event timeout; + struct timeval tv; + struct event_base *base; + int flags; + +#ifdef _WIN32 + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 2); + + (void)WSAStartup(wVersionRequested, &wsaData); +#endif + + if (argc == 2 && !strcmp(argv[1], "-p")) { + event_is_persistent = 1; + flags = EV_PERSIST; + } else { + event_is_persistent = 0; + flags = 0; + } + + /* Initalize the event library */ + base = event_base_new(); + + /* Initalize one event */ + event_assign(&timeout, base, -1, flags, timeout_cb, (void*) &timeout); + + evutil_timerclear(&tv); + tv.tv_sec = 2; + event_add(&timeout, &tv); + + evutil_gettimeofday(&lasttime, NULL); + + event_base_dispatch(base); + + return (0); +} + diff --git a/libs/libevent/docs/select.c b/libs/libevent/docs/select.c new file mode 100644 index 0000000000..8ae53cc11e --- /dev/null +++ b/libs/libevent/docs/select.c @@ -0,0 +1,346 @@ +/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ + +/* + * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "event2/event-config.h" +#include "evconfig-private.h" + +#ifdef EVENT__HAVE_SELECT + +#ifdef __APPLE__ +/* Apple wants us to define this if we might ever pass more than + * FD_SETSIZE bits to select(). */ +#define _DARWIN_UNLIMITED_SELECT +#endif + +#include <sys/types.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef EVENT__HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#include <sys/queue.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include "event-internal.h" +#include "evsignal-internal.h" +#include "event2/thread.h" +#include "evthread-internal.h" +#include "log-internal.h" +#include "evmap-internal.h" + +#ifndef EVENT__HAVE_FD_MASK +/* This type is mandatory, but Android doesn't define it. */ +typedef unsigned long fd_mask; +#endif + +#ifndef NFDBITS +#define NFDBITS (sizeof(fd_mask)*8) +#endif + +/* Divide positive x by y, rounding up. */ +#define DIV_ROUNDUP(x, y) (((x)+((y)-1))/(y)) + +/* How many bytes to allocate for N fds? */ +#define SELECT_ALLOC_SIZE(n) \ + (DIV_ROUNDUP(n, NFDBITS) * sizeof(fd_mask)) + +struct selectop { + int event_fds; /* Highest fd in fd set */ + int event_fdsz; + int resize_out_sets; + fd_set *event_readset_in; + fd_set *event_writeset_in; + fd_set *event_readset_out; + fd_set *event_writeset_out; +}; + +static void *select_init(struct event_base *); +static int select_add(struct event_base *, int, short old, short events, void*); +static int select_del(struct event_base *, int, short old, short events, void*); +static int select_dispatch(struct event_base *, struct timeval *); +static void select_dealloc(struct event_base *); + +const struct eventop selectops = { + "select", + select_init, + select_add, + select_del, + select_dispatch, + select_dealloc, + 0, /* doesn't need reinit. */ + EV_FEATURE_FDS, + 0, +}; + +static int select_resize(struct selectop *sop, int fdsz); +static void select_free_selectop(struct selectop *sop); + +static void * +select_init(struct event_base *base) +{ + struct selectop *sop; + + if (!(sop = mm_calloc(1, sizeof(struct selectop)))) + return (NULL); + + if (select_resize(sop, SELECT_ALLOC_SIZE(32 + 1))) { + select_free_selectop(sop); + return (NULL); + } + + evsig_init_(base); + + evutil_weakrand_seed_(&base->weakrand_seed, 0); + + return (sop); +} + +#ifdef CHECK_INVARIANTS +static void +check_selectop(struct selectop *sop) +{ + /* nothing to be done here */ +} +#else +#define check_selectop(sop) do { (void) sop; } while (0) +#endif + +static int +select_dispatch(struct event_base *base, struct timeval *tv) +{ + int res=0, i, j, nfds; + struct selectop *sop = base->evbase; + + check_selectop(sop); + if (sop->resize_out_sets) { + fd_set *readset_out=NULL, *writeset_out=NULL; + size_t sz = sop->event_fdsz; + if (!(readset_out = mm_realloc(sop->event_readset_out, sz))) + return (-1); + sop->event_readset_out = readset_out; + if (!(writeset_out = mm_realloc(sop->event_writeset_out, sz))) { + /* We don't free readset_out here, since it was + * already successfully reallocated. The next time + * we call select_dispatch, the realloc will be a + * no-op. */ + return (-1); + } + sop->event_writeset_out = writeset_out; + sop->resize_out_sets = 0; + } + + memcpy(sop->event_readset_out, sop->event_readset_in, + sop->event_fdsz); + memcpy(sop->event_writeset_out, sop->event_writeset_in, + sop->event_fdsz); + + nfds = sop->event_fds+1; + + EVBASE_RELEASE_LOCK(base, th_base_lock); + + res = select(nfds, sop->event_readset_out, + sop->event_writeset_out, NULL, tv); + + EVBASE_ACQUIRE_LOCK(base, th_base_lock); + + check_selectop(sop); + + if (res == -1) { + if (errno != EINTR) { + event_warn("select"); + return (-1); + } + + return (0); + } + + event_debug(("%s: select reports %d", __func__, res)); + + check_selectop(sop); + i = evutil_weakrand_range_(&base->weakrand_seed, nfds); + for (j = 0; j < nfds; ++j) { + if (++i >= nfds) + i = 0; + res = 0; + if (FD_ISSET(i, sop->event_readset_out)) + res |= EV_READ; + if (FD_ISSET(i, sop->event_writeset_out)) + res |= EV_WRITE; + + if (res == 0) + continue; + + evmap_io_active_(base, i, res); + } + check_selectop(sop); + + return (0); +} + +static int +select_resize(struct selectop *sop, int fdsz) +{ + fd_set *readset_in = NULL; + fd_set *writeset_in = NULL; + + if (sop->event_readset_in) + check_selectop(sop); + + if ((readset_in = mm_realloc(sop->event_readset_in, fdsz)) == NULL) + goto error; + sop->event_readset_in = readset_in; + if ((writeset_in = mm_realloc(sop->event_writeset_in, fdsz)) == NULL) { + /* Note that this will leave event_readset_in expanded. + * That's okay; we wouldn't want to free it, since that would + * change the semantics of select_resize from "expand the + * readset_in and writeset_in, or return -1" to "expand the + * *set_in members, or trash them and return -1." + */ + goto error; + } + sop->event_writeset_in = writeset_in; + sop->resize_out_sets = 1; + + memset((char *)sop->event_readset_in + sop->event_fdsz, 0, + fdsz - sop->event_fdsz); + memset((char *)sop->event_writeset_in + sop->event_fdsz, 0, + fdsz - sop->event_fdsz); + + sop->event_fdsz = fdsz; + check_selectop(sop); + + return (0); + + error: + event_warn("malloc"); + return (-1); +} + + +static int +select_add(struct event_base *base, int fd, short old, short events, void *p) +{ + struct selectop *sop = base->evbase; + (void) p; + + EVUTIL_ASSERT((events & EV_SIGNAL) == 0); + check_selectop(sop); + /* + * Keep track of the highest fd, so that we can calculate the size + * of the fd_sets for select(2) + */ + if (sop->event_fds < fd) { + int fdsz = sop->event_fdsz; + + if (fdsz < (int)sizeof(fd_mask)) + fdsz = (int)sizeof(fd_mask); + + /* In theory we should worry about overflow here. In + * reality, though, the highest fd on a unixy system will + * not overflow here. XXXX */ + while (fdsz < (int) SELECT_ALLOC_SIZE(fd + 1)) + fdsz *= 2; + + if (fdsz != sop->event_fdsz) { + if (select_resize(sop, fdsz)) { + check_selectop(sop); + return (-1); + } + } + + sop->event_fds = fd; + } + + if (events & EV_READ) + FD_SET(fd, sop->event_readset_in); + if (events & EV_WRITE) + FD_SET(fd, sop->event_writeset_in); + check_selectop(sop); + + return (0); +} + +/* + * Nothing to be done here. + */ + +static int +select_del(struct event_base *base, int fd, short old, short events, void *p) +{ + struct selectop *sop = base->evbase; + (void)p; + + EVUTIL_ASSERT((events & EV_SIGNAL) == 0); + check_selectop(sop); + + if (sop->event_fds < fd) { + check_selectop(sop); + return (0); + } + + if (events & EV_READ) + FD_CLR(fd, sop->event_readset_in); + + if (events & EV_WRITE) + FD_CLR(fd, sop->event_writeset_in); + + check_selectop(sop); + return (0); +} + +static void +select_free_selectop(struct selectop *sop) +{ + if (sop->event_readset_in) + mm_free(sop->event_readset_in); + if (sop->event_writeset_in) + mm_free(sop->event_writeset_in); + if (sop->event_readset_out) + mm_free(sop->event_readset_out); + if (sop->event_writeset_out) + mm_free(sop->event_writeset_out); + + memset(sop, 0, sizeof(struct selectop)); + mm_free(sop); +} + +static void +select_dealloc(struct event_base *base) +{ + evsig_dealloc_(base); + + select_free_selectop(base->evbase); +} + +#endif /* EVENT__HAVE_SELECT */ diff --git a/libs/libevent/docs/test/Makefile.nmake b/libs/libevent/docs/test/Makefile.nmake new file mode 100644 index 0000000000..30c3eb792b --- /dev/null +++ b/libs/libevent/docs/test/Makefile.nmake @@ -0,0 +1,79 @@ +# WATCH OUT! This makefile is a work in progress. -*- makefile -*- + +!IFDEF OPENSSL_DIR +SSL_CFLAGS=/I$(OPENSSL_DIR)\include /DEVENT__HAVE_OPENSSL +SSL_OBJS=regress_ssl.obj +SSL_LIBS=..\libevent_openssl.lib $(OPENSSL_DIR)\lib\libeay32.lib $(OPENSSL_DIR)\lib\ssleay32.lib gdi32.lib User32.lib +!ELSE +SSL_CFLAGS= +SSL_OBJS= +SSL_LIBS= +!ENDIF + +CFLAGS=/I.. /I../WIN32-Code /I../WIN32-Code/nmake /I../include /I../compat /DHAVE_CONFIG_H /DTINYTEST_LOCAL $(SSL_CFLAGS) + +CFLAGS=$(CFLAGS) /Ox /W3 /wd4996 /nologo + +REGRESS_OBJS=regress.obj regress_buffer.obj regress_http.obj regress_dns.obj \ + regress_testutils.obj \ + regress_rpc.obj regress.gen.obj \ + regress_et.obj regress_bufferevent.obj \ + regress_listener.obj regress_util.obj tinytest.obj \ + regress_main.obj regress_minheap.obj regress_iocp.obj \ + regress_thread.obj regress_finalize.obj $(SSL_OBJS) + +OTHER_OBJS=test-init.obj test-eof.obj test-closed.obj test-weof.obj test-time.obj \ + bench.obj bench_cascade.obj bench_http.obj bench_httpclient.obj \ + test-changelist.obj \ + print-winsock-errors.obj + +PROGRAMS=regress.exe \ + test-init.exe test-eof.exe test-closed.exe test-weof.exe test-time.exe \ + test-changelist.exe \ + print-winsock-errors.exe + +# Disabled for now: +# bench.exe bench_cascade.exe bench_http.exe bench_httpclient.exe + + +LIBS=..\libevent.lib ws2_32.lib shell32.lib advapi32.lib + +all: $(PROGRAMS) + +regress.exe: $(REGRESS_OBJS) + $(CC) $(CFLAGS) $(LIBS) $(SSL_LIBS) $(REGRESS_OBJS) + +test-init.exe: test-init.obj + $(CC) $(CFLAGS) $(LIBS) test-init.obj +test-eof.exe: test-eof.obj + $(CC) $(CFLAGS) $(LIBS) test-eof.obj +test-closed.exe: test-closed.obj + $(CC) $(CFLAGS) $(LIBS) test-closed.obj +test-changelist.exe: test-changelist.obj + $(CC) $(CFLAGS) $(LIBS) test-changelist.obj +test-weof.exe: test-weof.obj + $(CC) $(CFLAGS) $(LIBS) test-weof.obj +test-time.exe: test-time.obj + $(CC) $(CFLAGS) $(LIBS) test-time.obj + +print-winsock-errors.exe: print-winsock-errors.obj + $(CC) $(CFLAGS) $(LIBS) print-winsock-errors.obj + +bench.exe: bench.obj + $(CC) $(CFLAGS) $(LIBS) bench.obj +bench_cascade.exe: bench_cascade.obj + $(CC) $(CFLAGS) $(LIBS) bench_cascade.obj +bench_http.exe: bench_http.obj + $(CC) $(CFLAGS) $(LIBS) bench_http.obj +bench_httpclient.exe: bench_httpclient.obj + $(CC) $(CFLAGS) $(LIBS) bench_httpclient.obj + +regress.gen.c regress.gen.h: regress.rpc ../event_rpcgen.py + echo // > regress.gen.c + echo #define NO_PYTHON_EXISTS > regress.gen.h + -python ..\event_rpcgen.py regress.rpc + +clean: + -del $(REGRESS_OBJS) + -del $(OTHER_OBJS) + -del $(PROGRAMS) diff --git a/libs/libevent/docs/test/bench.c b/libs/libevent/docs/test/bench.c new file mode 100644 index 0000000000..214479c1ff --- /dev/null +++ b/libs/libevent/docs/test/bench.c @@ -0,0 +1,207 @@ +/* + * Copyright 2003-2007 Niels Provos <provos@citi.umich.edu> + * Copyright 2007-2012 Niels Provos and Nick Mathewson + * + * 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. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + * + * + * Mon 03/10/2003 - Modified by Davide Libenzi <davidel@xmailserver.org> + * + * Added chain event propagation to improve the sensitivity of + * the measure respect to the event loop efficency. + * + * + */ + +#include "event2/event-config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#else +#include <sys/socket.h> +#include <signal.h> +#include <sys/resource.h> +#endif +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#ifdef EVENT__HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <errno.h> + +#ifdef _WIN32 +#include <getopt.h> +#endif + +#include <event.h> +#include <evutil.h> + +static int count, writes, fired, failures; +static evutil_socket_t *pipes; +static int num_pipes, num_active, num_writes; +static struct event *events; + + +static void +read_cb(evutil_socket_t fd, short which, void *arg) +{ + ev_intptr_t idx = (ev_intptr_t) arg, widx = idx + 1; + unsigned char ch; + ev_ssize_t n; + + n = recv(fd, (char*)&ch, sizeof(ch), 0); + if (n >= 0) + count += n; + else + failures++; + if (writes) { + if (widx >= num_pipes) + widx -= num_pipes; + n = send(pipes[2 * widx + 1], "e", 1, 0); + if (n != 1) + failures++; + writes--; + fired++; + } +} + +static struct timeval * +run_once(void) +{ + evutil_socket_t *cp, space; + long i; + static struct timeval ts, te; + + for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) { + if (event_initialized(&events[i])) + event_del(&events[i]); + event_set(&events[i], cp[0], EV_READ | EV_PERSIST, read_cb, (void *)(ev_intptr_t) i); + event_add(&events[i], NULL); + } + + event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK); + + fired = 0; + space = num_pipes / num_active; + space = space * 2; + for (i = 0; i < num_active; i++, fired++) + (void) send(pipes[i * space + 1], "e", 1, 0); + + count = 0; + writes = num_writes; + { int xcount = 0; + evutil_gettimeofday(&ts, NULL); + do { + event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK); + xcount++; + } while (count != fired); + evutil_gettimeofday(&te, NULL); + + if (xcount != count) fprintf(stderr, "Xcount: %d, Rcount: %d\n", xcount, count); + } + + evutil_timersub(&te, &ts, &te); + + return (&te); +} + +int +main(int argc, char **argv) +{ +#ifdef HAVE_SETRLIMIT + struct rlimit rl; +#endif + int i, c; + struct timeval *tv; + evutil_socket_t *cp; + +#ifdef _WIN32 + WSADATA WSAData; + WSAStartup(0x101, &WSAData); +#endif + num_pipes = 100; + num_active = 1; + num_writes = num_pipes; + while ((c = getopt(argc, argv, "n:a:w:")) != -1) { + switch (c) { + case 'n': + num_pipes = atoi(optarg); + break; + case 'a': + num_active = atoi(optarg); + break; + case 'w': + num_writes = atoi(optarg); + break; + default: + fprintf(stderr, "Illegal argument \"%c\"\n", c); + exit(1); + } + } + +#ifdef HAVE_SETRLIMIT + rl.rlim_cur = rl.rlim_max = num_pipes * 2 + 50; + if (setrlimit(RLIMIT_NOFILE, &rl) == -1) { + perror("setrlimit"); + exit(1); + } +#endif + + events = calloc(num_pipes, sizeof(struct event)); + pipes = calloc(num_pipes * 2, sizeof(evutil_socket_t)); + if (events == NULL || pipes == NULL) { + perror("malloc"); + exit(1); + } + + event_init(); + + for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) { +#ifdef USE_PIPES + if (pipe(cp) == -1) { +#else + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, cp) == -1) { +#endif + perror("pipe"); + exit(1); + } + } + + for (i = 0; i < 25; i++) { + tv = run_once(); + if (tv == NULL) + exit(1); + fprintf(stdout, "%ld\n", + tv->tv_sec * 1000000L + tv->tv_usec); + } + + exit(0); +} diff --git a/libs/libevent/docs/test/bench_cascade.c b/libs/libevent/docs/test/bench_cascade.c new file mode 100644 index 0000000000..2d85cc1f10 --- /dev/null +++ b/libs/libevent/docs/test/bench_cascade.c @@ -0,0 +1,188 @@ +/* + * Copyright 2007-2012 Niels Provos and Nick Mathewson + * + * 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. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + * + */ + +#include "event2/event-config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#else +#include <sys/socket.h> +#include <sys/resource.h> +#endif +#include <signal.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#ifdef EVENT__HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <errno.h> +#include <getopt.h> +#include <event.h> +#include <evutil.h> + +/* + * This benchmark tests how quickly we can propagate a write down a chain + * of socket pairs. We start by writing to the first socket pair and all + * events will fire subsequently until the last socket pair has been reached + * and the benchmark terminates. + */ + +static int fired; +static evutil_socket_t *pipes; +static struct event *events; + +static void +read_cb(evutil_socket_t fd, short which, void *arg) +{ + char ch; + evutil_socket_t sock = (evutil_socket_t)(ev_intptr_t)arg; + + (void) recv(fd, &ch, sizeof(ch), 0); + if (sock >= 0) { + if (send(sock, "e", 1, 0) < 0) + perror("send"); + } + fired++; +} + +static struct timeval * +run_once(int num_pipes) +{ + int i; + evutil_socket_t *cp; + static struct timeval ts, te, tv_timeout; + + events = (struct event *)calloc(num_pipes, sizeof(struct event)); + pipes = (evutil_socket_t *)calloc(num_pipes * 2, sizeof(evutil_socket_t)); + + if (events == NULL || pipes == NULL) { + perror("malloc"); + exit(1); + } + + for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) { + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, cp) == -1) { + perror("socketpair"); + exit(1); + } + } + + /* measurements includes event setup */ + evutil_gettimeofday(&ts, NULL); + + /* provide a default timeout for events */ + evutil_timerclear(&tv_timeout); + tv_timeout.tv_sec = 60; + + for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) { + evutil_socket_t fd = i < num_pipes - 1 ? cp[3] : -1; + event_set(&events[i], cp[0], EV_READ, read_cb, + (void *)(ev_intptr_t)fd); + event_add(&events[i], &tv_timeout); + } + + fired = 0; + + /* kick everything off with a single write */ + if (send(pipes[1], "e", 1, 0) < 0) + perror("send"); + + event_dispatch(); + + evutil_gettimeofday(&te, NULL); + evutil_timersub(&te, &ts, &te); + + for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) { + event_del(&events[i]); + evutil_closesocket(cp[0]); + evutil_closesocket(cp[1]); + } + + free(pipes); + free(events); + + return (&te); +} + +int +main(int argc, char **argv) +{ +#ifdef HAVE_SETRLIMIT + struct rlimit rl; +#endif + int i, c; + struct timeval *tv; + + int num_pipes = 100; +#ifdef _WIN32 + WSADATA WSAData; + WSAStartup(0x101, &WSAData); +#endif + + while ((c = getopt(argc, argv, "n:")) != -1) { + switch (c) { + case 'n': + num_pipes = atoi(optarg); + break; + default: + fprintf(stderr, "Illegal argument \"%c\"\n", c); + exit(1); + } + } + +#ifdef HAVE_SETRLIMIT + rl.rlim_cur = rl.rlim_max = num_pipes * 2 + 50; + if (setrlimit(RLIMIT_NOFILE, &rl) == -1) { + perror("setrlimit"); + exit(1); + } +#endif + + event_init(); + + for (i = 0; i < 25; i++) { + tv = run_once(num_pipes); + if (tv == NULL) + exit(1); + fprintf(stdout, "%ld\n", + tv->tv_sec * 1000000L + tv->tv_usec); + } + +#ifdef _WIN32 + WSACleanup(); +#endif + + exit(0); +} diff --git a/libs/libevent/docs/test/bench_http.c b/libs/libevent/docs/test/bench_http.c new file mode 100644 index 0000000000..6d0d971799 --- /dev/null +++ b/libs/libevent/docs/test/bench_http.c @@ -0,0 +1,195 @@ +/* + * Copyright 2008-2012 Niels Provos and Nick Mathewson + * + * 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. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + * + */ + +#include <sys/types.h> +#include <sys/stat.h> +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <sys/socket.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <unistd.h> +#endif +#include <fcntl.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "event2/event.h" +#include "event2/buffer.h" +#include "event2/util.h" +#include "event2/http.h" +#include "event2/thread.h" + +static void http_basic_cb(struct evhttp_request *req, void *arg); + +static char *content; +static size_t content_len = 0; + +static void +http_basic_cb(struct evhttp_request *req, void *arg) +{ + struct evbuffer *evb = evbuffer_new(); + + evbuffer_add(evb, content, content_len); + + /* allow sending of an empty reply */ + evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); + + evbuffer_free(evb); +} + +#if LIBEVENT_VERSION_NUMBER >= 0x02000200 +static void +http_ref_cb(struct evhttp_request *req, void *arg) +{ + struct evbuffer *evb = evbuffer_new(); + + evbuffer_add_reference(evb, content, content_len, NULL, NULL); + + /* allow sending of an empty reply */ + evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); + + evbuffer_free(evb); +} +#endif + +int +main(int argc, char **argv) +{ + struct event_config *cfg = event_config_new(); + struct event_base *base; + struct evhttp *http; + int i; + int c; + int use_iocp = 0; + unsigned short port = 8080; + char *endptr = NULL; + +#ifdef _WIN32 + WSADATA WSAData; + WSAStartup(0x101, &WSAData); +#else + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + return (1); +#endif + + for (i = 1; i < argc; ++i) { + if (*argv[i] != '-') + continue; + + c = argv[i][1]; + + if ((c == 'p' || c == 'l') && i + 1 >= argc) { + fprintf(stderr, "-%c requires argument.\n", c); + exit(1); + } + + switch (c) { + case 'p': + if (i+1 >= argc || !argv[i+1]) { + fprintf(stderr, "Missing port\n"); + exit(1); + } + port = (int)strtol(argv[i+1], &endptr, 10); + if (*endptr != '\0') { + fprintf(stderr, "Bad port\n"); + exit(1); + } + break; + case 'l': + if (i+1 >= argc || !argv[i+1]) { + fprintf(stderr, "Missing content length\n"); + exit(1); + } + content_len = (size_t)strtol(argv[i+1], &endptr, 10); + if (*endptr != '\0' || content_len == 0) { + fprintf(stderr, "Bad content length\n"); + exit(1); + } + break; +#ifdef _WIN32 + case 'i': + use_iocp = 1; + evthread_use_windows_threads(); + event_config_set_flag(cfg,EVENT_BASE_FLAG_STARTUP_IOCP); + break; +#endif + default: + fprintf(stderr, "Illegal argument \"%c\"\n", c); + exit(1); + } + } + + base = event_base_new_with_config(cfg); + if (!base) { + fprintf(stderr, "creating event_base failed. Exiting.\n"); + return 1; + } + + http = evhttp_new(base); + + content = malloc(content_len); + if (content == NULL) { + fprintf(stderr, "Cannot allocate content\n"); + exit(1); + } else { + int i = 0; + for (i = 0; i < (int)content_len; ++i) + content[i] = (i & 255); + } + + evhttp_set_cb(http, "/ind", http_basic_cb, NULL); + fprintf(stderr, "/ind - basic content (memory copy)\n"); + + evhttp_set_cb(http, "/ref", http_ref_cb, NULL); + fprintf(stderr, "/ref - basic content (reference)\n"); + + fprintf(stderr, "Serving %d bytes on port %d using %s\n", + (int)content_len, port, + use_iocp? "IOCP" : event_base_get_method(base)); + + evhttp_bind_socket(http, "0.0.0.0", port); + +#ifdef _WIN32 + if (use_iocp) { + struct timeval tv={99999999,0}; + event_base_loopexit(base, &tv); + } +#endif + event_base_dispatch(base); + +#ifdef _WIN32 + WSACleanup(); +#endif + + /* NOTREACHED */ + return (0); +} diff --git a/libs/libevent/docs/test/bench_httpclient.c b/libs/libevent/docs/test/bench_httpclient.c new file mode 100644 index 0000000000..bcddc95f43 --- /dev/null +++ b/libs/libevent/docs/test/bench_httpclient.c @@ -0,0 +1,239 @@ +/* + * Copyright 2009-2012 Niels Provos and Nick Mathewson + * + * 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. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + * + */ + +/* for EVUTIL_ERR_CONNECT_RETRIABLE macro */ +#include "util-internal.h" + +#include <sys/types.h> +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +# ifdef _XOPEN_SOURCE_EXTENDED +# include <arpa/inet.h> +# endif +#endif +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "event2/event.h" +#include "event2/bufferevent.h" +#include "event2/buffer.h" +#include "event2/util.h" + +const char *resource = NULL; +struct event_base *base = NULL; + +int total_n_handled = 0; +int total_n_errors = 0; +int total_n_launched = 0; +size_t total_n_bytes = 0; +struct timeval total_time = {0,0}; +int n_errors = 0; + +const int PARALLELISM = 200; +const int N_REQUESTS = 20000; + +struct request_info { + size_t n_read; + struct timeval started; +}; + +static int launch_request(void); +static void readcb(struct bufferevent *b, void *arg); +static void errorcb(struct bufferevent *b, short what, void *arg); + +static void +readcb(struct bufferevent *b, void *arg) +{ + struct request_info *ri = arg; + struct evbuffer *input = bufferevent_get_input(b); + size_t n = evbuffer_get_length(input); + + ri->n_read += n; + evbuffer_drain(input, n); +} + +static void +errorcb(struct bufferevent *b, short what, void *arg) +{ + struct request_info *ri = arg; + struct timeval now, diff; + if (what & BEV_EVENT_EOF) { + ++total_n_handled; + total_n_bytes += ri->n_read; + evutil_gettimeofday(&now, NULL); + evutil_timersub(&now, &ri->started, &diff); + evutil_timeradd(&diff, &total_time, &total_time); + + if (total_n_handled && (total_n_handled%1000)==0) + printf("%d requests done\n",total_n_handled); + + if (total_n_launched < N_REQUESTS) { + if (launch_request() < 0) + perror("Can't launch"); + } + } else { + ++total_n_errors; + perror("Unexpected error"); + } + + bufferevent_setcb(b, NULL, NULL, NULL, NULL); + free(ri); + bufferevent_disable(b, EV_READ|EV_WRITE); + bufferevent_free(b); +} + +static void +frob_socket(evutil_socket_t sock) +{ +#ifdef HAVE_SO_LINGER + struct linger l; +#endif + int one = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(one))<0) + perror("setsockopt(SO_REUSEADDR)"); +#ifdef HAVE_SO_LINGER + l.l_onoff = 1; + l.l_linger = 0; + if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&l, sizeof(l))<0) + perror("setsockopt(SO_LINGER)"); +#endif +} + +static int +launch_request(void) +{ + evutil_socket_t sock; + struct sockaddr_in sin; + struct bufferevent *b; + + struct request_info *ri; + + memset(&sin, 0, sizeof(sin)); + + ++total_n_launched; + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(0x7f000001); + sin.sin_port = htons(8080); + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + return -1; + if (evutil_make_socket_nonblocking(sock) < 0) { + evutil_closesocket(sock); + return -1; + } + frob_socket(sock); + if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0) { + int e = evutil_socket_geterror(sock); + if (! EVUTIL_ERR_CONNECT_RETRIABLE(e)) { + evutil_closesocket(sock); + return -1; + } + } + + ri = malloc(sizeof(*ri)); + ri->n_read = 0; + evutil_gettimeofday(&ri->started, NULL); + + b = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE); + + bufferevent_setcb(b, readcb, NULL, errorcb, ri); + bufferevent_enable(b, EV_READ|EV_WRITE); + + evbuffer_add_printf(bufferevent_get_output(b), + "GET %s HTTP/1.0\r\n\r\n", resource); + + return 0; +} + + +int +main(int argc, char **argv) +{ + int i; + struct timeval start, end, total; + long long usec; + double throughput; + resource = "/ref"; + +#ifdef _WIN32 + WSADATA WSAData; + WSAStartup(0x101, &WSAData); +#endif + + setvbuf(stdout, NULL, _IONBF, 0); + + base = event_base_new(); + + for (i=0; i < PARALLELISM; ++i) { + if (launch_request() < 0) + perror("launch"); + } + + evutil_gettimeofday(&start, NULL); + + event_base_dispatch(base); + + evutil_gettimeofday(&end, NULL); + evutil_timersub(&end, &start, &total); + usec = total_time.tv_sec * (long long)1000000 + total_time.tv_usec; + + if (!total_n_handled) { + puts("Nothing worked. You probably did something dumb."); + return 0; + } + + + throughput = total_n_handled / + (total.tv_sec+ ((double)total.tv_usec)/1000000.0); + +#ifdef _WIN32 +#define I64_FMT "%I64d" +#define I64_TYP __int64 +#else +#define I64_FMT "%lld" +#define I64_TYP long long int +#endif + + printf("\n%d requests in %d.%06d sec. (%.2f throughput)\n" + "Each took about %.02f msec latency\n" + I64_FMT "bytes read. %d errors.\n", + total_n_handled, + (int)total.tv_sec, (int)total.tv_usec, + throughput, + (double)(usec/1000) / total_n_handled, + (I64_TYP)total_n_bytes, n_errors); + +#ifdef _WIN32 + WSACleanup(); +#endif + + return 0; +} diff --git a/libs/libevent/docs/test/check-dumpevents.py b/libs/libevent/docs/test/check-dumpevents.py new file mode 100644 index 0000000000..16fe9bc92f --- /dev/null +++ b/libs/libevent/docs/test/check-dumpevents.py @@ -0,0 +1,54 @@ +#!/usr/bin/python2 +# +# Post-process the output of test-dumpevents and check it for correctness. +# + +import math +import re +import sys + +text = sys.stdin.readlines() + +try: + expect_inserted_pos = text.index("Inserted:\n") + expect_active_pos = text.index("Active:\n") + got_inserted_pos = text.index("Inserted events:\n") + got_active_pos = text.index("Active events:\n") +except ValueError: + print >>sys.stderr, "Missing expected dividing line in dumpevents output" + sys.exit(1) + +if not (expect_inserted_pos < expect_active_pos < + got_inserted_pos < got_active_pos): + print >>sys.stderr, "Sections out of order in dumpevents output" + sys.exit(1) + +now,T= text[1].split() +T = float(T) + +want_inserted = set(text[expect_inserted_pos+1:expect_active_pos]) +want_active = set(text[expect_active_pos+1:got_inserted_pos-1]) +got_inserted = set(text[got_inserted_pos+1:got_active_pos]) +got_active = set(text[got_active_pos+1:]) + +pat = re.compile(r'Timeout=([0-9\.]+)') +def replace_time(m): + t = float(m.group(1)) + if .9 < abs(t-T) < 1.1: + return "Timeout=T+1" + elif 2.4 < abs(t-T) < 2.6: + return "Timeout=T+2.5" + else: + return m.group(0) + +cleaned_inserted = set( pat.sub(replace_time, s) for s in got_inserted + if "Internal" not in s) + +if cleaned_inserted != want_inserted: + print >>sys.stderr, "Inserted event lists were not as expected!" + sys.exit(1) + +if set(got_active) != set(want_active): + print >>sys.stderr, "Active event lists were not as expected!" + sys.exit(1) + diff --git a/libs/libevent/docs/test/include.am b/libs/libevent/docs/test/include.am new file mode 100644 index 0000000000..4cd49ef630 --- /dev/null +++ b/libs/libevent/docs/test/include.am @@ -0,0 +1,146 @@ +# test/Makefile.am for libevent +# Copyright 2000-2007 Niels Provos +# Copyright 2007-2012 Niels Provos and Nick Mathewson +# +# See LICENSE for copying information. + +regress_CPPFLAGS = -DTINYTEST_LOCAL + +EXTRA_DIST+= \ + test/check-dumpevents.py \ + test/regress.gen.c \ + test/regress.gen.h \ + test/regress.rpc \ + test/rpcgen_wrapper.sh \ + test/test.sh + +TESTPROGRAMS = \ + test/bench \ + test/bench_cascade \ + test/bench_http \ + test/bench_httpclient \ + test/test-changelist \ + test/test-dumpevents \ + test/test-eof \ + test/test-closed \ + test/test-fdleak \ + test/test-init \ + test/test-ratelim \ + test/test-time \ + test/test-weof \ + test/regress + +if BUILD_REGRESS +noinst_PROGRAMS += $(TESTPROGRAMS) +EXTRA_PROGRAMS+= test/regress +endif + +noinst_HEADERS+= \ + test/regress.h \ + test/regress_thread.h \ + test/tinytest.h \ + test/tinytest_local.h \ + test/tinytest_macros.h + +# We need to copy this file, since automake doesn't want us to use top_srcdir +# in TESTS. +TESTS = test/test-script.sh + +test/test-script.sh: test/test.sh + cp $(top_srcdir)/test/test.sh $@ + +DISTCLEANFILES += test/test-script.sh +DISTCLEANFILES += test/regress.gen.c test/regress.gen.h + +if BUILD_REGRESS +BUILT_SOURCES += test/regress.gen.c test/regress.gen.h +endif + +test_test_init_SOURCES = test/test-init.c +test_test_init_LDADD = libevent_core.la +test_test_dumpevents_SOURCES = test/test-dumpevents.c +test_test_dumpevents_LDADD = libevent_core.la +test_test_eof_SOURCES = test/test-eof.c +test_test_eof_LDADD = libevent_core.la +test_test_closed_SOURCES = test/test-closed.c +test_test_closed_LDADD = libevent_core.la +test_test_changelist_SOURCES = test/test-changelist.c +test_test_changelist_LDADD = libevent_core.la +test_test_weof_SOURCES = test/test-weof.c +test_test_weof_LDADD = libevent_core.la +test_test_time_SOURCES = test/test-time.c +test_test_time_LDADD = libevent_core.la +test_test_ratelim_SOURCES = test/test-ratelim.c +test_test_ratelim_LDADD = libevent_core.la -lm +test_test_fdleak_SOURCES = test/test-fdleak.c +test_test_fdleak_LDADD = libevent_core.la + +test_regress_SOURCES = \ + test/regress.c \ + test/regress.gen.c \ + test/regress.gen.h \ + test/regress_buffer.c \ + test/regress_bufferevent.c \ + test/regress_dns.c \ + test/regress_et.c \ + test/regress_finalize.c \ + test/regress_http.c \ + test/regress_listener.c \ + test/regress_main.c \ + test/regress_minheap.c \ + test/regress_rpc.c \ + test/regress_testutils.c \ + test/regress_testutils.h \ + test/regress_util.c \ + test/tinytest.c \ + $(regress_thread_SOURCES) \ + $(regress_zlib_SOURCES) + +if PTHREADS +regress_thread_SOURCES = test/regress_thread.c +PTHREAD_LIBS += libevent_pthreads.la +endif +if BUILD_WIN32 +regress_thread_SOURCES = test/regress_thread.c +endif +if ZLIB_REGRESS +regress_zlib_SOURCES = test/regress_zlib.c +endif +if BUILD_WIN32 +test_regress_SOURCES += test/regress_iocp.c +endif + +test_regress_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la $(PTHREAD_LIBS) $(ZLIB_LIBS) +test_regress_CPPFLAGS = $(AM_CPPFLAGS) $(PTHREAD_CFLAGS) $(ZLIB_CFLAGS) -Itest +test_regress_LDFLAGS = $(PTHREAD_CFLAGS) + +if OPENSSL +test_regress_SOURCES += test/regress_ssl.c +test_regress_CPPFLAGS += $(OPENSSL_INCS) +test_regress_LDADD += libevent_openssl.la $(OPENSSL_LIBS) ${OPENSSL_LIBADD} +endif + +test_bench_SOURCES = test/bench.c +test_bench_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la +test_bench_cascade_SOURCES = test/bench_cascade.c +test_bench_cascade_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la +test_bench_http_SOURCES = test/bench_http.c +test_bench_http_LDADD = $(LIBEVENT_GC_SECTIONS) libevent.la +test_bench_httpclient_SOURCES = test/bench_httpclient.c +test_bench_httpclient_LDADD = $(LIBEVENT_GC_SECTIONS) libevent_core.la + +test/regress.gen.c test/regress.gen.h: test/rpcgen-attempted + +test/rpcgen-attempted: test/regress.rpc event_rpcgen.py test/rpcgen_wrapper.sh + $(AM_V_GEN)date -u > $@ + $(AM_V_at)if $(srcdir)/test/rpcgen_wrapper.sh $(srcdir)/test; then \ + true; \ + else \ + echo "No Python installed; stubbing out RPC test." >&2; \ + echo " "> test/regress.gen.c; \ + echo "#define NO_PYTHON_EXISTS" > test/regress.gen.h; \ + fi + +CLEANFILES += test/rpcgen-attempted + +$(TESTPROGRAMS) : libevent.la diff --git a/libs/libevent/docs/test/print-winsock-errors.c b/libs/libevent/docs/test/print-winsock-errors.c new file mode 100644 index 0000000000..ab6e610e84 --- /dev/null +++ b/libs/libevent/docs/test/print-winsock-errors.c @@ -0,0 +1,84 @@ +#include <winsock2.h> +#include <windows.h> + +#include <stdlib.h> +#include <stdio.h> + +#include "event2/event.h" +#include "event2/util.h" +#include "event2/thread.h" + +#define E(x) printf (#x " -> \"%s\"\n", evutil_socket_error_to_string (x)); + +int main (int argc, char **argv) +{ + int i, j; + const char *s1, *s2; + + evthread_use_windows_threads (); + + s1 = evutil_socket_error_to_string (WSAEINTR); + + for (i = 0; i < 3; i++) { + printf ("\niteration %d:\n\n", i); + E(WSAEINTR); + E(WSAEACCES); + E(WSAEFAULT); + E(WSAEINVAL); + E(WSAEMFILE); + E(WSAEWOULDBLOCK); + E(WSAEINPROGRESS); + E(WSAEALREADY); + E(WSAENOTSOCK); + E(WSAEDESTADDRREQ); + E(WSAEMSGSIZE); + E(WSAEPROTOTYPE); + E(WSAENOPROTOOPT); + E(WSAEPROTONOSUPPORT); + E(WSAESOCKTNOSUPPORT); + E(WSAEOPNOTSUPP); + E(WSAEPFNOSUPPORT); + E(WSAEAFNOSUPPORT); + E(WSAEADDRINUSE); + E(WSAEADDRNOTAVAIL); + E(WSAENETDOWN); + E(WSAENETUNREACH); + E(WSAENETRESET); + E(WSAECONNABORTED); + E(WSAECONNRESET); + E(WSAENOBUFS); + E(WSAEISCONN); + E(WSAENOTCONN); + E(WSAESHUTDOWN); + E(WSAETIMEDOUT); + E(WSAECONNREFUSED); + E(WSAEHOSTDOWN); + E(WSAEHOSTUNREACH); + E(WSAEPROCLIM); + E(WSASYSNOTREADY); + E(WSAVERNOTSUPPORTED); + E(WSANOTINITIALISED); + E(WSAEDISCON); + E(WSATYPE_NOT_FOUND); + E(WSAHOST_NOT_FOUND); + E(WSATRY_AGAIN); + E(WSANO_RECOVERY); + E(WSANO_DATA); + E(0xdeadbeef); /* test the case where no message is available */ + + /* fill up the hash table a bit to make sure it grows properly */ + for (j = 0; j < 50; j++) { + int err; + evutil_secure_rng_get_bytes(&err, sizeof(err)); + evutil_socket_error_to_string(err); + } + } + + s2 = evutil_socket_error_to_string (WSAEINTR); + if (s1 != s2) + printf ("caching failed!\n"); + + libevent_global_shutdown (); + + return EXIT_SUCCESS; +} diff --git a/libs/libevent/docs/test/regress.c b/libs/libevent/docs/test/regress.c new file mode 100644 index 0000000000..b12c66dfa0 --- /dev/null +++ b/libs/libevent/docs/test/regress.c @@ -0,0 +1,3401 @@ +/* + * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "util-internal.h" + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#endif + +#ifdef EVENT__HAVE_PTHREADS +#include <pthread.h> +#endif + +#include "event2/event-config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/queue.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <sys/wait.h> +#include <signal.h> +#include <unistd.h> +#include <netdb.h> +#endif +#include <fcntl.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <ctype.h> + +#include "event2/event.h" +#include "event2/event_struct.h" +#include "event2/event_compat.h" +#include "event2/tag.h" +#include "event2/buffer.h" +#include "event2/buffer_compat.h" +#include "event2/util.h" +#include "event-internal.h" +#include "evthread-internal.h" +#include "log-internal.h" +#include "time-internal.h" + +#include "regress.h" + +#ifndef _WIN32 +#include "regress.gen.h" +#endif + +evutil_socket_t pair[2]; +int test_ok; +int called; +struct event_base *global_base; + +static char wbuf[4096]; +static char rbuf[4096]; +static int woff; +static int roff; +static int usepersist; +static struct timeval tset; +static struct timeval tcalled; + + +#define TEST1 "this is a test" + +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + +#ifdef _WIN32 +#define write(fd,buf,len) send((fd),(buf),(int)(len),0) +#define read(fd,buf,len) recv((fd),(buf),(int)(len),0) +#endif + +struct basic_cb_args +{ + struct event_base *eb; + struct event *ev; + unsigned int callcount; +}; + +static void +simple_read_cb(evutil_socket_t fd, short event, void *arg) +{ + char buf[256]; + int len; + + len = read(fd, buf, sizeof(buf)); + + if (len) { + if (!called) { + if (event_add(arg, NULL) == -1) + exit(1); + } + } else if (called == 1) + test_ok = 1; + + called++; +} + +static void +basic_read_cb(evutil_socket_t fd, short event, void *data) +{ + char buf[256]; + int len; + struct basic_cb_args *arg = data; + + len = read(fd, buf, sizeof(buf)); + + if (len < 0) { + tt_fail_perror("read (callback)"); + } else { + switch (arg->callcount++) { + case 0: /* first call: expect to read data; cycle */ + if (len > 0) + return; + + tt_fail_msg("EOF before data read"); + break; + + case 1: /* second call: expect EOF; stop */ + if (len > 0) + tt_fail_msg("not all data read on first cycle"); + break; + + default: /* third call: should not happen */ + tt_fail_msg("too many cycles"); + } + } + + event_del(arg->ev); + event_base_loopexit(arg->eb, NULL); +} + +static void +dummy_read_cb(evutil_socket_t fd, short event, void *arg) +{ +} + +static void +simple_write_cb(evutil_socket_t fd, short event, void *arg) +{ + int len; + + len = write(fd, TEST1, strlen(TEST1) + 1); + if (len == -1) + test_ok = 0; + else + test_ok = 1; +} + +static void +multiple_write_cb(evutil_socket_t fd, short event, void *arg) +{ + struct event *ev = arg; + int len; + + len = 128; + if (woff + len >= (int)sizeof(wbuf)) + len = sizeof(wbuf) - woff; + + len = write(fd, wbuf + woff, len); + if (len == -1) { + fprintf(stderr, "%s: write\n", __func__); + if (usepersist) + event_del(ev); + return; + } + + woff += len; + + if (woff >= (int)sizeof(wbuf)) { + shutdown(fd, SHUT_WR); + if (usepersist) + event_del(ev); + return; + } + + if (!usepersist) { + if (event_add(ev, NULL) == -1) + exit(1); + } +} + +static void +multiple_read_cb(evutil_socket_t fd, short event, void *arg) +{ + struct event *ev = arg; + int len; + + len = read(fd, rbuf + roff, sizeof(rbuf) - roff); + if (len == -1) + fprintf(stderr, "%s: read\n", __func__); + if (len <= 0) { + if (usepersist) + event_del(ev); + return; + } + + roff += len; + if (!usepersist) { + if (event_add(ev, NULL) == -1) + exit(1); + } +} + +static void +timeout_cb(evutil_socket_t fd, short event, void *arg) +{ + evutil_gettimeofday(&tcalled, NULL); +} + +struct both { + struct event ev; + int nread; +}; + +static void +combined_read_cb(evutil_socket_t fd, short event, void *arg) +{ + struct both *both = arg; + char buf[128]; + int len; + + len = read(fd, buf, sizeof(buf)); + if (len == -1) + fprintf(stderr, "%s: read\n", __func__); + if (len <= 0) + return; + + both->nread += len; + if (event_add(&both->ev, NULL) == -1) + exit(1); +} + +static void +combined_write_cb(evutil_socket_t fd, short event, void *arg) +{ + struct both *both = arg; + char buf[128]; + int len; + + len = sizeof(buf); + if (len > both->nread) + len = both->nread; + + memset(buf, 'q', len); + + len = write(fd, buf, len); + if (len == -1) + fprintf(stderr, "%s: write\n", __func__); + if (len <= 0) { + shutdown(fd, SHUT_WR); + return; + } + + both->nread -= len; + if (event_add(&both->ev, NULL) == -1) + exit(1); +} + +/* These macros used to replicate the work of the legacy test wrapper code */ +#define setup_test(x) do { \ + if (!in_legacy_test_wrapper) { \ + TT_FAIL(("Legacy test %s not wrapped properly", x)); \ + return; \ + } \ + } while (0) +#define cleanup_test() setup_test("cleanup") + +static void +test_simpleread(void) +{ + struct event ev; + + /* Very simple read test */ + setup_test("Simple read: "); + + if (write(pair[0], TEST1, strlen(TEST1)+1) < 0) { + tt_fail_perror("write"); + } + + shutdown(pair[0], SHUT_WR); + + event_set(&ev, pair[1], EV_READ, simple_read_cb, &ev); + if (event_add(&ev, NULL) == -1) + exit(1); + event_dispatch(); + + cleanup_test(); +} + +static void +test_simplewrite(void) +{ + struct event ev; + + /* Very simple write test */ + setup_test("Simple write: "); + + event_set(&ev, pair[0], EV_WRITE, simple_write_cb, &ev); + if (event_add(&ev, NULL) == -1) + exit(1); + event_dispatch(); + + cleanup_test(); +} + +static void +simpleread_multiple_cb(evutil_socket_t fd, short event, void *arg) +{ + if (++called == 2) + test_ok = 1; +} + +static void +test_simpleread_multiple(void) +{ + struct event one, two; + + /* Very simple read test */ + setup_test("Simple read to multiple evens: "); + + if (write(pair[0], TEST1, strlen(TEST1)+1) < 0) { + tt_fail_perror("write"); + } + + shutdown(pair[0], SHUT_WR); + + event_set(&one, pair[1], EV_READ, simpleread_multiple_cb, NULL); + if (event_add(&one, NULL) == -1) + exit(1); + event_set(&two, pair[1], EV_READ, simpleread_multiple_cb, NULL); + if (event_add(&two, NULL) == -1) + exit(1); + event_dispatch(); + + cleanup_test(); +} + +static int have_closed = 0; +static int premature_event = 0; +static void +simpleclose_close_fd_cb(evutil_socket_t s, short what, void *ptr) +{ + evutil_socket_t **fds = ptr; + TT_BLATHER(("Closing")); + evutil_closesocket(*fds[0]); + evutil_closesocket(*fds[1]); + *fds[0] = -1; + *fds[1] = -1; + have_closed = 1; +} + +static void +record_event_cb(evutil_socket_t s, short what, void *ptr) +{ + short *whatp = ptr; + if (!have_closed) + premature_event = 1; + *whatp = what; + TT_BLATHER(("Recorded %d on socket %d", (int)what, (int)s)); +} + +static void +test_simpleclose(void *ptr) +{ + /* Test that a close of FD is detected as a read and as a write. */ + struct event_base *base = event_base_new(); + evutil_socket_t pair1[2]={-1,-1}, pair2[2] = {-1, -1}; + evutil_socket_t *to_close[2]; + struct event *rev=NULL, *wev=NULL, *closeev=NULL; + struct timeval tv; + short got_read_on_close = 0, got_write_on_close = 0; + char buf[1024]; + memset(buf, 99, sizeof(buf)); +#ifdef _WIN32 +#define LOCAL_SOCKETPAIR_AF AF_INET +#else +#define LOCAL_SOCKETPAIR_AF AF_UNIX +#endif + if (evutil_socketpair(LOCAL_SOCKETPAIR_AF, SOCK_STREAM, 0, pair1)<0) + TT_DIE(("socketpair: %s", strerror(errno))); + if (evutil_socketpair(LOCAL_SOCKETPAIR_AF, SOCK_STREAM, 0, pair2)<0) + TT_DIE(("socketpair: %s", strerror(errno))); + if (evutil_make_socket_nonblocking(pair1[1]) < 0) + TT_DIE(("make_socket_nonblocking")); + if (evutil_make_socket_nonblocking(pair2[1]) < 0) + TT_DIE(("make_socket_nonblocking")); + + /** Stuff pair2[1] full of data, until write fails */ + while (1) { + int r = write(pair2[1], buf, sizeof(buf)); + if (r<0) { + int err = evutil_socket_geterror(pair2[1]); + if (! EVUTIL_ERR_RW_RETRIABLE(err)) + TT_DIE(("write failed strangely: %s", + evutil_socket_error_to_string(err))); + break; + } + } + to_close[0] = &pair1[0]; + to_close[1] = &pair2[0]; + + closeev = event_new(base, -1, EV_TIMEOUT, simpleclose_close_fd_cb, + to_close); + rev = event_new(base, pair1[1], EV_READ, record_event_cb, + &got_read_on_close); + TT_BLATHER(("Waiting for read on %d", (int)pair1[1])); + wev = event_new(base, pair2[1], EV_WRITE, record_event_cb, + &got_write_on_close); + TT_BLATHER(("Waiting for write on %d", (int)pair2[1])); + tv.tv_sec = 0; + tv.tv_usec = 100*1000; /* Close pair1[0] after a little while, and make + * sure we get a read event. */ + event_add(closeev, &tv); + event_add(rev, NULL); + event_add(wev, NULL); + /* Don't let the test go on too long. */ + tv.tv_sec = 0; + tv.tv_usec = 200*1000; + event_base_loopexit(base, &tv); + event_base_loop(base, 0); + + tt_int_op(got_read_on_close, ==, EV_READ); + tt_int_op(got_write_on_close, ==, EV_WRITE); + tt_int_op(premature_event, ==, 0); + +end: + if (pair1[0] >= 0) + evutil_closesocket(pair1[0]); + if (pair1[1] >= 0) + evutil_closesocket(pair1[1]); + if (pair2[0] >= 0) + evutil_closesocket(pair2[0]); + if (pair2[1] >= 0) + evutil_closesocket(pair2[1]); + if (rev) + event_free(rev); + if (wev) + event_free(wev); + if (closeev) + event_free(closeev); + if (base) + event_base_free(base); +} + + +static void +test_multiple(void) +{ + struct event ev, ev2; + int i; + + /* Multiple read and write test */ + setup_test("Multiple read/write: "); + memset(rbuf, 0, sizeof(rbuf)); + for (i = 0; i < (int)sizeof(wbuf); i++) + wbuf[i] = i; + + roff = woff = 0; + usepersist = 0; + + event_set(&ev, pair[0], EV_WRITE, multiple_write_cb, &ev); + if (event_add(&ev, NULL) == -1) + exit(1); + event_set(&ev2, pair[1], EV_READ, multiple_read_cb, &ev2); + if (event_add(&ev2, NULL) == -1) + exit(1); + event_dispatch(); + + if (roff == woff) + test_ok = memcmp(rbuf, wbuf, sizeof(wbuf)) == 0; + + cleanup_test(); +} + +static void +test_persistent(void) +{ + struct event ev, ev2; + int i; + + /* Multiple read and write test with persist */ + setup_test("Persist read/write: "); + memset(rbuf, 0, sizeof(rbuf)); + for (i = 0; i < (int)sizeof(wbuf); i++) + wbuf[i] = i; + + roff = woff = 0; + usepersist = 1; + + event_set(&ev, pair[0], EV_WRITE|EV_PERSIST, multiple_write_cb, &ev); + if (event_add(&ev, NULL) == -1) + exit(1); + event_set(&ev2, pair[1], EV_READ|EV_PERSIST, multiple_read_cb, &ev2); + if (event_add(&ev2, NULL) == -1) + exit(1); + event_dispatch(); + + if (roff == woff) + test_ok = memcmp(rbuf, wbuf, sizeof(wbuf)) == 0; + + cleanup_test(); +} + +static void +test_combined(void) +{ + struct both r1, r2, w1, w2; + + setup_test("Combined read/write: "); + memset(&r1, 0, sizeof(r1)); + memset(&r2, 0, sizeof(r2)); + memset(&w1, 0, sizeof(w1)); + memset(&w2, 0, sizeof(w2)); + + w1.nread = 4096; + w2.nread = 8192; + + event_set(&r1.ev, pair[0], EV_READ, combined_read_cb, &r1); + event_set(&w1.ev, pair[0], EV_WRITE, combined_write_cb, &w1); + event_set(&r2.ev, pair[1], EV_READ, combined_read_cb, &r2); + event_set(&w2.ev, pair[1], EV_WRITE, combined_write_cb, &w2); + tt_assert(event_add(&r1.ev, NULL) != -1); + tt_assert(!event_add(&w1.ev, NULL)); + tt_assert(!event_add(&r2.ev, NULL)); + tt_assert(!event_add(&w2.ev, NULL)); + event_dispatch(); + + if (r1.nread == 8192 && r2.nread == 4096) + test_ok = 1; + +end: + cleanup_test(); +} + +static void +test_simpletimeout(void) +{ + struct timeval tv; + struct event ev; + + setup_test("Simple timeout: "); + + tv.tv_usec = 200*1000; + tv.tv_sec = 0; + evutil_timerclear(&tcalled); + evtimer_set(&ev, timeout_cb, NULL); + evtimer_add(&ev, &tv); + + evutil_gettimeofday(&tset, NULL); + event_dispatch(); + test_timeval_diff_eq(&tset, &tcalled, 200); + + test_ok = 1; +end: + cleanup_test(); +} + +static void +periodic_timeout_cb(evutil_socket_t fd, short event, void *arg) +{ + int *count = arg; + + (*count)++; + if (*count == 6) { + /* call loopexit only once - on slow machines(?), it is + * apparently possible for this to get called twice. */ + test_ok = 1; + event_base_loopexit(global_base, NULL); + } +} + +static void +test_persistent_timeout(void) +{ + struct timeval tv; + struct event ev; + int count = 0; + + evutil_timerclear(&tv); + tv.tv_usec = 10000; + + event_assign(&ev, global_base, -1, EV_TIMEOUT|EV_PERSIST, + periodic_timeout_cb, &count); + event_add(&ev, &tv); + + event_dispatch(); + + event_del(&ev); +} + +static void +test_persistent_timeout_jump(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event ev; + int count = 0; + struct timeval msec100 = { 0, 100 * 1000 }; + struct timeval msec50 = { 0, 50 * 1000 }; + struct timeval msec300 = { 0, 300 * 1000 }; + + event_assign(&ev, data->base, -1, EV_PERSIST, periodic_timeout_cb, &count); + event_add(&ev, &msec100); + /* Wait for a bit */ + evutil_usleep_(&msec300); + event_base_loopexit(data->base, &msec50); + event_base_dispatch(data->base); + tt_int_op(count, ==, 1); + +end: + event_del(&ev); +} + +struct persist_active_timeout_called { + int n; + short events[16]; + struct timeval tvs[16]; +}; + +static void +activate_cb(evutil_socket_t fd, short event, void *arg) +{ + struct event *ev = arg; + event_active(ev, EV_READ, 1); +} + +static void +persist_active_timeout_cb(evutil_socket_t fd, short event, void *arg) +{ + struct persist_active_timeout_called *c = arg; + if (c->n < 15) { + c->events[c->n] = event; + evutil_gettimeofday(&c->tvs[c->n], NULL); + ++c->n; + } +} + +static void +test_persistent_active_timeout(void *ptr) +{ + struct timeval tv, tv2, tv_exit, start; + struct event ev; + struct persist_active_timeout_called res; + + struct basic_test_data *data = ptr; + struct event_base *base = data->base; + + memset(&res, 0, sizeof(res)); + + tv.tv_sec = 0; + tv.tv_usec = 200 * 1000; + event_assign(&ev, base, -1, EV_TIMEOUT|EV_PERSIST, + persist_active_timeout_cb, &res); + event_add(&ev, &tv); + + tv2.tv_sec = 0; + tv2.tv_usec = 100 * 1000; + event_base_once(base, -1, EV_TIMEOUT, activate_cb, &ev, &tv2); + + tv_exit.tv_sec = 0; + tv_exit.tv_usec = 600 * 1000; + event_base_loopexit(base, &tv_exit); + + event_base_assert_ok_(base); + evutil_gettimeofday(&start, NULL); + + event_base_dispatch(base); + event_base_assert_ok_(base); + + tt_int_op(res.n, ==, 3); + tt_int_op(res.events[0], ==, EV_READ); + tt_int_op(res.events[1], ==, EV_TIMEOUT); + tt_int_op(res.events[2], ==, EV_TIMEOUT); + test_timeval_diff_eq(&start, &res.tvs[0], 100); + test_timeval_diff_eq(&start, &res.tvs[1], 300); + test_timeval_diff_eq(&start, &res.tvs[2], 500); +end: + event_del(&ev); +} + +struct common_timeout_info { + struct event ev; + struct timeval called_at; + int which; + int count; +}; + +static void +common_timeout_cb(evutil_socket_t fd, short event, void *arg) +{ + struct common_timeout_info *ti = arg; + ++ti->count; + evutil_gettimeofday(&ti->called_at, NULL); + if (ti->count >= 4) + event_del(&ti->ev); +} + +static void +test_common_timeout(void *ptr) +{ + struct basic_test_data *data = ptr; + + struct event_base *base = data->base; + int i; + struct common_timeout_info info[100]; + + struct timeval start; + struct timeval tmp_100_ms = { 0, 100*1000 }; + struct timeval tmp_200_ms = { 0, 200*1000 }; + struct timeval tmp_5_sec = { 5, 0 }; + struct timeval tmp_5M_usec = { 0, 5*1000*1000 }; + + const struct timeval *ms_100, *ms_200, *sec_5; + + ms_100 = event_base_init_common_timeout(base, &tmp_100_ms); + ms_200 = event_base_init_common_timeout(base, &tmp_200_ms); + sec_5 = event_base_init_common_timeout(base, &tmp_5_sec); + tt_assert(ms_100); + tt_assert(ms_200); + tt_assert(sec_5); + tt_ptr_op(event_base_init_common_timeout(base, &tmp_200_ms), + ==, ms_200); + tt_ptr_op(event_base_init_common_timeout(base, ms_200), ==, ms_200); + tt_ptr_op(event_base_init_common_timeout(base, &tmp_5M_usec), ==, sec_5); + tt_int_op(ms_100->tv_sec, ==, 0); + tt_int_op(ms_200->tv_sec, ==, 0); + tt_int_op(sec_5->tv_sec, ==, 5); + tt_int_op(ms_100->tv_usec, ==, 100000|0x50000000); + tt_int_op(ms_200->tv_usec, ==, 200000|0x50100000); + tt_int_op(sec_5->tv_usec, ==, 0|0x50200000); + + memset(info, 0, sizeof(info)); + + for (i=0; i<100; ++i) { + info[i].which = i; + event_assign(&info[i].ev, base, -1, EV_TIMEOUT|EV_PERSIST, + common_timeout_cb, &info[i]); + if (i % 2) { + if ((i%20)==1) { + /* Glass-box test: Make sure we survive the + * transition to non-common timeouts. It's + * a little tricky. */ + event_add(&info[i].ev, ms_200); + event_add(&info[i].ev, &tmp_100_ms); + } else if ((i%20)==3) { + /* Check heap-to-common too. */ + event_add(&info[i].ev, &tmp_200_ms); + event_add(&info[i].ev, ms_100); + } else if ((i%20)==5) { + /* Also check common-to-common. */ + event_add(&info[i].ev, ms_200); + event_add(&info[i].ev, ms_100); + } else { + event_add(&info[i].ev, ms_100); + } + } else { + event_add(&info[i].ev, ms_200); + } + } + + event_base_assert_ok_(base); + evutil_gettimeofday(&start, NULL); + event_base_dispatch(base); + + event_base_assert_ok_(base); + + for (i=0; i<10; ++i) { + tt_int_op(info[i].count, ==, 4); + if (i % 2) { + test_timeval_diff_eq(&start, &info[i].called_at, 400); + } else { + test_timeval_diff_eq(&start, &info[i].called_at, 800); + } + } + + /* Make sure we can free the base with some events in. */ + for (i=0; i<100; ++i) { + if (i % 2) { + event_add(&info[i].ev, ms_100); + } else { + event_add(&info[i].ev, ms_200); + } + } + +end: + event_base_free(data->base); /* need to do this here before info is + * out-of-scope */ + data->base = NULL; +} + +#ifndef _WIN32 + +#define current_base event_global_current_base_ +extern struct event_base *current_base; + +static void +fork_signal_cb(evutil_socket_t fd, short events, void *arg) +{ + event_del(arg); +} + + +static void +test_fork(void) +{ + int status; + struct event ev, sig_ev, usr_ev, existing_ev; + pid_t pid; + + setup_test("After fork: "); + + tt_assert(current_base); + evthread_make_base_notifiable(current_base); + + if (write(pair[0], TEST1, strlen(TEST1)+1) < 0) { + tt_fail_perror("write"); + } + + event_set(&ev, pair[1], EV_READ, simple_read_cb, &ev); + if (event_add(&ev, NULL) == -1) + exit(1); + + evsignal_set(&sig_ev, SIGCHLD, fork_signal_cb, &sig_ev); + evsignal_add(&sig_ev, NULL); + + evsignal_set(&existing_ev, SIGUSR2, fork_signal_cb, &existing_ev); + evsignal_add(&existing_ev, NULL); + + event_base_assert_ok_(current_base); + TT_BLATHER(("Before fork")); + if ((pid = regress_fork()) == 0) { + /* in the child */ + TT_BLATHER(("In child, before reinit")); + event_base_assert_ok_(current_base); + if (event_reinit(current_base) == -1) { + fprintf(stdout, "FAILED (reinit)\n"); + exit(1); + } + TT_BLATHER(("After reinit")); + event_base_assert_ok_(current_base); + TT_BLATHER(("After assert-ok")); + + evsignal_del(&sig_ev); + + evsignal_set(&usr_ev, SIGUSR1, fork_signal_cb, &usr_ev); + evsignal_add(&usr_ev, NULL); + raise(SIGUSR1); + raise(SIGUSR2); + + called = 0; + + event_dispatch(); + + event_base_free(current_base); + + /* we do not send an EOF; simple_read_cb requires an EOF + * to set test_ok. we just verify that the callback was + * called. */ + exit(test_ok != 0 || called != 2 ? -2 : 76); + } + + /* wait for the child to read the data */ + { + const struct timeval tv = { 0, 100000 }; + evutil_usleep_(&tv); + } + + if (write(pair[0], TEST1, strlen(TEST1)+1) < 0) { + tt_fail_perror("write"); + } + + TT_BLATHER(("Before waitpid")); + if (waitpid(pid, &status, 0) == -1) { + fprintf(stdout, "FAILED (fork)\n"); + exit(1); + } + TT_BLATHER(("After waitpid")); + + if (WEXITSTATUS(status) != 76) { + fprintf(stdout, "FAILED (exit): %d\n", WEXITSTATUS(status)); + exit(1); + } + + /* test that the current event loop still works */ + if (write(pair[0], TEST1, strlen(TEST1)+1) < 0) { + fprintf(stderr, "%s: write\n", __func__); + } + + shutdown(pair[0], SHUT_WR); + + evsignal_set(&usr_ev, SIGUSR1, fork_signal_cb, &usr_ev); + evsignal_add(&usr_ev, NULL); + raise(SIGUSR1); + raise(SIGUSR2); + + event_dispatch(); + + evsignal_del(&sig_ev); + + end: + cleanup_test(); +} + +#ifdef EVENT__HAVE_PTHREADS +static void* del_wait_thread(void *arg) +{ + struct timeval tv_start, tv_end; + + evutil_gettimeofday(&tv_start, NULL); + event_dispatch(); + evutil_gettimeofday(&tv_end, NULL); + + test_timeval_diff_eq(&tv_start, &tv_end, 300); + + end: + return NULL; +} + +static void +del_wait_cb(evutil_socket_t fd, short event, void *arg) +{ + struct timeval delay = { 0, 300*1000 }; + TT_BLATHER(("Sleeping")); + evutil_usleep_(&delay); + test_ok = 1; +} + +static void +test_del_wait(void) +{ + struct event ev; + pthread_t thread; + + setup_test("event_del will wait: "); + + event_set(&ev, pair[1], EV_READ, del_wait_cb, &ev); + event_add(&ev, NULL); + + pthread_create(&thread, NULL, del_wait_thread, NULL); + + if (write(pair[0], TEST1, strlen(TEST1)+1) < 0) { + tt_fail_perror("write"); + } + + { + struct timeval delay = { 0, 30*1000 }; + evutil_usleep_(&delay); + } + + { + struct timeval tv_start, tv_end; + evutil_gettimeofday(&tv_start, NULL); + event_del(&ev); + evutil_gettimeofday(&tv_end, NULL); + test_timeval_diff_eq(&tv_start, &tv_end, 270); + } + + pthread_join(thread, NULL); + + end: + ; +} +#endif + +static void +signal_cb_sa(int sig) +{ + test_ok = 2; +} + +static void +signal_cb(evutil_socket_t fd, short event, void *arg) +{ + struct event *ev = arg; + + evsignal_del(ev); + test_ok = 1; +} + +static void +test_simplesignal_impl(int find_reorder) +{ + struct event ev; + struct itimerval itv; + + evsignal_set(&ev, SIGALRM, signal_cb, &ev); + evsignal_add(&ev, NULL); + /* find bugs in which operations are re-ordered */ + if (find_reorder) { + evsignal_del(&ev); + evsignal_add(&ev, NULL); + } + + memset(&itv, 0, sizeof(itv)); + itv.it_value.tv_sec = 0; + itv.it_value.tv_usec = 100000; + if (setitimer(ITIMER_REAL, &itv, NULL) == -1) + goto skip_simplesignal; + + event_dispatch(); + skip_simplesignal: + if (evsignal_del(&ev) == -1) + test_ok = 0; + + cleanup_test(); +} + +static void +test_simplestsignal(void) +{ + setup_test("Simplest one signal: "); + test_simplesignal_impl(0); +} + +static void +test_simplesignal(void) +{ + setup_test("Simple signal: "); + test_simplesignal_impl(1); +} + +static void +test_multiplesignal(void) +{ + struct event ev_one, ev_two; + struct itimerval itv; + + setup_test("Multiple signal: "); + + evsignal_set(&ev_one, SIGALRM, signal_cb, &ev_one); + evsignal_add(&ev_one, NULL); + + evsignal_set(&ev_two, SIGALRM, signal_cb, &ev_two); + evsignal_add(&ev_two, NULL); + + memset(&itv, 0, sizeof(itv)); + itv.it_value.tv_sec = 0; + itv.it_value.tv_usec = 100000; + if (setitimer(ITIMER_REAL, &itv, NULL) == -1) + goto skip_simplesignal; + + event_dispatch(); + + skip_simplesignal: + if (evsignal_del(&ev_one) == -1) + test_ok = 0; + if (evsignal_del(&ev_two) == -1) + test_ok = 0; + + cleanup_test(); +} + +static void +test_immediatesignal(void) +{ + struct event ev; + + test_ok = 0; + evsignal_set(&ev, SIGUSR1, signal_cb, &ev); + evsignal_add(&ev, NULL); + raise(SIGUSR1); + event_loop(EVLOOP_NONBLOCK); + evsignal_del(&ev); + cleanup_test(); +} + +static void +test_signal_dealloc(void) +{ + /* make sure that evsignal_event is event_del'ed and pipe closed */ + struct event ev; + struct event_base *base = event_init(); + evsignal_set(&ev, SIGUSR1, signal_cb, &ev); + evsignal_add(&ev, NULL); + evsignal_del(&ev); + event_base_free(base); + /* If we got here without asserting, we're fine. */ + test_ok = 1; + cleanup_test(); +} + +static void +test_signal_pipeloss(void) +{ + /* make sure that the base1 pipe is closed correctly. */ + struct event_base *base1, *base2; + int pipe1; + test_ok = 0; + base1 = event_init(); + pipe1 = base1->sig.ev_signal_pair[0]; + base2 = event_init(); + event_base_free(base2); + event_base_free(base1); + if (close(pipe1) != -1 || errno!=EBADF) { + /* fd must be closed, so second close gives -1, EBADF */ + printf("signal pipe not closed. "); + test_ok = 0; + } else { + test_ok = 1; + } + cleanup_test(); +} + +/* + * make two bases to catch signals, use both of them. this only works + * for event mechanisms that use our signal pipe trick. kqueue handles + * signals internally, and all interested kqueues get all the signals. + */ +static void +test_signal_switchbase(void) +{ + struct event ev1, ev2; + struct event_base *base1, *base2; + int is_kqueue; + test_ok = 0; + base1 = event_init(); + base2 = event_init(); + is_kqueue = !strcmp(event_get_method(),"kqueue"); + evsignal_set(&ev1, SIGUSR1, signal_cb, &ev1); + evsignal_set(&ev2, SIGUSR1, signal_cb, &ev2); + if (event_base_set(base1, &ev1) || + event_base_set(base2, &ev2) || + event_add(&ev1, NULL) || + event_add(&ev2, NULL)) { + fprintf(stderr, "%s: cannot set base, add\n", __func__); + exit(1); + } + + tt_ptr_op(event_get_base(&ev1), ==, base1); + tt_ptr_op(event_get_base(&ev2), ==, base2); + + test_ok = 0; + /* can handle signal before loop is called */ + raise(SIGUSR1); + event_base_loop(base2, EVLOOP_NONBLOCK); + if (is_kqueue) { + if (!test_ok) + goto end; + test_ok = 0; + } + event_base_loop(base1, EVLOOP_NONBLOCK); + if (test_ok && !is_kqueue) { + test_ok = 0; + + /* set base1 to handle signals */ + event_base_loop(base1, EVLOOP_NONBLOCK); + raise(SIGUSR1); + event_base_loop(base1, EVLOOP_NONBLOCK); + event_base_loop(base2, EVLOOP_NONBLOCK); + } +end: + event_base_free(base1); + event_base_free(base2); + cleanup_test(); +} + +/* + * assert that a signal event removed from the event queue really is + * removed - with no possibility of it's parent handler being fired. + */ +static void +test_signal_assert(void) +{ + struct event ev; + struct event_base *base = event_init(); + test_ok = 0; + /* use SIGCONT so we don't kill ourselves when we signal to nowhere */ + evsignal_set(&ev, SIGCONT, signal_cb, &ev); + evsignal_add(&ev, NULL); + /* + * if evsignal_del() fails to reset the handler, it's current handler + * will still point to evsig_handler(). + */ + evsignal_del(&ev); + + raise(SIGCONT); +#if 0 + /* only way to verify we were in evsig_handler() */ + /* XXXX Now there's no longer a good way. */ + if (base->sig.evsig_caught) + test_ok = 0; + else + test_ok = 1; +#else + test_ok = 1; +#endif + + event_base_free(base); + cleanup_test(); + return; +} + +/* + * assert that we restore our previous signal handler properly. + */ +static void +test_signal_restore(void) +{ + struct event ev; + struct event_base *base = event_init(); +#ifdef EVENT__HAVE_SIGACTION + struct sigaction sa; +#endif + + test_ok = 0; +#ifdef EVENT__HAVE_SIGACTION + sa.sa_handler = signal_cb_sa; + sa.sa_flags = 0x0; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGUSR1, &sa, NULL) == -1) + goto out; +#else + if (signal(SIGUSR1, signal_cb_sa) == SIG_ERR) + goto out; +#endif + evsignal_set(&ev, SIGUSR1, signal_cb, &ev); + evsignal_add(&ev, NULL); + evsignal_del(&ev); + + raise(SIGUSR1); + /* 1 == signal_cb, 2 == signal_cb_sa, we want our previous handler */ + if (test_ok != 2) + test_ok = 0; +out: + event_base_free(base); + cleanup_test(); + return; +} + +static void +signal_cb_swp(int sig, short event, void *arg) +{ + called++; + if (called < 5) + raise(sig); + else + event_loopexit(NULL); +} +static void +timeout_cb_swp(evutil_socket_t fd, short event, void *arg) +{ + if (called == -1) { + struct timeval tv = {5, 0}; + + called = 0; + evtimer_add((struct event *)arg, &tv); + raise(SIGUSR1); + return; + } + test_ok = 0; + event_loopexit(NULL); +} + +static void +test_signal_while_processing(void) +{ + struct event_base *base = event_init(); + struct event ev, ev_timer; + struct timeval tv = {0, 0}; + + setup_test("Receiving a signal while processing other signal: "); + + called = -1; + test_ok = 1; + signal_set(&ev, SIGUSR1, signal_cb_swp, NULL); + signal_add(&ev, NULL); + evtimer_set(&ev_timer, timeout_cb_swp, &ev_timer); + evtimer_add(&ev_timer, &tv); + event_dispatch(); + + event_base_free(base); + cleanup_test(); + return; +} +#endif + +static void +test_free_active_base(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event_base *base1; + struct event ev1; + + base1 = event_init(); + if (base1) { + event_assign(&ev1, base1, data->pair[1], EV_READ, + dummy_read_cb, NULL); + event_add(&ev1, NULL); + event_base_free(base1); /* should not crash */ + } else { + tt_fail_msg("failed to create event_base for test"); + } + + base1 = event_init(); + tt_assert(base1); + event_assign(&ev1, base1, 0, 0, dummy_read_cb, NULL); + event_active(&ev1, EV_READ, 1); + event_base_free(base1); +end: + ; +} + +static void +test_manipulate_active_events(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event_base *base = data->base; + struct event ev1; + + event_assign(&ev1, base, -1, EV_TIMEOUT, dummy_read_cb, NULL); + + /* Make sure an active event is pending. */ + event_active(&ev1, EV_READ, 1); + tt_int_op(event_pending(&ev1, EV_READ|EV_TIMEOUT|EV_WRITE, NULL), + ==, EV_READ); + + /* Make sure that activating an event twice works. */ + event_active(&ev1, EV_WRITE, 1); + tt_int_op(event_pending(&ev1, EV_READ|EV_TIMEOUT|EV_WRITE, NULL), + ==, EV_READ|EV_WRITE); + +end: + event_del(&ev1); +} + +static void +event_selfarg_cb(evutil_socket_t fd, short event, void *arg) +{ + struct event *ev = arg; + struct event_base *base = event_get_base(ev); + event_base_assert_ok_(base); + event_base_loopexit(base, NULL); + tt_want(ev == event_base_get_running_event(base)); +} + +static void +test_event_new_selfarg(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event_base *base = data->base; + struct event *ev = event_new(base, -1, EV_READ, event_selfarg_cb, + event_self_cbarg()); + + event_active(ev, EV_READ, 1); + event_base_dispatch(base); + + event_free(ev); +} + +static void +test_event_assign_selfarg(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event_base *base = data->base; + struct event ev; + + event_assign(&ev, base, -1, EV_READ, event_selfarg_cb, + event_self_cbarg()); + event_active(&ev, EV_READ, 1); + event_base_dispatch(base); +} + +static void +test_event_base_get_num_events(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event_base *base = data->base; + struct event ev; + int event_count_active; + int event_count_virtual; + int event_count_added; + int event_count_active_virtual; + int event_count_active_added; + int event_count_virtual_added; + int event_count_active_added_virtual; + + struct timeval qsec = {0, 100000}; + + event_assign(&ev, base, -1, EV_READ, event_selfarg_cb, + event_self_cbarg()); + + event_add(&ev, &qsec); + event_count_active = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE); + event_count_virtual = event_base_get_num_events(base, + EVENT_BASE_COUNT_VIRTUAL); + event_count_added = event_base_get_num_events(base, + EVENT_BASE_COUNT_ADDED); + event_count_active_virtual = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE|EVENT_BASE_COUNT_VIRTUAL); + event_count_active_added = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE|EVENT_BASE_COUNT_ADDED); + event_count_virtual_added = event_base_get_num_events(base, + EVENT_BASE_COUNT_VIRTUAL|EVENT_BASE_COUNT_ADDED); + event_count_active_added_virtual = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE| + EVENT_BASE_COUNT_ADDED| + EVENT_BASE_COUNT_VIRTUAL); + tt_int_op(event_count_active, ==, 0); + tt_int_op(event_count_virtual, ==, 0); + /* libevent itself adds a timeout event, so the event_count is 2 here */ + tt_int_op(event_count_added, ==, 2); + tt_int_op(event_count_active_virtual, ==, 0); + tt_int_op(event_count_active_added, ==, 2); + tt_int_op(event_count_virtual_added, ==, 2); + tt_int_op(event_count_active_added_virtual, ==, 2); + + event_active(&ev, EV_READ, 1); + event_count_active = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE); + event_count_virtual = event_base_get_num_events(base, + EVENT_BASE_COUNT_VIRTUAL); + event_count_added = event_base_get_num_events(base, + EVENT_BASE_COUNT_ADDED); + event_count_active_virtual = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE|EVENT_BASE_COUNT_VIRTUAL); + event_count_active_added = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE|EVENT_BASE_COUNT_ADDED); + event_count_virtual_added = event_base_get_num_events(base, + EVENT_BASE_COUNT_VIRTUAL|EVENT_BASE_COUNT_ADDED); + event_count_active_added_virtual = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE| + EVENT_BASE_COUNT_ADDED| + EVENT_BASE_COUNT_VIRTUAL); + tt_int_op(event_count_active, ==, 1); + tt_int_op(event_count_virtual, ==, 0); + tt_int_op(event_count_added, ==, 3); + tt_int_op(event_count_active_virtual, ==, 1); + tt_int_op(event_count_active_added, ==, 4); + tt_int_op(event_count_virtual_added, ==, 3); + tt_int_op(event_count_active_added_virtual, ==, 4); + + event_base_loop(base, 0); + event_count_active = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE); + event_count_virtual = event_base_get_num_events(base, + EVENT_BASE_COUNT_VIRTUAL); + event_count_added = event_base_get_num_events(base, + EVENT_BASE_COUNT_ADDED); + event_count_active_virtual = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE|EVENT_BASE_COUNT_VIRTUAL); + event_count_active_added = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE|EVENT_BASE_COUNT_ADDED); + event_count_virtual_added = event_base_get_num_events(base, + EVENT_BASE_COUNT_VIRTUAL|EVENT_BASE_COUNT_ADDED); + event_count_active_added_virtual = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE| + EVENT_BASE_COUNT_ADDED| + EVENT_BASE_COUNT_VIRTUAL); + tt_int_op(event_count_active, ==, 0); + tt_int_op(event_count_virtual, ==, 0); + tt_int_op(event_count_added, ==, 0); + tt_int_op(event_count_active_virtual, ==, 0); + tt_int_op(event_count_active_added, ==, 0); + tt_int_op(event_count_virtual_added, ==, 0); + tt_int_op(event_count_active_added_virtual, ==, 0); + + event_base_add_virtual_(base); + event_count_active = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE); + event_count_virtual = event_base_get_num_events(base, + EVENT_BASE_COUNT_VIRTUAL); + event_count_added = event_base_get_num_events(base, + EVENT_BASE_COUNT_ADDED); + event_count_active_virtual = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE|EVENT_BASE_COUNT_VIRTUAL); + event_count_active_added = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE|EVENT_BASE_COUNT_ADDED); + event_count_virtual_added = event_base_get_num_events(base, + EVENT_BASE_COUNT_VIRTUAL|EVENT_BASE_COUNT_ADDED); + event_count_active_added_virtual = event_base_get_num_events(base, + EVENT_BASE_COUNT_ACTIVE| + EVENT_BASE_COUNT_ADDED| + EVENT_BASE_COUNT_VIRTUAL); + tt_int_op(event_count_active, ==, 0); + tt_int_op(event_count_virtual, ==, 1); + tt_int_op(event_count_added, ==, 0); + tt_int_op(event_count_active_virtual, ==, 1); + tt_int_op(event_count_active_added, ==, 0); + tt_int_op(event_count_virtual_added, ==, 1); + tt_int_op(event_count_active_added_virtual, ==, 1); + +end: + ; +} + +static void +test_event_base_get_max_events(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event_base *base = data->base; + struct event ev; + struct event ev2; + int event_count_active; + int event_count_virtual; + int event_count_added; + int event_count_active_virtual; + int event_count_active_added; + int event_count_virtual_added; + int event_count_active_added_virtual; + + struct timeval qsec = {0, 100000}; + + event_assign(&ev, base, -1, EV_READ, event_selfarg_cb, + event_self_cbarg()); + event_assign(&ev2, base, -1, EV_READ, event_selfarg_cb, + event_self_cbarg()); + + event_add(&ev, &qsec); + event_add(&ev2, &qsec); + event_del(&ev2); + + event_count_active = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE, 0); + event_count_virtual = event_base_get_max_events(base, + EVENT_BASE_COUNT_VIRTUAL, 0); + event_count_added = event_base_get_max_events(base, + EVENT_BASE_COUNT_ADDED, 0); + event_count_active_virtual = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE | EVENT_BASE_COUNT_VIRTUAL, 0); + event_count_active_added = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE | EVENT_BASE_COUNT_ADDED, 0); + event_count_virtual_added = event_base_get_max_events(base, + EVENT_BASE_COUNT_VIRTUAL | EVENT_BASE_COUNT_ADDED, 0); + event_count_active_added_virtual = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE | + EVENT_BASE_COUNT_ADDED | + EVENT_BASE_COUNT_VIRTUAL, 0); + + tt_int_op(event_count_active, ==, 0); + tt_int_op(event_count_virtual, ==, 0); + /* libevent itself adds a timeout event, so the event_count is 4 here */ + tt_int_op(event_count_added, ==, 4); + tt_int_op(event_count_active_virtual, ==, 0); + tt_int_op(event_count_active_added, ==, 4); + tt_int_op(event_count_virtual_added, ==, 4); + tt_int_op(event_count_active_added_virtual, ==, 4); + + event_active(&ev, EV_READ, 1); + event_count_active = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE, 0); + event_count_virtual = event_base_get_max_events(base, + EVENT_BASE_COUNT_VIRTUAL, 0); + event_count_added = event_base_get_max_events(base, + EVENT_BASE_COUNT_ADDED, 0); + event_count_active_virtual = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE | EVENT_BASE_COUNT_VIRTUAL, 0); + event_count_active_added = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE | EVENT_BASE_COUNT_ADDED, 0); + event_count_virtual_added = event_base_get_max_events(base, + EVENT_BASE_COUNT_VIRTUAL | EVENT_BASE_COUNT_ADDED, 0); + event_count_active_added_virtual = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE | + EVENT_BASE_COUNT_ADDED | + EVENT_BASE_COUNT_VIRTUAL, 0); + + tt_int_op(event_count_active, ==, 1); + tt_int_op(event_count_virtual, ==, 0); + tt_int_op(event_count_added, ==, 4); + tt_int_op(event_count_active_virtual, ==, 1); + tt_int_op(event_count_active_added, ==, 5); + tt_int_op(event_count_virtual_added, ==, 4); + tt_int_op(event_count_active_added_virtual, ==, 5); + + event_base_loop(base, 0); + event_count_active = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE, 1); + event_count_virtual = event_base_get_max_events(base, + EVENT_BASE_COUNT_VIRTUAL, 1); + event_count_added = event_base_get_max_events(base, + EVENT_BASE_COUNT_ADDED, 1); + event_count_active_virtual = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE | EVENT_BASE_COUNT_VIRTUAL, 0); + event_count_active_added = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE | EVENT_BASE_COUNT_ADDED, 0); + event_count_virtual_added = event_base_get_max_events(base, + EVENT_BASE_COUNT_VIRTUAL | EVENT_BASE_COUNT_ADDED, 0); + event_count_active_added_virtual = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE | + EVENT_BASE_COUNT_ADDED | + EVENT_BASE_COUNT_VIRTUAL, 1); + + tt_int_op(event_count_active, ==, 1); + tt_int_op(event_count_virtual, ==, 0); + tt_int_op(event_count_added, ==, 4); + tt_int_op(event_count_active_virtual, ==, 0); + tt_int_op(event_count_active_added, ==, 0); + tt_int_op(event_count_virtual_added, ==, 0); + tt_int_op(event_count_active_added_virtual, ==, 0); + + event_count_active = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE, 0); + event_count_virtual = event_base_get_max_events(base, + EVENT_BASE_COUNT_VIRTUAL, 0); + event_count_added = event_base_get_max_events(base, + EVENT_BASE_COUNT_ADDED, 0); + tt_int_op(event_count_active, ==, 0); + tt_int_op(event_count_virtual, ==, 0); + tt_int_op(event_count_added, ==, 0); + + event_base_add_virtual_(base); + event_count_active = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE, 0); + event_count_virtual = event_base_get_max_events(base, + EVENT_BASE_COUNT_VIRTUAL, 0); + event_count_added = event_base_get_max_events(base, + EVENT_BASE_COUNT_ADDED, 0); + event_count_active_virtual = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE | EVENT_BASE_COUNT_VIRTUAL, 0); + event_count_active_added = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE | EVENT_BASE_COUNT_ADDED, 0); + event_count_virtual_added = event_base_get_max_events(base, + EVENT_BASE_COUNT_VIRTUAL | EVENT_BASE_COUNT_ADDED, 0); + event_count_active_added_virtual = event_base_get_max_events(base, + EVENT_BASE_COUNT_ACTIVE | + EVENT_BASE_COUNT_ADDED | + EVENT_BASE_COUNT_VIRTUAL, 0); + + tt_int_op(event_count_active, ==, 0); + tt_int_op(event_count_virtual, ==, 1); + tt_int_op(event_count_added, ==, 0); + tt_int_op(event_count_active_virtual, ==, 1); + tt_int_op(event_count_active_added, ==, 0); + tt_int_op(event_count_virtual_added, ==, 1); + tt_int_op(event_count_active_added_virtual, ==, 1); + +end: + ; +} + +static void +test_bad_assign(void *ptr) +{ + struct event ev; + int r; + /* READ|SIGNAL is not allowed */ + r = event_assign(&ev, NULL, -1, EV_SIGNAL|EV_READ, dummy_read_cb, NULL); + tt_int_op(r,==,-1); + +end: + ; +} + +static int reentrant_cb_run = 0; + +static void +bad_reentrant_run_loop_cb(evutil_socket_t fd, short what, void *ptr) +{ + struct event_base *base = ptr; + int r; + reentrant_cb_run = 1; + /* This reentrant call to event_base_loop should be detected and + * should fail */ + r = event_base_loop(base, 0); + tt_int_op(r, ==, -1); +end: + ; +} + +static void +test_bad_reentrant(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event_base *base = data->base; + struct event ev; + int r; + event_assign(&ev, base, -1, + 0, bad_reentrant_run_loop_cb, base); + + event_active(&ev, EV_WRITE, 1); + r = event_base_loop(base, 0); + tt_int_op(r, ==, 1); + tt_int_op(reentrant_cb_run, ==, 1); +end: + ; +} + +static int n_write_a_byte_cb=0; +static int n_read_and_drain_cb=0; +static int n_activate_other_event_cb=0; +static void +write_a_byte_cb(evutil_socket_t fd, short what, void *arg) +{ + char buf[] = "x"; + if (write(fd, buf, 1) == 1) + ++n_write_a_byte_cb; +} +static void +read_and_drain_cb(evutil_socket_t fd, short what, void *arg) +{ + char buf[128]; + int n; + ++n_read_and_drain_cb; + while ((n = read(fd, buf, sizeof(buf))) > 0) + ; +} + +static void +activate_other_event_cb(evutil_socket_t fd, short what, void *other_) +{ + struct event *ev_activate = other_; + ++n_activate_other_event_cb; + event_active_later_(ev_activate, EV_READ); +} + +static void +test_active_later(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event *ev1 = NULL, *ev2 = NULL; + struct event ev3, ev4; + struct timeval qsec = {0, 100000}; + ev1 = event_new(data->base, data->pair[0], EV_READ|EV_PERSIST, read_and_drain_cb, NULL); + ev2 = event_new(data->base, data->pair[1], EV_WRITE|EV_PERSIST, write_a_byte_cb, NULL); + event_assign(&ev3, data->base, -1, 0, activate_other_event_cb, &ev4); + event_assign(&ev4, data->base, -1, 0, activate_other_event_cb, &ev3); + event_add(ev1, NULL); + event_add(ev2, NULL); + event_active_later_(&ev3, EV_READ); + + event_base_loopexit(data->base, &qsec); + + event_base_loop(data->base, 0); + + TT_BLATHER(("%d write calls, %d read calls, %d activate-other calls.", + n_write_a_byte_cb, n_read_and_drain_cb, n_activate_other_event_cb)); + event_del(&ev3); + event_del(&ev4); + + tt_int_op(n_write_a_byte_cb, ==, n_activate_other_event_cb); + tt_int_op(n_write_a_byte_cb, >, 100); + tt_int_op(n_read_and_drain_cb, >, 100); + tt_int_op(n_activate_other_event_cb, >, 100); + + event_active_later_(&ev4, EV_READ); + event_active(&ev4, EV_READ, 1); /* This should make the event + active immediately. */ + tt_assert((ev4.ev_flags & EVLIST_ACTIVE) != 0); + tt_assert((ev4.ev_flags & EVLIST_ACTIVE_LATER) == 0); + + /* Now leave this one around, so that event_free sees it and removes + * it. */ + event_active_later_(&ev3, EV_READ); + event_base_assert_ok_(data->base); + +end: + if (ev1) + event_free(ev1); + if (ev2) + event_free(ev2); + + event_base_free(data->base); + data->base = NULL; +} + + +static void incr_arg_cb(evutil_socket_t fd, short what, void *arg) +{ + int *intptr = arg; + (void) fd; (void) what; + ++*intptr; +} +static void remove_timers_cb(evutil_socket_t fd, short what, void *arg) +{ + struct event **ep = arg; + (void) fd; (void) what; + event_remove_timer(ep[0]); + event_remove_timer(ep[1]); +} +static void send_a_byte_cb(evutil_socket_t fd, short what, void *arg) +{ + evutil_socket_t *sockp = arg; + (void) fd; (void) what; + (void) write(*sockp, "A", 1); +} +struct read_not_timeout_param +{ + struct event **ev; + int events; + int count; +}; +static void read_not_timeout_cb(evutil_socket_t fd, short what, void *arg) +{ + struct read_not_timeout_param *rntp = arg; + char c; + ev_ssize_t n; + (void) fd; (void) what; + n = read(fd, &c, 1); + tt_int_op(n, ==, 1); + rntp->events |= what; + ++rntp->count; + if(2 == rntp->count) event_del(rntp->ev[0]); +end: + ; +} + +static void +test_event_remove_timeout(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event_base *base = data->base; + struct event *ev[5]; + int ev1_fired=0; + struct timeval ms25 = { 0, 25*1000 }, + ms40 = { 0, 40*1000 }, + ms75 = { 0, 75*1000 }, + ms125 = { 0, 125*1000 }; + struct read_not_timeout_param rntp = { ev, 0, 0 }; + + event_base_assert_ok_(base); + + ev[0] = event_new(base, data->pair[0], EV_READ|EV_PERSIST, + read_not_timeout_cb, &rntp); + ev[1] = evtimer_new(base, incr_arg_cb, &ev1_fired); + ev[2] = evtimer_new(base, remove_timers_cb, ev); + ev[3] = evtimer_new(base, send_a_byte_cb, &data->pair[1]); + ev[4] = evtimer_new(base, send_a_byte_cb, &data->pair[1]); + tt_assert(base); + event_add(ev[2], &ms25); /* remove timers */ + event_add(ev[4], &ms40); /* write to test if timer re-activates */ + event_add(ev[0], &ms75); /* read */ + event_add(ev[1], &ms75); /* timer */ + event_add(ev[3], &ms125); /* timeout. */ + event_base_assert_ok_(base); + + event_base_dispatch(base); + + tt_int_op(ev1_fired, ==, 0); + tt_int_op(rntp.events, ==, EV_READ); + + event_base_assert_ok_(base); +end: + event_free(ev[0]); + event_free(ev[1]); + event_free(ev[2]); + event_free(ev[3]); + event_free(ev[4]); +} + +static void +test_event_base_new(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event_base *base = 0; + struct event ev1; + struct basic_cb_args args; + + int towrite = (int)strlen(TEST1)+1; + int len = write(data->pair[0], TEST1, towrite); + + if (len < 0) + tt_abort_perror("initial write"); + else if (len != towrite) + tt_abort_printf(("initial write fell short (%d of %d bytes)", + len, towrite)); + + if (shutdown(data->pair[0], SHUT_WR)) + tt_abort_perror("initial write shutdown"); + + base = event_base_new(); + if (!base) + tt_abort_msg("failed to create event base"); + + args.eb = base; + args.ev = &ev1; + args.callcount = 0; + event_assign(&ev1, base, data->pair[1], + EV_READ|EV_PERSIST, basic_read_cb, &args); + + if (event_add(&ev1, NULL)) + tt_abort_perror("initial event_add"); + + if (event_base_loop(base, 0)) + tt_abort_msg("unsuccessful exit from event loop"); + +end: + if (base) + event_base_free(base); +} + +static void +test_loopexit(void) +{ + struct timeval tv, tv_start, tv_end; + struct event ev; + + setup_test("Loop exit: "); + + tv.tv_usec = 0; + tv.tv_sec = 60*60*24; + evtimer_set(&ev, timeout_cb, NULL); + evtimer_add(&ev, &tv); + + tv.tv_usec = 300*1000; + tv.tv_sec = 0; + event_loopexit(&tv); + + evutil_gettimeofday(&tv_start, NULL); + event_dispatch(); + evutil_gettimeofday(&tv_end, NULL); + + evtimer_del(&ev); + + tt_assert(event_base_got_exit(global_base)); + tt_assert(!event_base_got_break(global_base)); + + test_timeval_diff_eq(&tv_start, &tv_end, 300); + + test_ok = 1; +end: + cleanup_test(); +} + +static void +test_loopexit_multiple(void) +{ + struct timeval tv, tv_start, tv_end; + struct event_base *base; + + setup_test("Loop Multiple exit: "); + + base = event_base_new(); + + tv.tv_usec = 200*1000; + tv.tv_sec = 0; + event_base_loopexit(base, &tv); + + tv.tv_usec = 0; + tv.tv_sec = 3; + event_base_loopexit(base, &tv); + + evutil_gettimeofday(&tv_start, NULL); + event_base_dispatch(base); + evutil_gettimeofday(&tv_end, NULL); + + tt_assert(event_base_got_exit(base)); + tt_assert(!event_base_got_break(base)); + + event_base_free(base); + + test_timeval_diff_eq(&tv_start, &tv_end, 200); + + test_ok = 1; + +end: + cleanup_test(); +} + +static void +break_cb(evutil_socket_t fd, short events, void *arg) +{ + test_ok = 1; + event_loopbreak(); +} + +static void +fail_cb(evutil_socket_t fd, short events, void *arg) +{ + test_ok = 0; +} + +static void +test_loopbreak(void) +{ + struct event ev1, ev2; + struct timeval tv; + + setup_test("Loop break: "); + + tv.tv_sec = 0; + tv.tv_usec = 0; + evtimer_set(&ev1, break_cb, NULL); + evtimer_add(&ev1, &tv); + evtimer_set(&ev2, fail_cb, NULL); + evtimer_add(&ev2, &tv); + + event_dispatch(); + + tt_assert(!event_base_got_exit(global_base)); + tt_assert(event_base_got_break(global_base)); + + evtimer_del(&ev1); + evtimer_del(&ev2); + +end: + cleanup_test(); +} + +static struct event *readd_test_event_last_added = NULL; +static void +re_add_read_cb(evutil_socket_t fd, short event, void *arg) +{ + char buf[256]; + struct event *ev_other = arg; + ev_ssize_t n_read; + + readd_test_event_last_added = ev_other; + + n_read = read(fd, buf, sizeof(buf)); + + if (n_read < 0) { + tt_fail_perror("read"); + event_base_loopbreak(event_get_base(ev_other)); + return; + } else { + event_add(ev_other, NULL); + ++test_ok; + } +} + +static void +test_nonpersist_readd(void) +{ + struct event ev1, ev2; + + setup_test("Re-add nonpersistent events: "); + event_set(&ev1, pair[0], EV_READ, re_add_read_cb, &ev2); + event_set(&ev2, pair[1], EV_READ, re_add_read_cb, &ev1); + + if (write(pair[0], "Hello", 5) < 0) { + tt_fail_perror("write(pair[0])"); + } + + if (write(pair[1], "Hello", 5) < 0) { + tt_fail_perror("write(pair[1])\n"); + } + + if (event_add(&ev1, NULL) == -1 || + event_add(&ev2, NULL) == -1) { + test_ok = 0; + } + if (test_ok != 0) + exit(1); + event_loop(EVLOOP_ONCE); + if (test_ok != 2) + exit(1); + /* At this point, we executed both callbacks. Whichever one got + * called first added the second, but the second then immediately got + * deleted before its callback was called. At this point, though, it + * re-added the first. + */ + if (!readd_test_event_last_added) { + test_ok = 0; + } else if (readd_test_event_last_added == &ev1) { + if (!event_pending(&ev1, EV_READ, NULL) || + event_pending(&ev2, EV_READ, NULL)) + test_ok = 0; + } else { + if (event_pending(&ev1, EV_READ, NULL) || + !event_pending(&ev2, EV_READ, NULL)) + test_ok = 0; + } + + event_del(&ev1); + event_del(&ev2); + + cleanup_test(); +} + +struct test_pri_event { + struct event ev; + int count; +}; + +static void +test_priorities_cb(evutil_socket_t fd, short what, void *arg) +{ + struct test_pri_event *pri = arg; + struct timeval tv; + + if (pri->count == 3) { + event_loopexit(NULL); + return; + } + + pri->count++; + + evutil_timerclear(&tv); + event_add(&pri->ev, &tv); +} + +static void +test_priorities_impl(int npriorities) +{ + struct test_pri_event one, two; + struct timeval tv; + + TT_BLATHER(("Testing Priorities %d: ", npriorities)); + + event_base_priority_init(global_base, npriorities); + + memset(&one, 0, sizeof(one)); + memset(&two, 0, sizeof(two)); + + timeout_set(&one.ev, test_priorities_cb, &one); + if (event_priority_set(&one.ev, 0) == -1) { + fprintf(stderr, "%s: failed to set priority", __func__); + exit(1); + } + + timeout_set(&two.ev, test_priorities_cb, &two); + if (event_priority_set(&two.ev, npriorities - 1) == -1) { + fprintf(stderr, "%s: failed to set priority", __func__); + exit(1); + } + + evutil_timerclear(&tv); + + if (event_add(&one.ev, &tv) == -1) + exit(1); + if (event_add(&two.ev, &tv) == -1) + exit(1); + + event_dispatch(); + + event_del(&one.ev); + event_del(&two.ev); + + if (npriorities == 1) { + if (one.count == 3 && two.count == 3) + test_ok = 1; + } else if (npriorities == 2) { + /* Two is called once because event_loopexit is priority 1 */ + if (one.count == 3 && two.count == 1) + test_ok = 1; + } else { + if (one.count == 3 && two.count == 0) + test_ok = 1; + } +} + +static void +test_priorities(void) +{ + test_priorities_impl(1); + if (test_ok) + test_priorities_impl(2); + if (test_ok) + test_priorities_impl(3); +} + +/* priority-active-inversion: activate a higher-priority event, and make sure + * it keeps us from running a lower-priority event first. */ +static int n_pai_calls = 0; +static struct event pai_events[3]; + +static void +prio_active_inversion_cb(evutil_socket_t fd, short what, void *arg) +{ + int *call_order = arg; + *call_order = n_pai_calls++; + if (n_pai_calls == 1) { + /* This should activate later, even though it shares a + priority with us. */ + event_active(&pai_events[1], EV_READ, 1); + /* This should activate next, since its priority is higher, + even though we activated it second. */ + event_active(&pai_events[2], EV_TIMEOUT, 1); + } +} + +static void +test_priority_active_inversion(void *data_) +{ + struct basic_test_data *data = data_; + struct event_base *base = data->base; + int call_order[3]; + int i; + tt_int_op(event_base_priority_init(base, 8), ==, 0); + + n_pai_calls = 0; + memset(call_order, 0, sizeof(call_order)); + + for (i=0;i<3;++i) { + event_assign(&pai_events[i], data->base, -1, 0, + prio_active_inversion_cb, &call_order[i]); + } + + event_priority_set(&pai_events[0], 4); + event_priority_set(&pai_events[1], 4); + event_priority_set(&pai_events[2], 0); + + event_active(&pai_events[0], EV_WRITE, 1); + + event_base_dispatch(base); + tt_int_op(n_pai_calls, ==, 3); + tt_int_op(call_order[0], ==, 0); + tt_int_op(call_order[1], ==, 2); + tt_int_op(call_order[2], ==, 1); +end: + ; +} + + +static void +test_multiple_cb(evutil_socket_t fd, short event, void *arg) +{ + if (event & EV_READ) + test_ok |= 1; + else if (event & EV_WRITE) + test_ok |= 2; +} + +static void +test_multiple_events_for_same_fd(void) +{ + struct event e1, e2; + + setup_test("Multiple events for same fd: "); + + event_set(&e1, pair[0], EV_READ, test_multiple_cb, NULL); + event_add(&e1, NULL); + event_set(&e2, pair[0], EV_WRITE, test_multiple_cb, NULL); + event_add(&e2, NULL); + event_loop(EVLOOP_ONCE); + event_del(&e2); + + if (write(pair[1], TEST1, strlen(TEST1)+1) < 0) { + tt_fail_perror("write"); + } + + event_loop(EVLOOP_ONCE); + event_del(&e1); + + if (test_ok != 3) + test_ok = 0; + + cleanup_test(); +} + +int evtag_decode_int(ev_uint32_t *pnumber, struct evbuffer *evbuf); +int evtag_decode_int64(ev_uint64_t *pnumber, struct evbuffer *evbuf); +int evtag_encode_tag(struct evbuffer *evbuf, ev_uint32_t number); +int evtag_decode_tag(ev_uint32_t *pnumber, struct evbuffer *evbuf); + +static void +read_once_cb(evutil_socket_t fd, short event, void *arg) +{ + char buf[256]; + int len; + + len = read(fd, buf, sizeof(buf)); + + if (called) { + test_ok = 0; + } else if (len) { + /* Assumes global pair[0] can be used for writing */ + if (write(pair[0], TEST1, strlen(TEST1)+1) < 0) { + tt_fail_perror("write"); + test_ok = 0; + } else { + test_ok = 1; + } + } + + called++; +} + +static void +test_want_only_once(void) +{ + struct event ev; + struct timeval tv; + + /* Very simple read test */ + setup_test("Want read only once: "); + + if (write(pair[0], TEST1, strlen(TEST1)+1) < 0) { + tt_fail_perror("write"); + } + + /* Setup the loop termination */ + evutil_timerclear(&tv); + tv.tv_usec = 300*1000; + event_loopexit(&tv); + + event_set(&ev, pair[1], EV_READ, read_once_cb, &ev); + if (event_add(&ev, NULL) == -1) + exit(1); + event_dispatch(); + + cleanup_test(); +} + +#define TEST_MAX_INT 6 + +static void +evtag_int_test(void *ptr) +{ + struct evbuffer *tmp = evbuffer_new(); + ev_uint32_t integers[TEST_MAX_INT] = { + 0xaf0, 0x1000, 0x1, 0xdeadbeef, 0x00, 0xbef000 + }; + ev_uint32_t integer; + ev_uint64_t big_int; + int i; + + evtag_init(); + + for (i = 0; i < TEST_MAX_INT; i++) { + int oldlen, newlen; + oldlen = (int)EVBUFFER_LENGTH(tmp); + evtag_encode_int(tmp, integers[i]); + newlen = (int)EVBUFFER_LENGTH(tmp); + TT_BLATHER(("encoded 0x%08x with %d bytes", + (unsigned)integers[i], newlen - oldlen)); + big_int = integers[i]; + big_int *= 1000000000; /* 1 billion */ + evtag_encode_int64(tmp, big_int); + } + + for (i = 0; i < TEST_MAX_INT; i++) { + tt_int_op(evtag_decode_int(&integer, tmp), !=, -1); + tt_uint_op(integer, ==, integers[i]); + tt_int_op(evtag_decode_int64(&big_int, tmp), !=, -1); + tt_assert((big_int / 1000000000) == integers[i]); + } + + tt_uint_op(EVBUFFER_LENGTH(tmp), ==, 0); +end: + evbuffer_free(tmp); +} + +static void +evtag_fuzz(void *ptr) +{ + unsigned char buffer[4096]; + struct evbuffer *tmp = evbuffer_new(); + struct timeval tv; + int i, j; + + int not_failed = 0; + + evtag_init(); + + for (j = 0; j < 100; j++) { + for (i = 0; i < (int)sizeof(buffer); i++) + buffer[i] = test_weakrand(); + evbuffer_drain(tmp, -1); + evbuffer_add(tmp, buffer, sizeof(buffer)); + + if (evtag_unmarshal_timeval(tmp, 0, &tv) != -1) + not_failed++; + } + + /* The majority of decodes should fail */ + tt_int_op(not_failed, <, 10); + + /* Now insert some corruption into the tag length field */ + evbuffer_drain(tmp, -1); + evutil_timerclear(&tv); + tv.tv_sec = 1; + evtag_marshal_timeval(tmp, 0, &tv); + evbuffer_add(tmp, buffer, sizeof(buffer)); + + ((char *)EVBUFFER_DATA(tmp))[1] = '\xff'; + if (evtag_unmarshal_timeval(tmp, 0, &tv) != -1) { + tt_abort_msg("evtag_unmarshal_timeval should have failed"); + } + +end: + evbuffer_free(tmp); +} + +static void +evtag_tag_encoding(void *ptr) +{ + struct evbuffer *tmp = evbuffer_new(); + ev_uint32_t integers[TEST_MAX_INT] = { + 0xaf0, 0x1000, 0x1, 0xdeadbeef, 0x00, 0xbef000 + }; + ev_uint32_t integer; + int i; + + evtag_init(); + + for (i = 0; i < TEST_MAX_INT; i++) { + int oldlen, newlen; + oldlen = (int)EVBUFFER_LENGTH(tmp); + evtag_encode_tag(tmp, integers[i]); + newlen = (int)EVBUFFER_LENGTH(tmp); + TT_BLATHER(("encoded 0x%08x with %d bytes", + (unsigned)integers[i], newlen - oldlen)); + } + + for (i = 0; i < TEST_MAX_INT; i++) { + tt_int_op(evtag_decode_tag(&integer, tmp), !=, -1); + tt_uint_op(integer, ==, integers[i]); + } + + tt_uint_op(EVBUFFER_LENGTH(tmp), ==, 0); + +end: + evbuffer_free(tmp); +} + +static void +evtag_test_peek(void *ptr) +{ + struct evbuffer *tmp = evbuffer_new(); + ev_uint32_t u32; + + evtag_marshal_int(tmp, 30, 0); + evtag_marshal_string(tmp, 40, "Hello world"); + + tt_int_op(evtag_peek(tmp, &u32), ==, 1); + tt_int_op(u32, ==, 30); + tt_int_op(evtag_peek_length(tmp, &u32), ==, 0); + tt_int_op(u32, ==, 1+1+1); + tt_int_op(evtag_consume(tmp), ==, 0); + + tt_int_op(evtag_peek(tmp, &u32), ==, 1); + tt_int_op(u32, ==, 40); + tt_int_op(evtag_peek_length(tmp, &u32), ==, 0); + tt_int_op(u32, ==, 1+1+11); + tt_int_op(evtag_payload_length(tmp, &u32), ==, 0); + tt_int_op(u32, ==, 11); + +end: + evbuffer_free(tmp); +} + + +static void +test_methods(void *ptr) +{ + const char **methods = event_get_supported_methods(); + struct event_config *cfg = NULL; + struct event_base *base = NULL; + const char *backend; + int n_methods = 0; + + tt_assert(methods); + + backend = methods[0]; + while (*methods != NULL) { + TT_BLATHER(("Support method: %s", *methods)); + ++methods; + ++n_methods; + } + + cfg = event_config_new(); + assert(cfg != NULL); + + tt_int_op(event_config_avoid_method(cfg, backend), ==, 0); + event_config_set_flag(cfg, EVENT_BASE_FLAG_IGNORE_ENV); + + base = event_base_new_with_config(cfg); + if (n_methods > 1) { + tt_assert(base); + tt_str_op(backend, !=, event_base_get_method(base)); + } else { + tt_assert(base == NULL); + } + +end: + if (base) + event_base_free(base); + if (cfg) + event_config_free(cfg); +} + +static void +test_version(void *arg) +{ + const char *vstr; + ev_uint32_t vint; + int major, minor, patch, n; + + vstr = event_get_version(); + vint = event_get_version_number(); + + tt_assert(vstr); + tt_assert(vint); + + tt_str_op(vstr, ==, LIBEVENT_VERSION); + tt_int_op(vint, ==, LIBEVENT_VERSION_NUMBER); + + n = sscanf(vstr, "%d.%d.%d", &major, &minor, &patch); + tt_assert(3 == n); + tt_int_op((vint&0xffffff00), ==, ((major<<24)|(minor<<16)|(patch<<8))); +end: + ; +} + +static void +test_base_features(void *arg) +{ + struct event_base *base = NULL; + struct event_config *cfg = NULL; + + cfg = event_config_new(); + + tt_assert(0 == event_config_require_features(cfg, EV_FEATURE_ET)); + + base = event_base_new_with_config(cfg); + if (base) { + tt_int_op(EV_FEATURE_ET, ==, + event_base_get_features(base) & EV_FEATURE_ET); + } else { + base = event_base_new(); + tt_int_op(0, ==, event_base_get_features(base) & EV_FEATURE_ET); + } + +end: + if (base) + event_base_free(base); + if (cfg) + event_config_free(cfg); +} + +#ifdef EVENT__HAVE_SETENV +#define SETENV_OK +#elif !defined(EVENT__HAVE_SETENV) && defined(EVENT__HAVE_PUTENV) +static void setenv(const char *k, const char *v, int o_) +{ + char b[256]; + evutil_snprintf(b, sizeof(b), "%s=%s",k,v); + putenv(b); +} +#define SETENV_OK +#endif + +#ifdef EVENT__HAVE_UNSETENV +#define UNSETENV_OK +#elif !defined(EVENT__HAVE_UNSETENV) && defined(EVENT__HAVE_PUTENV) +static void unsetenv(const char *k) +{ + char b[256]; + evutil_snprintf(b, sizeof(b), "%s=",k); + putenv(b); +} +#define UNSETENV_OK +#endif + +#if defined(SETENV_OK) && defined(UNSETENV_OK) +static void +methodname_to_envvar(const char *mname, char *buf, size_t buflen) +{ + char *cp; + evutil_snprintf(buf, buflen, "EVENT_NO%s", mname); + for (cp = buf; *cp; ++cp) { + *cp = EVUTIL_TOUPPER_(*cp); + } +} +#endif + +static void +test_base_environ(void *arg) +{ + struct event_base *base = NULL; + struct event_config *cfg = NULL; + +#if defined(SETENV_OK) && defined(UNSETENV_OK) + const char **basenames; + int i, n_methods=0; + char varbuf[128]; + const char *defaultname, *ignoreenvname; + + /* See if unsetenv works before we rely on it. */ + setenv("EVENT_NOWAFFLES", "1", 1); + unsetenv("EVENT_NOWAFFLES"); + if (getenv("EVENT_NOWAFFLES") != NULL) { +#ifndef EVENT__HAVE_UNSETENV + TT_DECLARE("NOTE", ("Can't fake unsetenv; skipping test")); +#else + TT_DECLARE("NOTE", ("unsetenv doesn't work; skipping test")); +#endif + tt_skip(); + } + + basenames = event_get_supported_methods(); + for (i = 0; basenames[i]; ++i) { + methodname_to_envvar(basenames[i], varbuf, sizeof(varbuf)); + unsetenv(varbuf); + ++n_methods; + } + + base = event_base_new(); + tt_assert(base); + + defaultname = event_base_get_method(base); + TT_BLATHER(("default is <%s>", defaultname)); + event_base_free(base); + base = NULL; + + /* Can we disable the method with EVENT_NOfoo ? */ + if (!strcmp(defaultname, "epoll (with changelist)")) { + setenv("EVENT_NOEPOLL", "1", 1); + ignoreenvname = "epoll"; + } else { + methodname_to_envvar(defaultname, varbuf, sizeof(varbuf)); + setenv(varbuf, "1", 1); + ignoreenvname = defaultname; + } + + /* Use an empty cfg rather than NULL so a failure doesn't exit() */ + cfg = event_config_new(); + base = event_base_new_with_config(cfg); + event_config_free(cfg); + cfg = NULL; + if (n_methods == 1) { + tt_assert(!base); + } else { + tt_assert(base); + tt_str_op(defaultname, !=, event_base_get_method(base)); + event_base_free(base); + base = NULL; + } + + /* Can we disable looking at the environment with IGNORE_ENV ? */ + cfg = event_config_new(); + event_config_set_flag(cfg, EVENT_BASE_FLAG_IGNORE_ENV); + base = event_base_new_with_config(cfg); + tt_assert(base); + tt_str_op(ignoreenvname, ==, event_base_get_method(base)); +#else + tt_skip(); +#endif + +end: + if (base) + event_base_free(base); + if (cfg) + event_config_free(cfg); +} + +static void +read_called_once_cb(evutil_socket_t fd, short event, void *arg) +{ + tt_int_op(event, ==, EV_READ); + called += 1; +end: + ; +} + +static void +timeout_called_once_cb(evutil_socket_t fd, short event, void *arg) +{ + tt_int_op(event, ==, EV_TIMEOUT); + called += 100; +end: + ; +} + +static void +immediate_called_twice_cb(evutil_socket_t fd, short event, void *arg) +{ + tt_int_op(event, ==, EV_TIMEOUT); + called += 1000; +end: + ; +} + +static void +test_event_once(void *ptr) +{ + struct basic_test_data *data = ptr; + struct timeval tv; + int r; + + tv.tv_sec = 0; + tv.tv_usec = 50*1000; + called = 0; + r = event_base_once(data->base, data->pair[0], EV_READ, + read_called_once_cb, NULL, NULL); + tt_int_op(r, ==, 0); + r = event_base_once(data->base, -1, EV_TIMEOUT, + timeout_called_once_cb, NULL, &tv); + tt_int_op(r, ==, 0); + r = event_base_once(data->base, -1, 0, NULL, NULL, NULL); + tt_int_op(r, <, 0); + r = event_base_once(data->base, -1, EV_TIMEOUT, + immediate_called_twice_cb, NULL, NULL); + tt_int_op(r, ==, 0); + tv.tv_sec = 0; + tv.tv_usec = 0; + r = event_base_once(data->base, -1, EV_TIMEOUT, + immediate_called_twice_cb, NULL, &tv); + tt_int_op(r, ==, 0); + + if (write(data->pair[1], TEST1, strlen(TEST1)+1) < 0) { + tt_fail_perror("write"); + } + + shutdown(data->pair[1], SHUT_WR); + + event_base_dispatch(data->base); + + tt_int_op(called, ==, 2101); +end: + ; +} + +static void +test_event_once_never(void *ptr) +{ + struct basic_test_data *data = ptr; + struct timeval tv; + + /* Have one trigger in 10 seconds (don't worry, because) */ + tv.tv_sec = 10; + tv.tv_usec = 0; + called = 0; + event_base_once(data->base, -1, EV_TIMEOUT, + timeout_called_once_cb, NULL, &tv); + + /* But shut down the base in 75 msec. */ + tv.tv_sec = 0; + tv.tv_usec = 75*1000; + event_base_loopexit(data->base, &tv); + + event_base_dispatch(data->base); + + tt_int_op(called, ==, 0); +end: + ; +} + +static void +test_event_pending(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event *r=NULL, *w=NULL, *t=NULL; + struct timeval tv, now, tv2; + + tv.tv_sec = 0; + tv.tv_usec = 500 * 1000; + r = event_new(data->base, data->pair[0], EV_READ, simple_read_cb, + NULL); + w = event_new(data->base, data->pair[1], EV_WRITE, simple_write_cb, + NULL); + t = evtimer_new(data->base, timeout_cb, NULL); + + tt_assert(r); + tt_assert(w); + tt_assert(t); + + evutil_gettimeofday(&now, NULL); + event_add(r, NULL); + event_add(t, &tv); + + tt_assert( event_pending(r, EV_READ, NULL)); + tt_assert(!event_pending(w, EV_WRITE, NULL)); + tt_assert(!event_pending(r, EV_WRITE, NULL)); + tt_assert( event_pending(r, EV_READ|EV_WRITE, NULL)); + tt_assert(!event_pending(r, EV_TIMEOUT, NULL)); + tt_assert( event_pending(t, EV_TIMEOUT, NULL)); + tt_assert( event_pending(t, EV_TIMEOUT, &tv2)); + + tt_assert(evutil_timercmp(&tv2, &now, >)); + + test_timeval_diff_eq(&now, &tv2, 500); + +end: + if (r) { + event_del(r); + event_free(r); + } + if (w) { + event_del(w); + event_free(w); + } + if (t) { + event_del(t); + event_free(t); + } +} + +#ifndef _WIN32 +/* You can't do this test on windows, since dup2 doesn't work on sockets */ + +static void +dfd_cb(evutil_socket_t fd, short e, void *data) +{ + *(int*)data = (int)e; +} + +/* Regression test for our workaround for a fun epoll/linux related bug + * where fd2 = dup(fd1); add(fd2); close(fd2); dup2(fd1,fd2); add(fd2) + * will get you an EEXIST */ +static void +test_dup_fd(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct event *ev1=NULL, *ev2=NULL; + int fd, dfd=-1; + int ev1_got, ev2_got; + + tt_int_op(write(data->pair[0], "Hello world", + strlen("Hello world")), >, 0); + fd = data->pair[1]; + + dfd = dup(fd); + tt_int_op(dfd, >=, 0); + + ev1 = event_new(base, fd, EV_READ|EV_PERSIST, dfd_cb, &ev1_got); + ev2 = event_new(base, dfd, EV_READ|EV_PERSIST, dfd_cb, &ev2_got); + ev1_got = ev2_got = 0; + event_add(ev1, NULL); + event_add(ev2, NULL); + event_base_loop(base, EVLOOP_ONCE); + tt_int_op(ev1_got, ==, EV_READ); + tt_int_op(ev2_got, ==, EV_READ); + + /* Now close and delete dfd then dispatch. We need to do the + * dispatch here so that when we add it later, we think there + * was an intermediate delete. */ + close(dfd); + event_del(ev2); + ev1_got = ev2_got = 0; + event_base_loop(base, EVLOOP_ONCE); + tt_want_int_op(ev1_got, ==, EV_READ); + tt_int_op(ev2_got, ==, 0); + + /* Re-duplicate the fd. We need to get the same duplicated + * value that we closed to provoke the epoll quirk. Also, we + * need to change the events to write, or else the old lingering + * read event will make the test pass whether the change was + * successful or not. */ + tt_int_op(dup2(fd, dfd), ==, dfd); + event_free(ev2); + ev2 = event_new(base, dfd, EV_WRITE|EV_PERSIST, dfd_cb, &ev2_got); + event_add(ev2, NULL); + ev1_got = ev2_got = 0; + event_base_loop(base, EVLOOP_ONCE); + tt_want_int_op(ev1_got, ==, EV_READ); + tt_int_op(ev2_got, ==, EV_WRITE); + +end: + if (ev1) + event_free(ev1); + if (ev2) + event_free(ev2); + if (dfd >= 0) + close(dfd); +} +#endif + +#ifdef EVENT__DISABLE_MM_REPLACEMENT +static void +test_mm_functions(void *arg) +{ + tinytest_set_test_skipped_(); +} +#else +static int +check_dummy_mem_ok(void *mem_) +{ + char *mem = mem_; + mem -= 16; + return !memcmp(mem, "{[<guardedram>]}", 16); +} + +static void * +dummy_malloc(size_t len) +{ + char *mem = malloc(len+16); + memcpy(mem, "{[<guardedram>]}", 16); + return mem+16; +} + +static void * +dummy_realloc(void *mem_, size_t len) +{ + char *mem = mem_; + if (!mem) + return dummy_malloc(len); + tt_want(check_dummy_mem_ok(mem_)); + mem -= 16; + mem = realloc(mem, len+16); + return mem+16; +} + +static void +dummy_free(void *mem_) +{ + char *mem = mem_; + tt_want(check_dummy_mem_ok(mem_)); + mem -= 16; + free(mem); +} + +static void +test_mm_functions(void *arg) +{ + struct event_base *b = NULL; + struct event_config *cfg = NULL; + event_set_mem_functions(dummy_malloc, dummy_realloc, dummy_free); + cfg = event_config_new(); + event_config_avoid_method(cfg, "Nonesuch"); + b = event_base_new_with_config(cfg); + tt_assert(b); + tt_assert(check_dummy_mem_ok(b)); +end: + if (cfg) + event_config_free(cfg); + if (b) + event_base_free(b); +} +#endif + +static void +many_event_cb(evutil_socket_t fd, short event, void *arg) +{ + int *calledp = arg; + *calledp += 1; +} + +static void +test_many_events(void *arg) +{ + /* Try 70 events that should all be ready at once. This will + * exercise the "resize" code on most of the backends, and will make + * sure that we can get past the 64-handle limit of some windows + * functions. */ +#define MANY 70 + + struct basic_test_data *data = arg; + struct event_base *base = data->base; + int one_at_a_time = data->setup_data != NULL; + evutil_socket_t sock[MANY]; + struct event *ev[MANY]; + int called[MANY]; + int i; + int loopflags = EVLOOP_NONBLOCK, evflags=0; + if (one_at_a_time) { + loopflags |= EVLOOP_ONCE; + evflags = EV_PERSIST; + } + + memset(sock, 0xff, sizeof(sock)); + memset(ev, 0, sizeof(ev)); + memset(called, 0, sizeof(called)); + + for (i = 0; i < MANY; ++i) { + /* We need an event that will hit the backend, and that will + * be ready immediately. "Send a datagram" is an easy + * instance of that. */ + sock[i] = socket(AF_INET, SOCK_DGRAM, 0); + tt_assert(sock[i] >= 0); + called[i] = 0; + ev[i] = event_new(base, sock[i], EV_WRITE|evflags, + many_event_cb, &called[i]); + event_add(ev[i], NULL); + if (one_at_a_time) + event_base_loop(base, EVLOOP_NONBLOCK|EVLOOP_ONCE); + } + + event_base_loop(base, loopflags); + + for (i = 0; i < MANY; ++i) { + if (one_at_a_time) + tt_int_op(called[i], ==, MANY - i + 1); + else + tt_int_op(called[i], ==, 1); + } + +end: + for (i = 0; i < MANY; ++i) { + if (ev[i]) + event_free(ev[i]); + if (sock[i] >= 0) + evutil_closesocket(sock[i]); + } +#undef MANY +} + +static void +test_struct_event_size(void *arg) +{ + tt_int_op(event_get_struct_event_size(), <=, sizeof(struct event)); +end: + ; +} + +static void +test_get_assignment(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct event *ev1 = NULL; + const char *str = "foo"; + + struct event_base *b; + evutil_socket_t s; + short what; + event_callback_fn cb; + void *cb_arg; + + ev1 = event_new(base, data->pair[1], EV_READ, dummy_read_cb, (void*)str); + event_get_assignment(ev1, &b, &s, &what, &cb, &cb_arg); + + tt_ptr_op(b, ==, base); + tt_int_op(s, ==, data->pair[1]); + tt_int_op(what, ==, EV_READ); + tt_ptr_op(cb, ==, dummy_read_cb); + tt_ptr_op(cb_arg, ==, str); + + /* Now make sure this doesn't crash. */ + event_get_assignment(ev1, NULL, NULL, NULL, NULL, NULL); + +end: + if (ev1) + event_free(ev1); +} + +struct foreach_helper { + int count; + const struct event *ev; +}; + +static int +foreach_count_cb(const struct event_base *base, const struct event *ev, void *arg) +{ + struct foreach_helper *h = event_get_callback_arg(ev); + struct timeval *tv = arg; + if (event_get_callback(ev) != timeout_cb) + return 0; + tt_ptr_op(event_get_base(ev), ==, base); + tt_int_op(tv->tv_sec, ==, 10); + h->ev = ev; + h->count++; + return 0; +end: + return -1; +} + +static int +foreach_find_cb(const struct event_base *base, const struct event *ev, void *arg) +{ + const struct event **ev_out = arg; + struct foreach_helper *h = event_get_callback_arg(ev); + if (event_get_callback(ev) != timeout_cb) + return 0; + if (h->count == 99) { + *ev_out = ev; + return 101; + } + return 0; +} + +static void +test_event_foreach(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct event *ev[5]; + struct foreach_helper visited[5]; + int i; + struct timeval ten_sec = {10,0}; + const struct event *ev_found = NULL; + + for (i = 0; i < 5; ++i) { + visited[i].count = 0; + visited[i].ev = NULL; + ev[i] = event_new(base, -1, 0, timeout_cb, &visited[i]); + } + + tt_int_op(-1, ==, event_base_foreach_event(NULL, foreach_count_cb, NULL)); + tt_int_op(-1, ==, event_base_foreach_event(base, NULL, NULL)); + + event_add(ev[0], &ten_sec); + event_add(ev[1], &ten_sec); + event_active(ev[1], EV_TIMEOUT, 1); + event_active(ev[2], EV_TIMEOUT, 1); + event_add(ev[3], &ten_sec); + /* Don't touch ev[4]. */ + + tt_int_op(0, ==, event_base_foreach_event(base, foreach_count_cb, + &ten_sec)); + tt_int_op(1, ==, visited[0].count); + tt_int_op(1, ==, visited[1].count); + tt_int_op(1, ==, visited[2].count); + tt_int_op(1, ==, visited[3].count); + tt_ptr_op(ev[0], ==, visited[0].ev); + tt_ptr_op(ev[1], ==, visited[1].ev); + tt_ptr_op(ev[2], ==, visited[2].ev); + tt_ptr_op(ev[3], ==, visited[3].ev); + + visited[2].count = 99; + tt_int_op(101, ==, event_base_foreach_event(base, foreach_find_cb, + &ev_found)); + tt_ptr_op(ev_found, ==, ev[2]); + +end: + for (i=0; i<5; ++i) { + event_free(ev[i]); + } +} + +static struct event_base *cached_time_base = NULL; +static int cached_time_reset = 0; +static int cached_time_sleep = 0; +static void +cache_time_cb(evutil_socket_t fd, short what, void *arg) +{ + struct timeval *tv = arg; + tt_int_op(0, ==, event_base_gettimeofday_cached(cached_time_base, tv)); + if (cached_time_sleep) { + struct timeval delay = { 0, 30*1000 }; + evutil_usleep_(&delay); + } + if (cached_time_reset) { + event_base_update_cache_time(cached_time_base); + } +end: + ; +} + +static void +test_gettimeofday_cached(void *arg) +{ + struct basic_test_data *data = arg; + struct event_config *cfg = NULL; + struct event_base *base = NULL; + struct timeval tv1, tv2, tv3, now; + struct event *ev1=NULL, *ev2=NULL, *ev3=NULL; + int cached_time_disable = strstr(data->setup_data, "disable") != NULL; + + cfg = event_config_new(); + if (cached_time_disable) { + event_config_set_flag(cfg, EVENT_BASE_FLAG_NO_CACHE_TIME); + } + cached_time_base = base = event_base_new_with_config(cfg); + tt_assert(base); + + /* Try gettimeofday_cached outside of an event loop. */ + evutil_gettimeofday(&now, NULL); + tt_int_op(0, ==, event_base_gettimeofday_cached(NULL, &tv1)); + tt_int_op(0, ==, event_base_gettimeofday_cached(base, &tv2)); + tt_int_op(timeval_msec_diff(&tv1, &tv2), <, 10); + tt_int_op(timeval_msec_diff(&tv1, &now), <, 10); + + cached_time_reset = strstr(data->setup_data, "reset") != NULL; + cached_time_sleep = strstr(data->setup_data, "sleep") != NULL; + + ev1 = event_new(base, -1, 0, cache_time_cb, &tv1); + ev2 = event_new(base, -1, 0, cache_time_cb, &tv2); + ev3 = event_new(base, -1, 0, cache_time_cb, &tv3); + + event_active(ev1, EV_TIMEOUT, 1); + event_active(ev2, EV_TIMEOUT, 1); + event_active(ev3, EV_TIMEOUT, 1); + + event_base_dispatch(base); + + if (cached_time_reset && cached_time_sleep) { + tt_int_op(labs(timeval_msec_diff(&tv1,&tv2)), >, 10); + tt_int_op(labs(timeval_msec_diff(&tv2,&tv3)), >, 10); + } else if (cached_time_disable && cached_time_sleep) { + tt_int_op(labs(timeval_msec_diff(&tv1,&tv2)), >, 10); + tt_int_op(labs(timeval_msec_diff(&tv2,&tv3)), >, 10); + } else if (! cached_time_disable) { + tt_assert(evutil_timercmp(&tv1, &tv2, ==)); + tt_assert(evutil_timercmp(&tv2, &tv3, ==)); + } + +end: + if (ev1) + event_free(ev1); + if (ev2) + event_free(ev2); + if (ev3) + event_free(ev3); + if (base) + event_base_free(base); + if (cfg) + event_config_free(cfg); +} + +static void +tabf_cb(evutil_socket_t fd, short what, void *arg) +{ + int *ptr = arg; + *ptr = what; + *ptr += 0x10000; +} + +static void +test_active_by_fd(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct event *ev1 = NULL, *ev2 = NULL, *ev3 = NULL, *ev4 = NULL; + int e1,e2,e3,e4; +#ifndef _WIN32 + struct event *evsig = NULL; + int es; +#endif + struct timeval tenmin = { 600, 0 }; + + /* Ensure no crash on nonexistent FD. */ + event_base_active_by_fd(base, 1000, EV_READ); + + /* Ensure no crash on bogus FD. */ + event_base_active_by_fd(base, -1, EV_READ); + + /* Ensure no crash on nonexistent/bogus signal. */ + event_base_active_by_signal(base, 1000); + event_base_active_by_signal(base, -1); + + event_base_assert_ok_(base); + + e1 = e2 = e3 = e4 = 0; + ev1 = event_new(base, data->pair[0], EV_READ, tabf_cb, &e1); + ev2 = event_new(base, data->pair[0], EV_WRITE, tabf_cb, &e2); + ev3 = event_new(base, data->pair[1], EV_READ, tabf_cb, &e3); + ev4 = event_new(base, data->pair[1], EV_READ, tabf_cb, &e4); + tt_assert(ev1); + tt_assert(ev2); + tt_assert(ev3); + tt_assert(ev4); +#ifndef _WIN32 + evsig = event_new(base, SIGHUP, EV_SIGNAL, tabf_cb, &es); + tt_assert(evsig); + event_add(evsig, &tenmin); +#endif + + event_add(ev1, &tenmin); + event_add(ev2, NULL); + event_add(ev3, NULL); + event_add(ev4, &tenmin); + + + event_base_assert_ok_(base); + + /* Trigger 2, 3, 4 */ + event_base_active_by_fd(base, data->pair[0], EV_WRITE); + event_base_active_by_fd(base, data->pair[1], EV_READ); +#ifndef _WIN32 + event_base_active_by_signal(base, SIGHUP); +#endif + + event_base_assert_ok_(base); + + event_base_loop(base, EVLOOP_ONCE); + + tt_int_op(e1, ==, 0); + tt_int_op(e2, ==, EV_WRITE | 0x10000); + tt_int_op(e3, ==, EV_READ | 0x10000); + /* Mask out EV_WRITE here, since it could be genuinely writeable. */ + tt_int_op((e4 & ~EV_WRITE), ==, EV_READ | 0x10000); +#ifndef _WIN32 + tt_int_op(es, ==, EV_SIGNAL | 0x10000); +#endif + +end: + if (ev1) + event_free(ev1); + if (ev2) + event_free(ev2); + if (ev3) + event_free(ev3); + if (ev4) + event_free(ev4); +#ifndef _WIN32 + if (evsig) + event_free(evsig); +#endif +} + +struct testcase_t main_testcases[] = { + /* Some converted-over tests */ + { "methods", test_methods, TT_FORK, NULL, NULL }, + { "version", test_version, 0, NULL, NULL }, + BASIC(base_features, TT_FORK|TT_NO_LOGS), + { "base_environ", test_base_environ, TT_FORK, NULL, NULL }, + + BASIC(event_base_new, TT_FORK|TT_NEED_SOCKETPAIR), + BASIC(free_active_base, TT_FORK|TT_NEED_SOCKETPAIR), + + BASIC(manipulate_active_events, TT_FORK|TT_NEED_BASE), + BASIC(event_new_selfarg, TT_FORK|TT_NEED_BASE), + BASIC(event_assign_selfarg, TT_FORK|TT_NEED_BASE), + BASIC(event_base_get_num_events, TT_FORK|TT_NEED_BASE), + BASIC(event_base_get_max_events, TT_FORK|TT_NEED_BASE), + + BASIC(bad_assign, TT_FORK|TT_NEED_BASE|TT_NO_LOGS), + BASIC(bad_reentrant, TT_FORK|TT_NEED_BASE|TT_NO_LOGS), + BASIC(active_later, TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR), + BASIC(event_remove_timeout, TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR), + + /* These are still using the old API */ + LEGACY(persistent_timeout, TT_FORK|TT_NEED_BASE), + { "persistent_timeout_jump", test_persistent_timeout_jump, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + { "persistent_active_timeout", test_persistent_active_timeout, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + LEGACY(priorities, TT_FORK|TT_NEED_BASE), + BASIC(priority_active_inversion, TT_FORK|TT_NEED_BASE), + { "common_timeout", test_common_timeout, TT_FORK|TT_NEED_BASE, + &basic_setup, NULL }, + + /* These legacy tests may not all need all of these flags. */ + LEGACY(simpleread, TT_ISOLATED), + LEGACY(simpleread_multiple, TT_ISOLATED), + LEGACY(simplewrite, TT_ISOLATED), + { "simpleclose", test_simpleclose, TT_FORK, &basic_setup, + NULL }, + LEGACY(multiple, TT_ISOLATED), + LEGACY(persistent, TT_ISOLATED), + LEGACY(combined, TT_ISOLATED), + LEGACY(simpletimeout, TT_ISOLATED), + LEGACY(loopbreak, TT_ISOLATED), + LEGACY(loopexit, TT_ISOLATED), + LEGACY(loopexit_multiple, TT_ISOLATED), + LEGACY(nonpersist_readd, TT_ISOLATED), + LEGACY(multiple_events_for_same_fd, TT_ISOLATED), + LEGACY(want_only_once, TT_ISOLATED), + { "event_once", test_event_once, TT_ISOLATED, &basic_setup, NULL }, + { "event_once_never", test_event_once_never, TT_ISOLATED, &basic_setup, NULL }, + { "event_pending", test_event_pending, TT_ISOLATED, &basic_setup, + NULL }, +#ifndef _WIN32 + { "dup_fd", test_dup_fd, TT_ISOLATED, &basic_setup, NULL }, +#endif + { "mm_functions", test_mm_functions, TT_FORK, NULL, NULL }, + { "many_events", test_many_events, TT_ISOLATED, &basic_setup, NULL }, + { "many_events_slow_add", test_many_events, TT_ISOLATED, &basic_setup, (void*)1 }, + + { "struct_event_size", test_struct_event_size, 0, NULL, NULL }, + BASIC(get_assignment, TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR), + + BASIC(event_foreach, TT_FORK|TT_NEED_BASE), + { "gettimeofday_cached", test_gettimeofday_cached, TT_FORK, &basic_setup, (void*)"" }, + { "gettimeofday_cached_sleep", test_gettimeofday_cached, TT_FORK, &basic_setup, (void*)"sleep" }, + { "gettimeofday_cached_reset", test_gettimeofday_cached, TT_FORK, &basic_setup, (void*)"sleep reset" }, + { "gettimeofday_cached_disabled", test_gettimeofday_cached, TT_FORK, &basic_setup, (void*)"sleep disable" }, + { "gettimeofday_cached_disabled_nosleep", test_gettimeofday_cached, TT_FORK, &basic_setup, (void*)"disable" }, + + BASIC(active_by_fd, TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR), + +#ifndef _WIN32 + LEGACY(fork, TT_ISOLATED), +#endif +#ifdef EVENT__HAVE_PTHREADS + /** TODO: support win32 */ + LEGACY(del_wait, TT_ISOLATED|TT_NEED_THREADS), +#endif + + END_OF_TESTCASES +}; + +struct testcase_t evtag_testcases[] = { + { "int", evtag_int_test, TT_FORK, NULL, NULL }, + { "fuzz", evtag_fuzz, TT_FORK, NULL, NULL }, + { "encoding", evtag_tag_encoding, TT_FORK, NULL, NULL }, + { "peek", evtag_test_peek, 0, NULL, NULL }, + + END_OF_TESTCASES +}; + +struct testcase_t signal_testcases[] = { +#ifndef _WIN32 + LEGACY(simplestsignal, TT_ISOLATED), + LEGACY(simplesignal, TT_ISOLATED), + LEGACY(multiplesignal, TT_ISOLATED), + LEGACY(immediatesignal, TT_ISOLATED), + LEGACY(signal_dealloc, TT_ISOLATED), + LEGACY(signal_pipeloss, TT_ISOLATED), + LEGACY(signal_switchbase, TT_ISOLATED|TT_NO_LOGS), + LEGACY(signal_restore, TT_ISOLATED), + LEGACY(signal_assert, TT_ISOLATED), + LEGACY(signal_while_processing, TT_ISOLATED), +#endif + END_OF_TESTCASES +}; + diff --git a/libs/libevent/docs/test/regress.h b/libs/libevent/docs/test/regress.h new file mode 100644 index 0000000000..de1aed3089 --- /dev/null +++ b/libs/libevent/docs/test/regress.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2000-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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 REGRESS_H_INCLUDED_ +#define REGRESS_H_INCLUDED_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "tinytest.h" +#include "tinytest_macros.h" + +extern struct testcase_t main_testcases[]; +extern struct testcase_t evtag_testcases[]; +extern struct testcase_t evbuffer_testcases[]; +extern struct testcase_t finalize_testcases[]; +extern struct testcase_t bufferevent_testcases[]; +extern struct testcase_t bufferevent_iocp_testcases[]; +extern struct testcase_t util_testcases[]; +extern struct testcase_t signal_testcases[]; +extern struct testcase_t http_testcases[]; +extern struct testcase_t dns_testcases[]; +extern struct testcase_t rpc_testcases[]; +extern struct testcase_t edgetriggered_testcases[]; +extern struct testcase_t minheap_testcases[]; +extern struct testcase_t iocp_testcases[]; +extern struct testcase_t ssl_testcases[]; +extern struct testcase_t listener_testcases[]; +extern struct testcase_t listener_iocp_testcases[]; +extern struct testcase_t thread_testcases[]; + +extern struct evutil_weakrand_state test_weakrand_state; + +#define test_weakrand() (evutil_weakrand_(&test_weakrand_state)) + +void regress_threads(void *); +void test_bufferevent_zlib(void *); + +/* Helpers to wrap old testcases */ +extern evutil_socket_t pair[2]; +extern int test_ok; +extern int called; +extern struct event_base *global_base; +extern int in_legacy_test_wrapper; + +int regress_make_tmpfile(const void *data, size_t datalen, char **filename_out); + +struct basic_test_data { + struct event_base *base; + evutil_socket_t pair[2]; + + void (*legacy_test_fn)(void); + + void *setup_data; +}; +extern const struct testcase_setup_t basic_setup; + + +extern const struct testcase_setup_t legacy_setup; +void run_legacy_test_fn(void *ptr); + +extern int libevent_tests_running_in_debug_mode; + +/* A couple of flags that basic/legacy_setup can support. */ +#define TT_NEED_SOCKETPAIR TT_FIRST_USER_FLAG +#define TT_NEED_BASE (TT_FIRST_USER_FLAG<<1) +#define TT_NEED_DNS (TT_FIRST_USER_FLAG<<2) +#define TT_LEGACY (TT_FIRST_USER_FLAG<<3) +#define TT_NEED_THREADS (TT_FIRST_USER_FLAG<<4) +#define TT_NO_LOGS (TT_FIRST_USER_FLAG<<5) +#define TT_ENABLE_IOCP_FLAG (TT_FIRST_USER_FLAG<<6) +#define TT_ENABLE_IOCP (TT_ENABLE_IOCP_FLAG|TT_NEED_THREADS) + +/* All the flags that a legacy test needs. */ +#define TT_ISOLATED TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_BASE + + +#define BASIC(name,flags) \ + { #name, test_## name, flags, &basic_setup, NULL } + +#define LEGACY(name,flags) \ + { #name, run_legacy_test_fn, flags|TT_LEGACY, &legacy_setup, \ + test_## name } + +struct evutil_addrinfo; +struct evutil_addrinfo *ai_find_by_family(struct evutil_addrinfo *ai, int f); +struct evutil_addrinfo *ai_find_by_protocol(struct evutil_addrinfo *ai, int p); +int test_ai_eq_(const struct evutil_addrinfo *ai, const char *sockaddr_port, + int socktype, int protocol, int line); + +#define test_ai_eq(ai, str, s, p) do { \ + if (test_ai_eq_((ai), (str), (s), (p), __LINE__)<0) \ + goto end; \ + } while (0) + +#define test_timeval_diff_leq(tv1, tv2, diff, tolerance) \ + tt_int_op(labs(timeval_msec_diff((tv1), (tv2)) - diff), <=, tolerance) + +#define test_timeval_diff_eq(tv1, tv2, diff) \ + test_timeval_diff_leq((tv1), (tv2), (diff), 50) + +long timeval_msec_diff(const struct timeval *start, const struct timeval *end); + +#ifndef _WIN32 +pid_t regress_fork(void); +#endif + +#ifdef EVENT__HAVE_OPENSSL +#include <openssl/ssl.h> +EVP_PKEY *ssl_getkey(void); +X509 *ssl_getcert(void); +SSL_CTX *get_ssl_ctx(void); +void init_ssl(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* REGRESS_H_INCLUDED_ */ diff --git a/libs/libevent/docs/test/regress.rpc b/libs/libevent/docs/test/regress.rpc new file mode 100644 index 0000000000..0ee904e913 --- /dev/null +++ b/libs/libevent/docs/test/regress.rpc @@ -0,0 +1,25 @@ +/* tests data packing and unpacking */ + +struct msg { + string /* sender */ from_name = 1; /* be verbose */ + string to_name = 2; + optional struct[kill] attack = 3; + array struct[run] run = 4; +} + +struct kill { + string weapon = 0x10121; + string action = 2; + array int how_often = 3; +} + +struct run { + string how = 1; + optional bytes some_bytes = 2; + + bytes fixed_bytes[24] = 3; + array string notes = 4; + + optional int64 large_number = 5; + array int other_numbers = 6; +} diff --git a/libs/libevent/docs/test/regress_buffer.c b/libs/libevent/docs/test/regress_buffer.c new file mode 100644 index 0000000000..957e59f178 --- /dev/null +++ b/libs/libevent/docs/test/regress_buffer.c @@ -0,0 +1,2281 @@ +/* + * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "util-internal.h" + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#endif + +#include "event2/event-config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/queue.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <sys/wait.h> +#include <signal.h> +#include <unistd.h> +#include <netdb.h> +#endif +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "event2/event.h" +#include "event2/buffer.h" +#include "event2/buffer_compat.h" +#include "event2/util.h" + +#include "defer-internal.h" +#include "evbuffer-internal.h" +#include "log-internal.h" + +#include "regress.h" + +/* Validates that an evbuffer is good. Returns false if it isn't, true if it + * is*/ +static int +evbuffer_validate_(struct evbuffer *buf) +{ + struct evbuffer_chain *chain; + size_t sum = 0; + int found_last_with_datap = 0; + + if (buf->first == NULL) { + tt_assert(buf->last == NULL); + tt_assert(buf->total_len == 0); + } + + chain = buf->first; + + tt_assert(buf->last_with_datap); + if (buf->last_with_datap == &buf->first) + found_last_with_datap = 1; + + while (chain != NULL) { + if (&chain->next == buf->last_with_datap) + found_last_with_datap = 1; + sum += chain->off; + if (chain->next == NULL) { + tt_assert(buf->last == chain); + } + tt_assert(chain->buffer_len >= chain->misalign + chain->off); + chain = chain->next; + } + + if (buf->first) + tt_assert(*buf->last_with_datap); + + if (*buf->last_with_datap) { + chain = *buf->last_with_datap; + if (chain->off == 0 || buf->total_len == 0) { + tt_assert(chain->off == 0) + tt_assert(chain == buf->first); + tt_assert(buf->total_len == 0); + } + chain = chain->next; + while (chain != NULL) { + tt_assert(chain->off == 0); + chain = chain->next; + } + } else { + tt_assert(buf->last_with_datap == &buf->first); + } + tt_assert(found_last_with_datap); + + tt_assert(sum == buf->total_len); + return 1; + end: + return 0; +} + +static void +evbuffer_get_waste(struct evbuffer *buf, size_t *allocatedp, size_t *wastedp, size_t *usedp) +{ + struct evbuffer_chain *chain; + size_t a, w, u; + int n = 0; + u = a = w = 0; + + chain = buf->first; + /* skip empty at start */ + while (chain && chain->off==0) { + ++n; + a += chain->buffer_len; + chain = chain->next; + } + /* first nonempty chain: stuff at the end only is wasted. */ + if (chain) { + ++n; + a += chain->buffer_len; + u += chain->off; + if (chain->next && chain->next->off) + w += (size_t)(chain->buffer_len - (chain->misalign + chain->off)); + chain = chain->next; + } + /* subsequent nonempty chains */ + while (chain && chain->off) { + ++n; + a += chain->buffer_len; + w += (size_t)chain->misalign; + u += chain->off; + if (chain->next && chain->next->off) + w += (size_t) (chain->buffer_len - (chain->misalign + chain->off)); + chain = chain->next; + } + /* subsequent empty chains */ + while (chain) { + ++n; + a += chain->buffer_len; + } + *allocatedp = a; + *wastedp = w; + *usedp = u; +} + +#define evbuffer_validate(buf) \ + TT_STMT_BEGIN if (!evbuffer_validate_(buf)) TT_DIE(("Buffer format invalid")); TT_STMT_END + +static void +test_evbuffer(void *ptr) +{ + static char buffer[512], *tmp; + struct evbuffer *evb = evbuffer_new(); + struct evbuffer *evb_two = evbuffer_new(); + size_t sz_tmp; + int i; + + evbuffer_validate(evb); + evbuffer_add_printf(evb, "%s/%d", "hello", 1); + evbuffer_validate(evb); + + tt_assert(evbuffer_get_length(evb) == 7); + tt_assert(!memcmp((char*)EVBUFFER_DATA(evb), "hello/1", 1)); + + evbuffer_add_buffer(evb, evb_two); + evbuffer_validate(evb); + + evbuffer_drain(evb, strlen("hello/")); + evbuffer_validate(evb); + tt_assert(evbuffer_get_length(evb) == 1); + tt_assert(!memcmp((char*)EVBUFFER_DATA(evb), "1", 1)); + + evbuffer_add_printf(evb_two, "%s", "/hello"); + evbuffer_validate(evb); + evbuffer_add_buffer(evb, evb_two); + evbuffer_validate(evb); + + tt_assert(evbuffer_get_length(evb_two) == 0); + tt_assert(evbuffer_get_length(evb) == 7); + tt_assert(!memcmp((char*)EVBUFFER_DATA(evb), "1/hello", 7)); + + memset(buffer, 0, sizeof(buffer)); + evbuffer_add(evb, buffer, sizeof(buffer)); + evbuffer_validate(evb); + tt_assert(evbuffer_get_length(evb) == 7 + 512); + + tmp = (char *)evbuffer_pullup(evb, 7 + 512); + tt_assert(tmp); + tt_assert(!strncmp(tmp, "1/hello", 7)); + tt_assert(!memcmp(tmp + 7, buffer, sizeof(buffer))); + evbuffer_validate(evb); + + evbuffer_prepend(evb, "something", 9); + evbuffer_validate(evb); + evbuffer_prepend(evb, "else", 4); + evbuffer_validate(evb); + + tmp = (char *)evbuffer_pullup(evb, 4 + 9 + 7); + tt_assert(!strncmp(tmp, "elsesomething1/hello", 4 + 9 + 7)); + evbuffer_validate(evb); + + evbuffer_drain(evb, -1); + evbuffer_validate(evb); + evbuffer_drain(evb_two, -1); + evbuffer_validate(evb); + + for (i = 0; i < 3; ++i) { + evbuffer_add(evb_two, buffer, sizeof(buffer)); + evbuffer_validate(evb_two); + evbuffer_add_buffer(evb, evb_two); + evbuffer_validate(evb); + evbuffer_validate(evb_two); + } + + tt_assert(evbuffer_get_length(evb_two) == 0); + tt_assert(evbuffer_get_length(evb) == i * sizeof(buffer)); + + /* test remove buffer */ + sz_tmp = (size_t)(sizeof(buffer)*2.5); + evbuffer_remove_buffer(evb, evb_two, sz_tmp); + tt_assert(evbuffer_get_length(evb_two) == sz_tmp); + tt_assert(evbuffer_get_length(evb) == sizeof(buffer) / 2); + evbuffer_validate(evb); + + if (memcmp(evbuffer_pullup( + evb, -1), buffer, sizeof(buffer) / 2) != 0 || + memcmp(evbuffer_pullup( + evb_two, -1), buffer, sizeof(buffer)) != 0) + tt_abort_msg("Pullup did not preserve content"); + + evbuffer_validate(evb); + + + /* testing one-vector reserve and commit */ + { + struct evbuffer_iovec v[1]; + char *buf; + int i, j, r; + + for (i = 0; i < 3; ++i) { + r = evbuffer_reserve_space(evb, 10000, v, 1); + tt_int_op(r, ==, 1); + tt_assert(v[0].iov_len >= 10000); + tt_assert(v[0].iov_base != NULL); + + evbuffer_validate(evb); + buf = v[0].iov_base; + for (j = 0; j < 10000; ++j) { + buf[j] = j; + } + evbuffer_validate(evb); + + tt_int_op(evbuffer_commit_space(evb, v, 1), ==, 0); + evbuffer_validate(evb); + + tt_assert(evbuffer_get_length(evb) >= 10000); + + evbuffer_drain(evb, j * 5000); + evbuffer_validate(evb); + } + } + + end: + evbuffer_free(evb); + evbuffer_free(evb_two); +} + +static void +no_cleanup(const void *data, size_t datalen, void *extra) +{ +} + +static void +test_evbuffer_remove_buffer_with_empty(void *ptr) +{ + struct evbuffer *src = evbuffer_new(); + struct evbuffer *dst = evbuffer_new(); + char buf[2]; + + evbuffer_validate(src); + evbuffer_validate(dst); + + /* setup the buffers */ + /* we need more data in src than we will move later */ + evbuffer_add_reference(src, buf, sizeof(buf), no_cleanup, NULL); + evbuffer_add_reference(src, buf, sizeof(buf), no_cleanup, NULL); + /* we need one buffer in dst and one empty buffer at the end */ + evbuffer_add(dst, buf, sizeof(buf)); + evbuffer_add_reference(dst, buf, 0, no_cleanup, NULL); + + evbuffer_validate(src); + evbuffer_validate(dst); + + /* move three bytes over */ + evbuffer_remove_buffer(src, dst, 3); + + evbuffer_validate(src); + evbuffer_validate(dst); + +end: + evbuffer_free(src); + evbuffer_free(dst); +} + +static void +test_evbuffer_reserve2(void *ptr) +{ + /* Test the two-vector cases of reserve/commit. */ + struct evbuffer *buf = evbuffer_new(); + int n, i; + struct evbuffer_iovec v[2]; + size_t remaining; + char *cp, *cp2; + + /* First chunk will necessarily be one chunk. Use 512 bytes of it.*/ + n = evbuffer_reserve_space(buf, 1024, v, 2); + tt_int_op(n, ==, 1); + tt_int_op(evbuffer_get_length(buf), ==, 0); + tt_assert(v[0].iov_base != NULL); + tt_int_op(v[0].iov_len, >=, 1024); + memset(v[0].iov_base, 'X', 512); + cp = v[0].iov_base; + remaining = v[0].iov_len - 512; + v[0].iov_len = 512; + evbuffer_validate(buf); + tt_int_op(0, ==, evbuffer_commit_space(buf, v, 1)); + tt_int_op(evbuffer_get_length(buf), ==, 512); + evbuffer_validate(buf); + + /* Ask for another same-chunk request, in an existing chunk. Use 8 + * bytes of it. */ + n = evbuffer_reserve_space(buf, 32, v, 2); + tt_int_op(n, ==, 1); + tt_assert(cp + 512 == v[0].iov_base); + tt_int_op(remaining, ==, v[0].iov_len); + memset(v[0].iov_base, 'Y', 8); + v[0].iov_len = 8; + tt_int_op(0, ==, evbuffer_commit_space(buf, v, 1)); + tt_int_op(evbuffer_get_length(buf), ==, 520); + remaining -= 8; + evbuffer_validate(buf); + + /* Now ask for a request that will be split. Use only one byte of it, + though. */ + n = evbuffer_reserve_space(buf, remaining+64, v, 2); + tt_int_op(n, ==, 2); + tt_assert(cp + 520 == v[0].iov_base); + tt_int_op(remaining, ==, v[0].iov_len); + tt_assert(v[1].iov_base); + tt_assert(v[1].iov_len >= 64); + cp2 = v[1].iov_base; + memset(v[0].iov_base, 'Z', 1); + v[0].iov_len = 1; + tt_int_op(0, ==, evbuffer_commit_space(buf, v, 1)); + tt_int_op(evbuffer_get_length(buf), ==, 521); + remaining -= 1; + evbuffer_validate(buf); + + /* Now ask for a request that will be split. Use some of the first + * part and some of the second. */ + n = evbuffer_reserve_space(buf, remaining+64, v, 2); + evbuffer_validate(buf); + tt_int_op(n, ==, 2); + tt_assert(cp + 521 == v[0].iov_base); + tt_int_op(remaining, ==, v[0].iov_len); + tt_assert(v[1].iov_base == cp2); + tt_assert(v[1].iov_len >= 64); + memset(v[0].iov_base, 'W', 400); + v[0].iov_len = 400; + memset(v[1].iov_base, 'x', 60); + v[1].iov_len = 60; + tt_int_op(0, ==, evbuffer_commit_space(buf, v, 2)); + tt_int_op(evbuffer_get_length(buf), ==, 981); + evbuffer_validate(buf); + + /* Now peek to make sure stuff got made how we like. */ + memset(v,0,sizeof(v)); + n = evbuffer_peek(buf, -1, NULL, v, 2); + tt_int_op(n, ==, 2); + tt_int_op(v[0].iov_len, ==, 921); + tt_int_op(v[1].iov_len, ==, 60); + + cp = v[0].iov_base; + for (i=0; i<512; ++i) + tt_int_op(cp[i], ==, 'X'); + for (i=512; i<520; ++i) + tt_int_op(cp[i], ==, 'Y'); + for (i=520; i<521; ++i) + tt_int_op(cp[i], ==, 'Z'); + for (i=521; i<921; ++i) + tt_int_op(cp[i], ==, 'W'); + + cp = v[1].iov_base; + for (i=0; i<60; ++i) + tt_int_op(cp[i], ==, 'x'); + +end: + evbuffer_free(buf); +} + +static void +test_evbuffer_reserve_many(void *ptr) +{ + /* This is a glass-box test to handle expanding a buffer with more + * chunks and reallocating chunks as needed */ + struct evbuffer *buf = evbuffer_new(); + struct evbuffer_iovec v[8]; + int n; + size_t sz; + int add_data = ptr && !strcmp(ptr, "add"); + int fill_first = ptr && !strcmp(ptr, "fill"); + char *cp1, *cp2; + + /* When reserving the the first chunk, we just allocate it */ + n = evbuffer_reserve_space(buf, 128, v, 2); + evbuffer_validate(buf); + tt_int_op(n, ==, 1); + tt_assert(v[0].iov_len >= 128); + sz = v[0].iov_len; + cp1 = v[0].iov_base; + if (add_data) { + *(char*)v[0].iov_base = 'X'; + v[0].iov_len = 1; + n = evbuffer_commit_space(buf, v, 1); + tt_int_op(n, ==, 0); + } else if (fill_first) { + memset(v[0].iov_base, 'X', v[0].iov_len); + n = evbuffer_commit_space(buf, v, 1); + tt_int_op(n, ==, 0); + n = evbuffer_reserve_space(buf, 128, v, 2); + tt_int_op(n, ==, 1); + sz = v[0].iov_len; + tt_assert(v[0].iov_base != cp1); + cp1 = v[0].iov_base; + } + + /* Make another chunk get added. */ + n = evbuffer_reserve_space(buf, sz+128, v, 2); + evbuffer_validate(buf); + tt_int_op(n, ==, 2); + sz = v[0].iov_len + v[1].iov_len; + tt_int_op(sz, >=, v[0].iov_len+128); + if (add_data) { + tt_assert(v[0].iov_base == cp1 + 1); + } else { + tt_assert(v[0].iov_base == cp1); + } + cp1 = v[0].iov_base; + cp2 = v[1].iov_base; + + /* And a third chunk. */ + n = evbuffer_reserve_space(buf, sz+128, v, 3); + evbuffer_validate(buf); + tt_int_op(n, ==, 3); + tt_assert(cp1 == v[0].iov_base); + tt_assert(cp2 == v[1].iov_base); + sz = v[0].iov_len + v[1].iov_len + v[2].iov_len; + + /* Now force a reallocation by asking for more space in only 2 + * buffers. */ + n = evbuffer_reserve_space(buf, sz+128, v, 2); + evbuffer_validate(buf); + if (add_data) { + tt_int_op(n, ==, 2); + tt_assert(cp1 == v[0].iov_base); + } else { + tt_int_op(n, ==, 1); + } + +end: + evbuffer_free(buf); +} + +static void +test_evbuffer_expand(void *ptr) +{ + char data[4096]; + struct evbuffer *buf; + size_t a,w,u; + void *buffer; + + memset(data, 'X', sizeof(data)); + + /* Make sure that expand() works on an empty buffer */ + buf = evbuffer_new(); + tt_int_op(evbuffer_expand(buf, 20000), ==, 0); + evbuffer_validate(buf); + a=w=u=0; + evbuffer_get_waste(buf, &a,&w,&u); + tt_assert(w == 0); + tt_assert(u == 0); + tt_assert(a >= 20000); + tt_assert(buf->first); + tt_assert(buf->first == buf->last); + tt_assert(buf->first->off == 0); + tt_assert(buf->first->buffer_len >= 20000); + + /* Make sure that expand() works as a no-op when there's enough + * contiguous space already. */ + buffer = buf->first->buffer; + evbuffer_add(buf, data, 1024); + tt_int_op(evbuffer_expand(buf, 1024), ==, 0); + tt_assert(buf->first->buffer == buffer); + evbuffer_validate(buf); + evbuffer_free(buf); + + /* Make sure that expand() can work by moving misaligned data + * when it makes sense to do so. */ + buf = evbuffer_new(); + evbuffer_add(buf, data, 400); + { + int n = (int)(buf->first->buffer_len - buf->first->off - 1); + tt_assert(n < (int)sizeof(data)); + evbuffer_add(buf, data, n); + } + tt_assert(buf->first == buf->last); + tt_assert(buf->first->off == buf->first->buffer_len - 1); + evbuffer_drain(buf, buf->first->off - 1); + tt_assert(1 == evbuffer_get_length(buf)); + tt_assert(buf->first->misalign > 0); + tt_assert(buf->first->off == 1); + buffer = buf->first->buffer; + tt_assert(evbuffer_expand(buf, 40) == 0); + tt_assert(buf->first == buf->last); + tt_assert(buf->first->off == 1); + tt_assert(buf->first->buffer == buffer); + tt_assert(buf->first->misalign == 0); + evbuffer_validate(buf); + evbuffer_free(buf); + + /* add, expand, pull-up: This used to crash libevent. */ + buf = evbuffer_new(); + + evbuffer_add(buf, data, sizeof(data)); + evbuffer_add(buf, data, sizeof(data)); + evbuffer_add(buf, data, sizeof(data)); + + evbuffer_validate(buf); + evbuffer_expand(buf, 1024); + evbuffer_validate(buf); + evbuffer_pullup(buf, -1); + evbuffer_validate(buf); + +end: + evbuffer_free(buf); +} + + +static int reference_cb_called; +static void +reference_cb(const void *data, size_t len, void *extra) +{ + tt_str_op(data, ==, "this is what we add as read-only memory."); + tt_int_op(len, ==, strlen(data)); + tt_want(extra == (void *)0xdeadaffe); + ++reference_cb_called; +end: + ; +} + +static void +test_evbuffer_reference(void *ptr) +{ + struct evbuffer *src = evbuffer_new(); + struct evbuffer *dst = evbuffer_new(); + struct evbuffer_iovec v[1]; + const char *data = "this is what we add as read-only memory."; + reference_cb_called = 0; + + tt_assert(evbuffer_add_reference(src, data, strlen(data), + reference_cb, (void *)0xdeadaffe) != -1); + + evbuffer_reserve_space(dst, strlen(data), v, 1); + tt_assert(evbuffer_remove(src, v[0].iov_base, 10) != -1); + + evbuffer_validate(src); + evbuffer_validate(dst); + + /* make sure that we don't write data at the beginning */ + evbuffer_prepend(src, "aaaaa", 5); + evbuffer_validate(src); + evbuffer_drain(src, 5); + + tt_assert(evbuffer_remove(src, ((char*)(v[0].iov_base)) + 10, + strlen(data) - 10) != -1); + + v[0].iov_len = strlen(data); + + evbuffer_commit_space(dst, v, 1); + evbuffer_validate(src); + evbuffer_validate(dst); + + tt_int_op(reference_cb_called, ==, 1); + + tt_assert(!memcmp(evbuffer_pullup(dst, strlen(data)), + data, strlen(data))); + evbuffer_validate(dst); + + end: + evbuffer_free(dst); + evbuffer_free(src); +} + +static struct event_base *addfile_test_event_base = NULL; +static int addfile_test_done_writing = 0; +static int addfile_test_total_written = 0; +static int addfile_test_total_read = 0; + +static void +addfile_test_writecb(evutil_socket_t fd, short what, void *arg) +{ + struct evbuffer *b = arg; + int r; + evbuffer_validate(b); + while (evbuffer_get_length(b)) { + r = evbuffer_write(b, fd); + if (r > 0) { + addfile_test_total_written += r; + TT_BLATHER(("Wrote %d/%d bytes", r, addfile_test_total_written)); + } else { + int e = evutil_socket_geterror(fd); + if (EVUTIL_ERR_RW_RETRIABLE(e)) + return; + tt_fail_perror("write"); + event_base_loopexit(addfile_test_event_base,NULL); + } + evbuffer_validate(b); + } + addfile_test_done_writing = 1; + return; +end: + event_base_loopexit(addfile_test_event_base,NULL); +} + +static void +addfile_test_readcb(evutil_socket_t fd, short what, void *arg) +{ + struct evbuffer *b = arg; + int e, r = 0; + do { + r = evbuffer_read(b, fd, 1024); + if (r > 0) { + addfile_test_total_read += r; + TT_BLATHER(("Read %d/%d bytes", r, addfile_test_total_read)); + } + } while (r > 0); + if (r < 0) { + e = evutil_socket_geterror(fd); + if (! EVUTIL_ERR_RW_RETRIABLE(e)) { + tt_fail_perror("read"); + event_base_loopexit(addfile_test_event_base,NULL); + } + } + if (addfile_test_done_writing && + addfile_test_total_read >= addfile_test_total_written) { + event_base_loopexit(addfile_test_event_base,NULL); + } +} + +static void +test_evbuffer_add_file(void *ptr) +{ + struct basic_test_data *testdata = ptr; + const char *impl = testdata->setup_data; + struct evbuffer *src = evbuffer_new(), *dest = evbuffer_new(); + char *tmpfilename = NULL; + char *data = NULL; + const char *expect_data; + size_t datalen, expect_len; + const char *compare; + int fd = -1; + int want_ismapping = -1, want_cansendfile = -1; + unsigned flags = 0; + int use_segment = 1, use_bigfile = 0, map_from_offset = 0, + view_from_offset = 0; + struct evbuffer_file_segment *seg = NULL; + ev_off_t starting_offset = 0, mapping_len = -1; + ev_off_t segment_offset = 0, segment_len = -1; + struct event *rev=NULL, *wev=NULL; + struct event_base *base = testdata->base; + evutil_socket_t pair[2] = {-1, -1}; + struct evutil_weakrand_state seed = { 123456789U }; + + /* This test is highly parameterized based on substrings of its + * argument. The strings are: */ + tt_assert(impl); + if (strstr(impl, "nosegment")) { + /* If nosegment is set, use the older evbuffer_add_file + * interface */ + use_segment = 0; + } + if (strstr(impl, "bigfile")) { + /* If bigfile is set, use a 512K file. Else use a smaller + * one. */ + use_bigfile = 1; + } + if (strstr(impl, "map_offset")) { + /* If map_offset is set, we build the file segment starting + * from a point other than byte 0 and ending somewhere other + * than the last byte. Otherwise we map the whole thing */ + map_from_offset = 1; + } + if (strstr(impl, "offset_in_segment")) { + /* If offset_in_segment is set, we add a subsection of the + * file semgment starting from a point other than byte 0 of + * the segment. */ + view_from_offset = 1; + } + if (strstr(impl, "sendfile")) { + /* If sendfile is set, we try to use a sendfile/splice style + * backend. */ + flags = EVBUF_FS_DISABLE_MMAP; + want_cansendfile = 1; + want_ismapping = 0; + } else if (strstr(impl, "mmap")) { + /* If sendfile is set, we try to use a mmap/CreateFileMapping + * style backend. */ + flags = EVBUF_FS_DISABLE_SENDFILE; + want_ismapping = 1; + want_cansendfile = 0; + } else if (strstr(impl, "linear")) { + /* If linear is set, we try to use a read-the-whole-thing + * backend. */ + flags = EVBUF_FS_DISABLE_SENDFILE|EVBUF_FS_DISABLE_MMAP; + want_ismapping = 0; + want_cansendfile = 0; + } else if (strstr(impl, "default")) { + /* The caller doesn't care which backend we use. */ + ; + } else { + /* The caller must choose a backend. */ + TT_DIE(("Didn't recognize the implementation")); + } + + if (use_bigfile) { + unsigned int i; + datalen = 1024*512; + data = malloc(1024*512); + tt_assert(data); + for (i = 0; i < datalen; ++i) + data[i] = (char)evutil_weakrand_(&seed); + } else { + data = strdup("here is a relatively small string."); + tt_assert(data); + datalen = strlen(data); + } + + fd = regress_make_tmpfile(data, datalen, &tmpfilename); + + if (map_from_offset) { + starting_offset = datalen/4 + 1; + mapping_len = datalen / 2 - 1; + expect_data = data + starting_offset; + expect_len = mapping_len; + } else { + expect_data = data; + expect_len = datalen; + } + if (view_from_offset) { + tt_assert(use_segment); /* Can't do this with add_file*/ + segment_offset = expect_len / 3; + segment_len = expect_len / 2; + expect_data = expect_data + segment_offset; + expect_len = segment_len; + } + + if (use_segment) { + seg = evbuffer_file_segment_new(fd, starting_offset, + mapping_len, flags); + tt_assert(seg); + if (want_ismapping >= 0) { + if (seg->is_mapping != (unsigned)want_ismapping) + tt_skip(); + } + if (want_cansendfile >= 0) { + if (seg->can_sendfile != (unsigned)want_cansendfile) + tt_skip(); + } + } + + /* Say that it drains to a fd so that we can use sendfile. */ + evbuffer_set_flags(src, EVBUFFER_FLAG_DRAINS_TO_FD); + +#if defined(EVENT__HAVE_SENDFILE) && defined(__sun__) && defined(__svr4__) + /* We need to use a pair of AF_INET sockets, since Solaris + doesn't support sendfile() over AF_UNIX. */ + if (evutil_ersatz_socketpair_(AF_INET, SOCK_STREAM, 0, pair) == -1) + tt_abort_msg("ersatz_socketpair failed"); +#else + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) + tt_abort_msg("socketpair failed"); +#endif + evutil_make_socket_nonblocking(pair[0]); + evutil_make_socket_nonblocking(pair[1]); + + tt_assert(fd != -1); + + if (use_segment) { + tt_assert(evbuffer_add_file_segment(src, seg, + segment_offset, segment_len)!=-1); + } else { + tt_assert(evbuffer_add_file(src, fd, starting_offset, + mapping_len) != -1); + } + + evbuffer_validate(src); + + addfile_test_event_base = base; + wev = event_new(base, pair[0], EV_WRITE|EV_PERSIST, + addfile_test_writecb, src); + rev = event_new(base, pair[1], EV_READ|EV_PERSIST, + addfile_test_readcb, dest); + + event_add(wev, NULL); + event_add(rev, NULL); + event_base_dispatch(base); + + evbuffer_validate(src); + evbuffer_validate(dest); + + tt_assert(addfile_test_done_writing); + tt_int_op(addfile_test_total_written, ==, expect_len); + tt_int_op(addfile_test_total_read, ==, expect_len); + + compare = (char *)evbuffer_pullup(dest, expect_len); + tt_assert(compare != NULL); + if (memcmp(compare, expect_data, expect_len)) { + tt_abort_msg("Data from add_file differs."); + } + + evbuffer_validate(dest); + end: + if (data) + free(data); + if (seg) + evbuffer_file_segment_free(seg); + if (src) + evbuffer_free(src); + if (dest) + evbuffer_free(dest); + if (pair[0] >= 0) + evutil_closesocket(pair[0]); + if (pair[1] >= 0) + evutil_closesocket(pair[1]); + if (wev) + event_free(wev); + if (rev) + event_free(rev); + if (tmpfilename) { + unlink(tmpfilename); + free(tmpfilename); + } +} + +static int file_segment_cleanup_cb_called_count = 0; +static struct evbuffer_file_segment const* file_segment_cleanup_cb_called_with = NULL; +static int file_segment_cleanup_cb_called_with_flags = 0; +static void* file_segment_cleanup_cb_called_with_arg = NULL; +static void +file_segment_cleanup_cp(struct evbuffer_file_segment const* seg, int flags, void* arg) +{ + ++file_segment_cleanup_cb_called_count; + file_segment_cleanup_cb_called_with = seg; + file_segment_cleanup_cb_called_with_flags = flags; + file_segment_cleanup_cb_called_with_arg = arg; +} + +static void +test_evbuffer_file_segment_add_cleanup_cb(void* ptr) +{ + char *tmpfilename = NULL; + int fd = -1; + struct evbuffer *evb = NULL; + struct evbuffer_file_segment *seg = NULL, *segptr; + char const* arg = "token"; + + fd = regress_make_tmpfile("file_segment_test_file", 22, &tmpfilename); + tt_int_op(fd, >=, 0); + + evb = evbuffer_new(); + tt_assert(evb); + + segptr = seg = evbuffer_file_segment_new(fd, 0, -1, 0); + tt_assert(seg); + + evbuffer_file_segment_add_cleanup_cb( + seg, &file_segment_cleanup_cp, (void*)arg); + + tt_assert(fd != -1); + + tt_assert(evbuffer_add_file_segment(evb, seg, 0, -1)!=-1); + + evbuffer_validate(evb); + + tt_int_op(file_segment_cleanup_cb_called_count, ==, 0); + evbuffer_file_segment_free(seg); + seg = NULL; /* Prevent double-free. */ + + tt_int_op(file_segment_cleanup_cb_called_count, ==, 0); + evbuffer_free(evb); + evb = NULL; /* pevent double-free */ + + tt_int_op(file_segment_cleanup_cb_called_count, ==, 1); + tt_assert(file_segment_cleanup_cb_called_with == segptr); + tt_assert(file_segment_cleanup_cb_called_with_flags == 0); + tt_assert(file_segment_cleanup_cb_called_with_arg == (void*)arg); + +end: + if (evb) + evbuffer_free(evb); + if (seg) + evbuffer_file_segment_free(seg); + if (tmpfilename) { + unlink(tmpfilename); + free(tmpfilename); + } +} + +#ifndef EVENT__DISABLE_MM_REPLACEMENT +static void * +failing_malloc(size_t how_much) +{ + errno = ENOMEM; + return NULL; +} +#endif + +static void +test_evbuffer_readln(void *ptr) +{ + struct evbuffer *evb = evbuffer_new(); + struct evbuffer *evb_tmp = evbuffer_new(); + const char *s; + char *cp = NULL; + size_t sz; + +#define tt_line_eq(content) \ + TT_STMT_BEGIN \ + if (!cp || sz != strlen(content) || strcmp(cp, content)) { \ + TT_DIE(("Wanted %s; got %s [%d]", content, cp, (int)sz)); \ + } \ + TT_STMT_END + + /* Test EOL_ANY. */ + s = "complex silly newline\r\n\n\r\n\n\rmore\0\n"; + evbuffer_add(evb, s, strlen(s)+2); + evbuffer_validate(evb); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_ANY); + tt_line_eq("complex silly newline"); + free(cp); + evbuffer_validate(evb); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_ANY); + if (!cp || sz != 5 || memcmp(cp, "more\0\0", 6)) + tt_abort_msg("Not as expected"); + tt_uint_op(evbuffer_get_length(evb), ==, 0); + evbuffer_validate(evb); + s = "\nno newline"; + evbuffer_add(evb, s, strlen(s)); + free(cp); + evbuffer_validate(evb); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_ANY); + tt_line_eq(""); + free(cp); + evbuffer_validate(evb); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_ANY); + tt_assert(!cp); + evbuffer_validate(evb); + evbuffer_drain(evb, evbuffer_get_length(evb)); + tt_assert(evbuffer_get_length(evb) == 0); + evbuffer_validate(evb); + + /* Test EOL_CRLF */ + s = "Line with\rin the middle\nLine with good crlf\r\n\nfinal\n"; + evbuffer_add(evb, s, strlen(s)); + evbuffer_validate(evb); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF); + tt_line_eq("Line with\rin the middle"); + free(cp); + evbuffer_validate(evb); + + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF); + tt_line_eq("Line with good crlf"); + free(cp); + evbuffer_validate(evb); + + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF); + tt_line_eq(""); + free(cp); + evbuffer_validate(evb); + + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF); + tt_line_eq("final"); + s = "x"; + evbuffer_validate(evb); + evbuffer_add(evb, s, 1); + evbuffer_validate(evb); + free(cp); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF); + tt_assert(!cp); + evbuffer_validate(evb); + + /* Test CRLF_STRICT */ + s = " and a bad crlf\nand a good one\r\n\r\nMore\r"; + evbuffer_add(evb, s, strlen(s)); + evbuffer_validate(evb); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT); + tt_line_eq("x and a bad crlf\nand a good one"); + free(cp); + evbuffer_validate(evb); + + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT); + tt_line_eq(""); + free(cp); + evbuffer_validate(evb); + + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT); + tt_assert(!cp); + evbuffer_validate(evb); + evbuffer_add(evb, "\n", 1); + evbuffer_validate(evb); + + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT); + tt_line_eq("More"); + free(cp); + tt_assert(evbuffer_get_length(evb) == 0); + evbuffer_validate(evb); + + s = "An internal CR\r is not an eol\r\nNor is a lack of one"; + evbuffer_add(evb, s, strlen(s)); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT); + tt_line_eq("An internal CR\r is not an eol"); + free(cp); + evbuffer_validate(evb); + + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT); + tt_assert(!cp); + evbuffer_validate(evb); + + evbuffer_add(evb, "\r\n", 2); + evbuffer_validate(evb); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT); + tt_line_eq("Nor is a lack of one"); + free(cp); + tt_assert(evbuffer_get_length(evb) == 0); + evbuffer_validate(evb); + + /* Test LF */ + s = "An\rand a nl\n\nText"; + evbuffer_add(evb, s, strlen(s)); + evbuffer_validate(evb); + + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_LF); + tt_line_eq("An\rand a nl"); + free(cp); + evbuffer_validate(evb); + + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_LF); + tt_line_eq(""); + free(cp); + evbuffer_validate(evb); + + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_LF); + tt_assert(!cp); + free(cp); + evbuffer_add(evb, "\n", 1); + evbuffer_validate(evb); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_LF); + tt_line_eq("Text"); + free(cp); + evbuffer_validate(evb); + + /* Test NUL */ + tt_int_op(evbuffer_get_length(evb), ==, 0); + { + char x[] = + "NUL\n\0\0" + "The all-zeros character which may serve\0" + "to accomplish time fill\0and media fill"; + /* Add all but the final NUL of x. */ + evbuffer_add(evb, x, sizeof(x)-1); + } + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_NUL); + tt_line_eq("NUL\n"); + free(cp); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_NUL); + tt_line_eq(""); + free(cp); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_NUL); + tt_line_eq("The all-zeros character which may serve"); + free(cp); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_NUL); + tt_line_eq("to accomplish time fill"); + free(cp); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_NUL); + tt_ptr_op(cp, ==, NULL); + evbuffer_drain(evb, -1); + + /* Test CRLF_STRICT - across boundaries*/ + s = " and a bad crlf\nand a good one\r"; + evbuffer_add(evb_tmp, s, strlen(s)); + evbuffer_validate(evb); + evbuffer_add_buffer(evb, evb_tmp); + evbuffer_validate(evb); + s = "\n\r"; + evbuffer_add(evb_tmp, s, strlen(s)); + evbuffer_validate(evb); + evbuffer_add_buffer(evb, evb_tmp); + evbuffer_validate(evb); + s = "\nMore\r"; + evbuffer_add(evb_tmp, s, strlen(s)); + evbuffer_validate(evb); + evbuffer_add_buffer(evb, evb_tmp); + evbuffer_validate(evb); + + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT); + tt_line_eq(" and a bad crlf\nand a good one"); + free(cp); + evbuffer_validate(evb); + + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT); + tt_line_eq(""); + free(cp); + evbuffer_validate(evb); + + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT); + tt_assert(!cp); + free(cp); + evbuffer_validate(evb); + evbuffer_add(evb, "\n", 1); + evbuffer_validate(evb); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_CRLF_STRICT); + tt_line_eq("More"); + free(cp); cp = NULL; + evbuffer_validate(evb); + tt_assert(evbuffer_get_length(evb) == 0); + + /* Test memory problem*/ + s = "one line\ntwo line\nblue line"; + evbuffer_add(evb_tmp, s, strlen(s)); + evbuffer_validate(evb); + evbuffer_add_buffer(evb, evb_tmp); + evbuffer_validate(evb); + + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_LF); + tt_line_eq("one line"); + free(cp); cp = NULL; + evbuffer_validate(evb); + + /* the next call to readline should fail */ +#ifndef EVENT__DISABLE_MM_REPLACEMENT + event_set_mem_functions(failing_malloc, realloc, free); + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_LF); + tt_assert(cp == NULL); + evbuffer_validate(evb); + + /* now we should get the next line back */ + event_set_mem_functions(malloc, realloc, free); +#endif + cp = evbuffer_readln(evb, &sz, EVBUFFER_EOL_LF); + tt_line_eq("two line"); + free(cp); cp = NULL; + evbuffer_validate(evb); + + end: + evbuffer_free(evb); + evbuffer_free(evb_tmp); + if (cp) free(cp); +} + +static void +test_evbuffer_search_eol(void *ptr) +{ + struct evbuffer *buf = evbuffer_new(); + struct evbuffer_ptr ptr1, ptr2; + const char *s; + size_t eol_len; + + s = "string! \r\n\r\nx\n"; + evbuffer_add(buf, s, strlen(s)); + eol_len = -1; + ptr1 = evbuffer_search_eol(buf, NULL, &eol_len, EVBUFFER_EOL_CRLF); + tt_int_op(ptr1.pos, ==, 8); + tt_int_op(eol_len, ==, 2); + + eol_len = -1; + ptr2 = evbuffer_search_eol(buf, &ptr1, &eol_len, EVBUFFER_EOL_CRLF); + tt_int_op(ptr2.pos, ==, 8); + tt_int_op(eol_len, ==, 2); + + evbuffer_ptr_set(buf, &ptr1, 1, EVBUFFER_PTR_ADD); + eol_len = -1; + ptr2 = evbuffer_search_eol(buf, &ptr1, &eol_len, EVBUFFER_EOL_CRLF); + tt_int_op(ptr2.pos, ==, 9); + tt_int_op(eol_len, ==, 1); + + eol_len = -1; + ptr2 = evbuffer_search_eol(buf, &ptr1, &eol_len, EVBUFFER_EOL_CRLF_STRICT); + tt_int_op(ptr2.pos, ==, 10); + tt_int_op(eol_len, ==, 2); + + eol_len = -1; + ptr1 = evbuffer_search_eol(buf, NULL, &eol_len, EVBUFFER_EOL_LF); + tt_int_op(ptr1.pos, ==, 9); + tt_int_op(eol_len, ==, 1); + + eol_len = -1; + ptr2 = evbuffer_search_eol(buf, &ptr1, &eol_len, EVBUFFER_EOL_LF); + tt_int_op(ptr2.pos, ==, 9); + tt_int_op(eol_len, ==, 1); + + evbuffer_ptr_set(buf, &ptr1, 1, EVBUFFER_PTR_ADD); + eol_len = -1; + ptr2 = evbuffer_search_eol(buf, &ptr1, &eol_len, EVBUFFER_EOL_LF); + tt_int_op(ptr2.pos, ==, 11); + tt_int_op(eol_len, ==, 1); + + tt_assert(evbuffer_ptr_set(buf, &ptr1, evbuffer_get_length(buf), EVBUFFER_PTR_SET) == 0); + eol_len = -1; + ptr2 = evbuffer_search_eol(buf, &ptr1, &eol_len, EVBUFFER_EOL_LF); + tt_int_op(ptr2.pos, ==, -1); + tt_int_op(eol_len, ==, 0); + +end: + evbuffer_free(buf); +} + +static void +test_evbuffer_iterative(void *ptr) +{ + struct evbuffer *buf = evbuffer_new(); + const char *abc = "abcdefghijklmnopqrstvuwxyzabcdefghijklmnopqrstvuwxyzabcdefghijklmnopqrstvuwxyzabcdefghijklmnopqrstvuwxyz"; + unsigned i, j, sum, n; + + sum = 0; + n = 0; + for (i = 0; i < 1000; ++i) { + for (j = 1; j < strlen(abc); ++j) { + char format[32]; + evutil_snprintf(format, sizeof(format), "%%%u.%us", j, j); + evbuffer_add_printf(buf, format, abc); + + /* Only check for rep violations every so often. + Walking over the whole list of chains can get + pretty expensive as it gets long. + */ + if ((n % 337) == 0) + evbuffer_validate(buf); + + sum += j; + n++; + } + } + evbuffer_validate(buf); + + tt_uint_op(sum, ==, evbuffer_get_length(buf)); + + { + size_t a,w,u; + a=w=u=0; + evbuffer_get_waste(buf, &a, &w, &u); + if (0) + printf("Allocated: %u.\nWasted: %u.\nUsed: %u.", + (unsigned)a, (unsigned)w, (unsigned)u); + tt_assert( ((double)w)/a < .125); + } + end: + evbuffer_free(buf); + +} + +static void +test_evbuffer_find(void *ptr) +{ + unsigned char* p; + const char* test1 = "1234567890\r\n"; + const char* test2 = "1234567890\r"; +#define EVBUFFER_INITIAL_LENGTH 256 + char test3[EVBUFFER_INITIAL_LENGTH]; + unsigned int i; + struct evbuffer * buf = evbuffer_new(); + + tt_assert(buf); + + /* make sure evbuffer_find doesn't match past the end of the buffer */ + evbuffer_add(buf, (unsigned char*)test1, strlen(test1)); + evbuffer_validate(buf); + evbuffer_drain(buf, strlen(test1)); + evbuffer_validate(buf); + evbuffer_add(buf, (unsigned char*)test2, strlen(test2)); + evbuffer_validate(buf); + p = evbuffer_find(buf, (unsigned char*)"\r\n", 2); + tt_want(p == NULL); + + /* + * drain the buffer and do another find; in r309 this would + * read past the allocated buffer causing a valgrind error. + */ + evbuffer_drain(buf, strlen(test2)); + evbuffer_validate(buf); + for (i = 0; i < EVBUFFER_INITIAL_LENGTH; ++i) + test3[i] = 'a'; + test3[EVBUFFER_INITIAL_LENGTH - 1] = 'x'; + evbuffer_add(buf, (unsigned char *)test3, EVBUFFER_INITIAL_LENGTH); + evbuffer_validate(buf); + p = evbuffer_find(buf, (unsigned char *)"xy", 2); + tt_want(p == NULL); + + /* simple test for match at end of allocated buffer */ + p = evbuffer_find(buf, (unsigned char *)"ax", 2); + tt_assert(p != NULL); + tt_want(strncmp((char*)p, "ax", 2) == 0); + +end: + if (buf) + evbuffer_free(buf); +} + +static void +test_evbuffer_ptr_set(void *ptr) +{ + struct evbuffer *buf = evbuffer_new(); + struct evbuffer_ptr pos; + struct evbuffer_iovec v[1]; + + tt_assert(buf); + + tt_int_op(evbuffer_get_length(buf), ==, 0); + + tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0); + tt_assert(pos.pos == 0); + tt_assert(evbuffer_ptr_set(buf, &pos, 1, EVBUFFER_PTR_ADD) == -1); + tt_assert(pos.pos == -1); + tt_assert(evbuffer_ptr_set(buf, &pos, 1, EVBUFFER_PTR_SET) == -1); + tt_assert(pos.pos == -1); + + /* create some chains */ + evbuffer_reserve_space(buf, 5000, v, 1); + v[0].iov_len = 5000; + memset(v[0].iov_base, 1, v[0].iov_len); + evbuffer_commit_space(buf, v, 1); + evbuffer_validate(buf); + + evbuffer_reserve_space(buf, 4000, v, 1); + v[0].iov_len = 4000; + memset(v[0].iov_base, 2, v[0].iov_len); + evbuffer_commit_space(buf, v, 1); + + evbuffer_reserve_space(buf, 3000, v, 1); + v[0].iov_len = 3000; + memset(v[0].iov_base, 3, v[0].iov_len); + evbuffer_commit_space(buf, v, 1); + evbuffer_validate(buf); + + tt_int_op(evbuffer_get_length(buf), ==, 12000); + + tt_assert(evbuffer_ptr_set(buf, &pos, 13000, EVBUFFER_PTR_SET) == -1); + tt_assert(pos.pos == -1); + tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0); + tt_assert(pos.pos == 0); + tt_assert(evbuffer_ptr_set(buf, &pos, 13000, EVBUFFER_PTR_ADD) == -1); + + tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0); + tt_assert(pos.pos == 0); + tt_assert(evbuffer_ptr_set(buf, &pos, 10000, EVBUFFER_PTR_ADD) == 0); + tt_assert(pos.pos == 10000); + tt_assert(evbuffer_ptr_set(buf, &pos, 1000, EVBUFFER_PTR_ADD) == 0); + tt_assert(pos.pos == 11000); + tt_assert(evbuffer_ptr_set(buf, &pos, 1000, EVBUFFER_PTR_ADD) == 0); + tt_assert(pos.pos == 12000); + tt_assert(evbuffer_ptr_set(buf, &pos, 1000, EVBUFFER_PTR_ADD) == -1); + tt_assert(pos.pos == -1); + +end: + if (buf) + evbuffer_free(buf); +} + +static void +test_evbuffer_search(void *ptr) +{ + struct evbuffer *buf = evbuffer_new(); + struct evbuffer *tmp = evbuffer_new(); + struct evbuffer_ptr pos, end; + + tt_assert(buf); + tt_assert(tmp); + + pos = evbuffer_search(buf, "x", 1, NULL); + tt_int_op(pos.pos, ==, -1); + tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0); + pos = evbuffer_search(buf, "x", 1, &pos); + tt_int_op(pos.pos, ==, -1); + tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0); + pos = evbuffer_search_range(buf, "x", 1, &pos, &pos); + tt_int_op(pos.pos, ==, -1); + tt_assert(evbuffer_ptr_set(buf, &pos, 0, EVBUFFER_PTR_SET) == 0); + pos = evbuffer_search_range(buf, "x", 1, &pos, NULL); + tt_int_op(pos.pos, ==, -1); + + /* set up our chains */ + evbuffer_add_printf(tmp, "hello"); /* 5 chars */ + evbuffer_add_buffer(buf, tmp); + evbuffer_add_printf(tmp, "foo"); /* 3 chars */ + evbuffer_add_buffer(buf, tmp); + evbuffer_add_printf(tmp, "cat"); /* 3 chars */ + evbuffer_add_buffer(buf, tmp); + evbuffer_add_printf(tmp, "attack"); + evbuffer_add_buffer(buf, tmp); + + pos = evbuffer_search(buf, "attack", 6, NULL); + tt_int_op(pos.pos, ==, 11); + pos = evbuffer_search(buf, "attacker", 8, NULL); + tt_int_op(pos.pos, ==, -1); + + /* test continuing search */ + pos = evbuffer_search(buf, "oc", 2, NULL); + tt_int_op(pos.pos, ==, 7); + pos = evbuffer_search(buf, "cat", 3, &pos); + tt_int_op(pos.pos, ==, 8); + pos = evbuffer_search(buf, "tacking", 7, &pos); + tt_int_op(pos.pos, ==, -1); + + evbuffer_ptr_set(buf, &pos, 5, EVBUFFER_PTR_SET); + pos = evbuffer_search(buf, "foo", 3, &pos); + tt_int_op(pos.pos, ==, 5); + + evbuffer_ptr_set(buf, &pos, 2, EVBUFFER_PTR_ADD); + pos = evbuffer_search(buf, "tat", 3, &pos); + tt_int_op(pos.pos, ==, 10); + + /* test bounded search. */ + /* Set "end" to the first t in "attack". */ + evbuffer_ptr_set(buf, &end, 12, EVBUFFER_PTR_SET); + pos = evbuffer_search_range(buf, "foo", 3, NULL, &end); + tt_int_op(pos.pos, ==, 5); + pos = evbuffer_search_range(buf, "foocata", 7, NULL, &end); + tt_int_op(pos.pos, ==, 5); + pos = evbuffer_search_range(buf, "foocatat", 8, NULL, &end); + tt_int_op(pos.pos, ==, -1); + pos = evbuffer_search_range(buf, "ack", 3, NULL, &end); + tt_int_op(pos.pos, ==, -1); + + /* Set "end" after the last byte in the buffer. */ + tt_assert(evbuffer_ptr_set(buf, &end, 17, EVBUFFER_PTR_SET) == 0); + + pos = evbuffer_search_range(buf, "attack", 6, NULL, &end); + tt_int_op(pos.pos, ==, 11); + tt_assert(evbuffer_ptr_set(buf, &pos, 11, EVBUFFER_PTR_SET) == 0); + pos = evbuffer_search_range(buf, "attack", 6, &pos, &end); + tt_int_op(pos.pos, ==, 11); + tt_assert(evbuffer_ptr_set(buf, &pos, 17, EVBUFFER_PTR_SET) == 0); + pos = evbuffer_search_range(buf, "attack", 6, &pos, &end); + tt_int_op(pos.pos, ==, -1); + tt_assert(evbuffer_ptr_set(buf, &pos, 17, EVBUFFER_PTR_SET) == 0); + pos = evbuffer_search_range(buf, "attack", 6, &pos, NULL); + tt_int_op(pos.pos, ==, -1); + +end: + if (buf) + evbuffer_free(buf); + if (tmp) + evbuffer_free(tmp); +} + +static void +log_change_callback(struct evbuffer *buffer, + const struct evbuffer_cb_info *cbinfo, + void *arg) +{ + + size_t old_len = cbinfo->orig_size; + size_t new_len = old_len + cbinfo->n_added - cbinfo->n_deleted; + struct evbuffer *out = arg; + evbuffer_add_printf(out, "%lu->%lu; ", (unsigned long)old_len, + (unsigned long)new_len); +} +static void +self_draining_callback(struct evbuffer *evbuffer, size_t old_len, + size_t new_len, void *arg) +{ + if (new_len > old_len) + evbuffer_drain(evbuffer, new_len); +} + +static void +test_evbuffer_callbacks(void *ptr) +{ + struct evbuffer *buf = evbuffer_new(); + struct evbuffer *buf_out1 = evbuffer_new(); + struct evbuffer *buf_out2 = evbuffer_new(); + struct evbuffer_cb_entry *cb1, *cb2; + + tt_assert(buf); + tt_assert(buf_out1); + tt_assert(buf_out2); + + cb1 = evbuffer_add_cb(buf, log_change_callback, buf_out1); + cb2 = evbuffer_add_cb(buf, log_change_callback, buf_out2); + + /* Let's run through adding and deleting some stuff from the buffer + * and turning the callbacks on and off and removing them. The callback + * adds a summary of length changes to buf_out1/buf_out2 when called. */ + /* size: 0-> 36. */ + evbuffer_add_printf(buf, "The %d magic words are spotty pudding", 2); + evbuffer_validate(buf); + evbuffer_cb_clear_flags(buf, cb2, EVBUFFER_CB_ENABLED); + evbuffer_drain(buf, 10); /*36->26*/ + evbuffer_validate(buf); + evbuffer_prepend(buf, "Hello", 5);/*26->31*/ + evbuffer_cb_set_flags(buf, cb2, EVBUFFER_CB_ENABLED); + evbuffer_add_reference(buf, "Goodbye", 7, NULL, NULL); /*31->38*/ + evbuffer_remove_cb_entry(buf, cb1); + evbuffer_validate(buf); + evbuffer_drain(buf, evbuffer_get_length(buf)); /*38->0*/; + tt_assert(-1 == evbuffer_remove_cb(buf, log_change_callback, NULL)); + evbuffer_add(buf, "X", 1); /* 0->1 */ + tt_assert(!evbuffer_remove_cb(buf, log_change_callback, buf_out2)); + evbuffer_validate(buf); + + tt_str_op((const char *) evbuffer_pullup(buf_out1, -1), ==, + "0->36; 36->26; 26->31; 31->38; "); + tt_str_op((const char *) evbuffer_pullup(buf_out2, -1), ==, + "0->36; 31->38; 38->0; 0->1; "); + evbuffer_drain(buf_out1, evbuffer_get_length(buf_out1)); + evbuffer_drain(buf_out2, evbuffer_get_length(buf_out2)); + /* Let's test the obsolete buffer_setcb function too. */ + cb1 = evbuffer_add_cb(buf, log_change_callback, buf_out1); + tt_assert(cb1 != NULL); + cb2 = evbuffer_add_cb(buf, log_change_callback, buf_out2); + tt_assert(cb2 != NULL); + evbuffer_setcb(buf, self_draining_callback, NULL); + evbuffer_add_printf(buf, "This should get drained right away."); + tt_uint_op(evbuffer_get_length(buf), ==, 0); + tt_uint_op(evbuffer_get_length(buf_out1), ==, 0); + tt_uint_op(evbuffer_get_length(buf_out2), ==, 0); + evbuffer_setcb(buf, NULL, NULL); + evbuffer_add_printf(buf, "This will not."); + tt_str_op((const char *) evbuffer_pullup(buf, -1), ==, "This will not."); + evbuffer_validate(buf); + evbuffer_drain(buf, evbuffer_get_length(buf)); + evbuffer_validate(buf); +#if 0 + /* Now let's try a suspended callback. */ + cb1 = evbuffer_add_cb(buf, log_change_callback, buf_out1); + cb2 = evbuffer_add_cb(buf, log_change_callback, buf_out2); + evbuffer_cb_suspend(buf,cb2); + evbuffer_prepend(buf,"Hello world",11); /*0->11*/ + evbuffer_validate(buf); + evbuffer_cb_suspend(buf,cb1); + evbuffer_add(buf,"more",4); /* 11->15 */ + evbuffer_cb_unsuspend(buf,cb2); + evbuffer_drain(buf, 4); /* 15->11 */ + evbuffer_cb_unsuspend(buf,cb1); + evbuffer_drain(buf, evbuffer_get_length(buf)); /* 11->0 */ + + tt_str_op(evbuffer_pullup(buf_out1, -1), ==, + "0->11; 11->11; 11->0; "); + tt_str_op(evbuffer_pullup(buf_out2, -1), ==, + "0->15; 15->11; 11->0; "); +#endif + + end: + if (buf) + evbuffer_free(buf); + if (buf_out1) + evbuffer_free(buf_out1); + if (buf_out2) + evbuffer_free(buf_out2); +} + +static int ref_done_cb_called_count = 0; +static void *ref_done_cb_called_with = NULL; +static const void *ref_done_cb_called_with_data = NULL; +static size_t ref_done_cb_called_with_len = 0; +static void ref_done_cb(const void *data, size_t len, void *info) +{ + ++ref_done_cb_called_count; + ref_done_cb_called_with = info; + ref_done_cb_called_with_data = data; + ref_done_cb_called_with_len = len; +} + +static void +test_evbuffer_add_reference(void *ptr) +{ + const char chunk1[] = "If you have found the answer to such a problem"; + const char chunk2[] = "you ought to write it up for publication"; + /* -- Knuth's "Notes on the Exercises" from TAOCP */ + char tmp[16]; + size_t len1 = strlen(chunk1), len2=strlen(chunk2); + + struct evbuffer *buf1 = NULL, *buf2 = NULL; + + buf1 = evbuffer_new(); + tt_assert(buf1); + + evbuffer_add_reference(buf1, chunk1, len1, ref_done_cb, (void*)111); + evbuffer_add(buf1, ", ", 2); + evbuffer_add_reference(buf1, chunk2, len2, ref_done_cb, (void*)222); + tt_int_op(evbuffer_get_length(buf1), ==, len1+len2+2); + + /* Make sure we can drain a little from a reference. */ + tt_int_op(evbuffer_remove(buf1, tmp, 6), ==, 6); + tt_int_op(memcmp(tmp, "If you", 6), ==, 0); + tt_int_op(evbuffer_remove(buf1, tmp, 5), ==, 5); + tt_int_op(memcmp(tmp, " have", 5), ==, 0); + + /* Make sure that prepending does not meddle with immutable data */ + tt_int_op(evbuffer_prepend(buf1, "I have ", 7), ==, 0); + tt_int_op(memcmp(chunk1, "If you", 6), ==, 0); + evbuffer_validate(buf1); + + /* Make sure that when the chunk is over, the callback is invoked. */ + evbuffer_drain(buf1, 7); /* Remove prepended stuff. */ + evbuffer_drain(buf1, len1-11-1); /* remove all but one byte of chunk1 */ + tt_int_op(ref_done_cb_called_count, ==, 0); + evbuffer_remove(buf1, tmp, 1); + tt_int_op(tmp[0], ==, 'm'); + tt_assert(ref_done_cb_called_with == (void*)111); + tt_assert(ref_done_cb_called_with_data == chunk1); + tt_assert(ref_done_cb_called_with_len == len1); + tt_int_op(ref_done_cb_called_count, ==, 1); + evbuffer_validate(buf1); + + /* Drain some of the remaining chunk, then add it to another buffer */ + evbuffer_drain(buf1, 6); /* Remove the ", you ". */ + buf2 = evbuffer_new(); + tt_assert(buf2); + tt_int_op(ref_done_cb_called_count, ==, 1); + evbuffer_add(buf2, "I ", 2); + + evbuffer_add_buffer(buf2, buf1); + tt_int_op(ref_done_cb_called_count, ==, 1); + evbuffer_remove(buf2, tmp, 16); + tt_int_op(memcmp("I ought to write", tmp, 16), ==, 0); + evbuffer_drain(buf2, evbuffer_get_length(buf2)); + tt_int_op(ref_done_cb_called_count, ==, 2); + tt_assert(ref_done_cb_called_with == (void*)222); + evbuffer_validate(buf2); + + /* Now add more stuff to buf1 and make sure that it gets removed on + * free. */ + evbuffer_add(buf1, "You shake and shake the ", 24); + evbuffer_add_reference(buf1, "ketchup bottle", 14, ref_done_cb, + (void*)3333); + evbuffer_add(buf1, ". Nothing comes and then a lot'll.", 35); + evbuffer_free(buf1); + buf1 = NULL; + tt_int_op(ref_done_cb_called_count, ==, 3); + tt_assert(ref_done_cb_called_with == (void*)3333); + +end: + if (buf1) + evbuffer_free(buf1); + if (buf2) + evbuffer_free(buf2); +} + +static void +test_evbuffer_multicast(void *ptr) +{ + const char chunk1[] = "If you have found the answer to such a problem"; + const char chunk2[] = "you ought to write it up for publication"; + /* -- Knuth's "Notes on the Exercises" from TAOCP */ + char tmp[16]; + size_t len1 = strlen(chunk1), len2=strlen(chunk2); + + struct evbuffer *buf1 = NULL, *buf2 = NULL; + + buf1 = evbuffer_new(); + tt_assert(buf1); + + evbuffer_add(buf1, chunk1, len1); + evbuffer_add(buf1, ", ", 2); + evbuffer_add(buf1, chunk2, len2); + tt_int_op(evbuffer_get_length(buf1), ==, len1+len2+2); + + buf2 = evbuffer_new(); + tt_assert(buf2); + + tt_int_op(evbuffer_add_buffer_reference(buf2, buf1), ==, 0); + /* nested references are not allowed */ + tt_int_op(evbuffer_add_buffer_reference(buf2, buf2), ==, -1); + tt_int_op(evbuffer_add_buffer_reference(buf1, buf2), ==, -1); + + /* both buffers contain the same amount of data */ + tt_int_op(evbuffer_get_length(buf1), ==, evbuffer_get_length(buf1)); + + /* Make sure we can drain a little from the first buffer. */ + tt_int_op(evbuffer_remove(buf1, tmp, 6), ==, 6); + tt_int_op(memcmp(tmp, "If you", 6), ==, 0); + tt_int_op(evbuffer_remove(buf1, tmp, 5), ==, 5); + tt_int_op(memcmp(tmp, " have", 5), ==, 0); + + /* Make sure that prepending does not meddle with immutable data */ + tt_int_op(evbuffer_prepend(buf1, "I have ", 7), ==, 0); + tt_int_op(memcmp(chunk1, "If you", 6), ==, 0); + evbuffer_validate(buf1); + + /* Make sure we can drain a little from the second buffer. */ + tt_int_op(evbuffer_remove(buf2, tmp, 6), ==, 6); + tt_int_op(memcmp(tmp, "If you", 6), ==, 0); + tt_int_op(evbuffer_remove(buf2, tmp, 5), ==, 5); + tt_int_op(memcmp(tmp, " have", 5), ==, 0); + + /* Make sure that prepending does not meddle with immutable data */ + tt_int_op(evbuffer_prepend(buf2, "I have ", 7), ==, 0); + tt_int_op(memcmp(chunk1, "If you", 6), ==, 0); + evbuffer_validate(buf2); + + /* Make sure the data can be read from the second buffer when the first is freed */ + evbuffer_free(buf1); + buf1 = NULL; + + tt_int_op(evbuffer_remove(buf2, tmp, 6), ==, 6); + tt_int_op(memcmp(tmp, "I have", 6), ==, 0); + + tt_int_op(evbuffer_remove(buf2, tmp, 6), ==, 6); + tt_int_op(memcmp(tmp, " foun", 6), ==, 0); + +end: + if (buf1) + evbuffer_free(buf1); + if (buf2) + evbuffer_free(buf2); +} + +static void +test_evbuffer_multicast_drain(void *ptr) +{ + const char chunk1[] = "If you have found the answer to such a problem"; + const char chunk2[] = "you ought to write it up for publication"; + /* -- Knuth's "Notes on the Exercises" from TAOCP */ + size_t len1 = strlen(chunk1), len2=strlen(chunk2); + + struct evbuffer *buf1 = NULL, *buf2 = NULL; + + buf1 = evbuffer_new(); + tt_assert(buf1); + + evbuffer_add(buf1, chunk1, len1); + evbuffer_add(buf1, ", ", 2); + evbuffer_add(buf1, chunk2, len2); + tt_int_op(evbuffer_get_length(buf1), ==, len1+len2+2); + + buf2 = evbuffer_new(); + tt_assert(buf2); + + tt_int_op(evbuffer_add_buffer_reference(buf2, buf1), ==, 0); + tt_int_op(evbuffer_get_length(buf2), ==, len1+len2+2); + tt_int_op(evbuffer_drain(buf1, evbuffer_get_length(buf1)), ==, 0); + tt_int_op(evbuffer_get_length(buf2), ==, len1+len2+2); + tt_int_op(evbuffer_drain(buf2, evbuffer_get_length(buf2)), ==, 0); + evbuffer_validate(buf1); + evbuffer_validate(buf2); + +end: + if (buf1) + evbuffer_free(buf1); + if (buf2) + evbuffer_free(buf2); +} + +/* Some cases that we didn't get in test_evbuffer() above, for more coverage. */ +static void +test_evbuffer_prepend(void *ptr) +{ + struct evbuffer *buf1 = NULL, *buf2 = NULL; + char tmp[128]; + int n; + + buf1 = evbuffer_new(); + tt_assert(buf1); + + /* Case 0: The evbuffer is entirely empty. */ + evbuffer_prepend(buf1, "This string has 29 characters", 29); + evbuffer_validate(buf1); + + /* Case 1: Prepend goes entirely in new chunk. */ + evbuffer_prepend(buf1, "Short.", 6); + evbuffer_validate(buf1); + + /* Case 2: prepend goes entirely in first chunk. */ + evbuffer_drain(buf1, 6+11); + evbuffer_prepend(buf1, "it", 2); + evbuffer_validate(buf1); + tt_assert(!memcmp(buf1->first->buffer+buf1->first->misalign, + "it has", 6)); + + /* Case 3: prepend is split over multiple chunks. */ + evbuffer_prepend(buf1, "It is no longer true to say ", 28); + evbuffer_validate(buf1); + n = evbuffer_remove(buf1, tmp, sizeof(tmp)-1); + tt_int_op(n, >=, 0); + tmp[n]='\0'; + tt_str_op(tmp,==,"It is no longer true to say it has 29 characters"); + + buf2 = evbuffer_new(); + tt_assert(buf2); + + /* Case 4: prepend a buffer to an empty buffer. */ + n = 999; + evbuffer_add_printf(buf1, "Here is string %d. ", n++); + evbuffer_prepend_buffer(buf2, buf1); + evbuffer_validate(buf2); + + /* Case 5: prepend a buffer to a nonempty buffer. */ + evbuffer_add_printf(buf1, "Here is string %d. ", n++); + evbuffer_prepend_buffer(buf2, buf1); + evbuffer_validate(buf2); + evbuffer_validate(buf1); + n = evbuffer_remove(buf2, tmp, sizeof(tmp)-1); + tt_int_op(n, >=, 0); + tmp[n]='\0'; + tt_str_op(tmp,==,"Here is string 1000. Here is string 999. "); + +end: + if (buf1) + evbuffer_free(buf1); + if (buf2) + evbuffer_free(buf2); + +} + +static void +test_evbuffer_peek_first_gt(void *info) +{ + struct evbuffer *buf = NULL, *tmp_buf = NULL; + struct evbuffer_ptr ptr; + struct evbuffer_iovec v[2]; + + buf = evbuffer_new(); + tmp_buf = evbuffer_new(); + evbuffer_add_printf(tmp_buf, "Contents of chunk 100\n"); + evbuffer_add_buffer(buf, tmp_buf); + evbuffer_add_printf(tmp_buf, "Contents of chunk 1\n"); + evbuffer_add_buffer(buf, tmp_buf); + + evbuffer_ptr_set(buf, &ptr, 0, EVBUFFER_PTR_SET); + + /** The only case that matters*/ + tt_int_op(evbuffer_peek(buf, -1, &ptr, NULL, 0), ==, 2); + /** Just in case */ + tt_int_op(evbuffer_peek(buf, -1, &ptr, v, 2), ==, 2); + + evbuffer_ptr_set(buf, &ptr, 20, EVBUFFER_PTR_ADD); + tt_int_op(evbuffer_peek(buf, -1, &ptr, NULL, 0), ==, 2); + tt_int_op(evbuffer_peek(buf, -1, &ptr, v, 2), ==, 2); + tt_int_op(evbuffer_peek(buf, 2, &ptr, NULL, 0), ==, 1); + tt_int_op(evbuffer_peek(buf, 2, &ptr, v, 2), ==, 1); + tt_int_op(evbuffer_peek(buf, 3, &ptr, NULL, 0), ==, 2); + tt_int_op(evbuffer_peek(buf, 3, &ptr, v, 2), ==, 2); + +end: + if (buf) + evbuffer_free(buf); + if (tmp_buf) + evbuffer_free(tmp_buf); +} + +static void +test_evbuffer_peek(void *info) +{ + struct evbuffer *buf = NULL, *tmp_buf = NULL; + int i; + struct evbuffer_iovec v[20]; + struct evbuffer_ptr ptr; + +#define tt_iov_eq(v, s) \ + tt_int_op((v)->iov_len, ==, strlen(s)); \ + tt_assert(!memcmp((v)->iov_base, (s), strlen(s))) + + /* Let's make a very fragmented buffer. */ + buf = evbuffer_new(); + tmp_buf = evbuffer_new(); + for (i = 0; i < 16; ++i) { + evbuffer_add_printf(tmp_buf, "Contents of chunk [%d]\n", i); + evbuffer_add_buffer(buf, tmp_buf); + } + + /* How many chunks do we need for everything? */ + i = evbuffer_peek(buf, -1, NULL, NULL, 0); + tt_int_op(i, ==, 16); + + /* Simple peek: get everything. */ + i = evbuffer_peek(buf, -1, NULL, v, 20); + tt_int_op(i, ==, 16); /* we used only 16 chunks. */ + tt_iov_eq(&v[0], "Contents of chunk [0]\n"); + tt_iov_eq(&v[3], "Contents of chunk [3]\n"); + tt_iov_eq(&v[12], "Contents of chunk [12]\n"); + tt_iov_eq(&v[15], "Contents of chunk [15]\n"); + + /* Just get one chunk worth. */ + memset(v, 0, sizeof(v)); + i = evbuffer_peek(buf, -1, NULL, v, 1); + tt_int_op(i, ==, 1); + tt_iov_eq(&v[0], "Contents of chunk [0]\n"); + tt_assert(v[1].iov_base == NULL); + + /* Suppose we want at least the first 40 bytes. */ + memset(v, 0, sizeof(v)); + i = evbuffer_peek(buf, 40, NULL, v, 16); + tt_int_op(i, ==, 2); + tt_iov_eq(&v[0], "Contents of chunk [0]\n"); + tt_iov_eq(&v[1], "Contents of chunk [1]\n"); + tt_assert(v[2].iov_base == NULL); + + /* How many chunks do we need for 100 bytes? */ + memset(v, 0, sizeof(v)); + i = evbuffer_peek(buf, 100, NULL, NULL, 0); + tt_int_op(i, ==, 5); + tt_assert(v[0].iov_base == NULL); + + /* Now we ask for more bytes than we provide chunks for */ + memset(v, 0, sizeof(v)); + i = evbuffer_peek(buf, 60, NULL, v, 1); + tt_int_op(i, ==, 3); + tt_iov_eq(&v[0], "Contents of chunk [0]\n"); + tt_assert(v[1].iov_base == NULL); + + /* Now we ask for more bytes than the buffer has. */ + memset(v, 0, sizeof(v)); + i = evbuffer_peek(buf, 65536, NULL, v, 20); + tt_int_op(i, ==, 16); /* we used only 16 chunks. */ + tt_iov_eq(&v[0], "Contents of chunk [0]\n"); + tt_iov_eq(&v[3], "Contents of chunk [3]\n"); + tt_iov_eq(&v[12], "Contents of chunk [12]\n"); + tt_iov_eq(&v[15], "Contents of chunk [15]\n"); + tt_assert(v[16].iov_base == NULL); + + /* What happens if we try an empty buffer? */ + memset(v, 0, sizeof(v)); + i = evbuffer_peek(tmp_buf, -1, NULL, v, 20); + tt_int_op(i, ==, 0); + tt_assert(v[0].iov_base == NULL); + memset(v, 0, sizeof(v)); + i = evbuffer_peek(tmp_buf, 50, NULL, v, 20); + tt_int_op(i, ==, 0); + tt_assert(v[0].iov_base == NULL); + + /* Okay, now time to have fun with pointers. */ + memset(v, 0, sizeof(v)); + evbuffer_ptr_set(buf, &ptr, 30, EVBUFFER_PTR_SET); + i = evbuffer_peek(buf, 50, &ptr, v, 20); + tt_int_op(i, ==, 3); + tt_iov_eq(&v[0], " of chunk [1]\n"); + tt_iov_eq(&v[1], "Contents of chunk [2]\n"); + tt_iov_eq(&v[2], "Contents of chunk [3]\n"); /*more than we asked for*/ + + /* advance to the start of another chain. */ + memset(v, 0, sizeof(v)); + evbuffer_ptr_set(buf, &ptr, 14, EVBUFFER_PTR_ADD); + i = evbuffer_peek(buf, 44, &ptr, v, 20); + tt_int_op(i, ==, 2); + tt_iov_eq(&v[0], "Contents of chunk [2]\n"); + tt_iov_eq(&v[1], "Contents of chunk [3]\n"); /*more than we asked for*/ + + /* peek at the end of the buffer */ + memset(v, 0, sizeof(v)); + tt_assert(evbuffer_ptr_set(buf, &ptr, evbuffer_get_length(buf), EVBUFFER_PTR_SET) == 0); + i = evbuffer_peek(buf, 44, &ptr, v, 20); + tt_int_op(i, ==, 0); + tt_assert(v[0].iov_base == NULL); + +end: + if (buf) + evbuffer_free(buf); + if (tmp_buf) + evbuffer_free(tmp_buf); +} + +/* Check whether evbuffer freezing works right. This is called twice, + once with the argument "start" and once with the argument "end". + When we test "start", we freeze the start of an evbuffer and make sure + that modifying the start of the buffer doesn't work. When we test + "end", we freeze the end of an evbuffer and make sure that modifying + the end of the buffer doesn't work. + */ +static void +test_evbuffer_freeze(void *ptr) +{ + struct evbuffer *buf = NULL, *tmp_buf=NULL; + const char string[] = /* Year's End, Richard Wilbur */ + "I've known the wind by water banks to shake\n" + "The late leaves down, which frozen where they fell\n" + "And held in ice as dancers in a spell\n" + "Fluttered all winter long into a lake..."; + const int start = !strcmp(ptr, "start"); + char *cp; + char charbuf[128]; + int r; + size_t orig_length; + struct evbuffer_iovec v[1]; + + if (!start) + tt_str_op(ptr, ==, "end"); + + buf = evbuffer_new(); + tmp_buf = evbuffer_new(); + tt_assert(tmp_buf); + + evbuffer_add(buf, string, strlen(string)); + evbuffer_freeze(buf, start); /* Freeze the start or the end.*/ + +#define FREEZE_EQ(a, startcase, endcase) \ + do { \ + if (start) { \ + tt_int_op((a), ==, (startcase)); \ + } else { \ + tt_int_op((a), ==, (endcase)); \ + } \ + } while (0) + + + orig_length = evbuffer_get_length(buf); + + /* These functions all manipulate the end of buf. */ + r = evbuffer_add(buf, "abc", 0); + FREEZE_EQ(r, 0, -1); + r = evbuffer_reserve_space(buf, 10, v, 1); + FREEZE_EQ(r, 1, -1); + if (r == 1) { + memset(v[0].iov_base, 'X', 10); + v[0].iov_len = 10; + } + r = evbuffer_commit_space(buf, v, 1); + FREEZE_EQ(r, 0, -1); + r = evbuffer_add_reference(buf, string, 5, NULL, NULL); + FREEZE_EQ(r, 0, -1); + r = evbuffer_add_printf(buf, "Hello %s", "world"); + FREEZE_EQ(r, 11, -1); + /* TODO: test add_buffer, add_file, read */ + + if (!start) + tt_int_op(orig_length, ==, evbuffer_get_length(buf)); + + orig_length = evbuffer_get_length(buf); + + /* These functions all manipulate the start of buf. */ + r = evbuffer_remove(buf, charbuf, 1); + FREEZE_EQ(r, -1, 1); + r = evbuffer_drain(buf, 3); + FREEZE_EQ(r, -1, 0); + r = evbuffer_prepend(buf, "dummy", 5); + FREEZE_EQ(r, -1, 0); + cp = evbuffer_readln(buf, NULL, EVBUFFER_EOL_LF); + FREEZE_EQ(cp==NULL, 1, 0); + if (cp) + free(cp); + /* TODO: Test remove_buffer, add_buffer, write, prepend_buffer */ + + if (start) + tt_int_op(orig_length, ==, evbuffer_get_length(buf)); + +end: + if (buf) + evbuffer_free(buf); + + if (tmp_buf) + evbuffer_free(tmp_buf); +} + +static void +test_evbuffer_add_iovec(void * ptr) +{ + struct evbuffer * buf = NULL; + struct evbuffer_iovec vec[4]; + const char * data[] = { + "Guilt resembles a sword with two edges.", + "On the one hand, it cuts for Justice, imposing practical morality upon those who fear it.", + "Conscience does not always adhere to rational judgment.", + "Guilt is always a self-imposed burden, but it is not always rightly imposed." + /* -- R.A. Salvatore, _Sojurn_ */ + }; + size_t expected_length = 0; + size_t returned_length = 0; + int i; + + buf = evbuffer_new(); + + tt_assert(buf); + + for (i = 0; i < 4; i++) { + vec[i].iov_len = strlen(data[i]); + vec[i].iov_base = (char*) data[i]; + expected_length += vec[i].iov_len; + } + + returned_length = evbuffer_add_iovec(buf, vec, 4); + + tt_int_op(returned_length, ==, evbuffer_get_length(buf)); + tt_int_op(evbuffer_get_length(buf), ==, expected_length); + + for (i = 0; i < 4; i++) { + char charbuf[1024]; + + memset(charbuf, 0, 1024); + evbuffer_remove(buf, charbuf, strlen(data[i])); + tt_assert(strcmp(charbuf, data[i]) == 0); + } + + tt_assert(evbuffer_get_length(buf) == 0); +end: + if (buf) { + evbuffer_free(buf); + } +} + +static void +test_evbuffer_copyout(void *dummy) +{ + const char string[] = + "Still they skirmish to and fro, men my messmates on the snow " + "When we headed off the aurochs turn for turn; " + "When the rich Allobrogenses never kept amanuenses, " + "And our only plots were piled in lakes at Berne."; + /* -- Kipling, "In The Neolithic Age" */ + char tmp[1024]; + struct evbuffer_ptr ptr; + struct evbuffer *buf; + + (void)dummy; + + buf = evbuffer_new(); + tt_assert(buf); + + tt_int_op(strlen(string), ==, 206); + + /* Ensure separate chains */ + evbuffer_add_reference(buf, string, 80, no_cleanup, NULL); + evbuffer_add_reference(buf, string+80, 80, no_cleanup, NULL); + evbuffer_add(buf, string+160, strlen(string)-160); + + tt_int_op(206, ==, evbuffer_get_length(buf)); + + /* First, let's test plain old copyout. */ + + /* Copy a little from the beginning. */ + tt_int_op(10, ==, evbuffer_copyout(buf, tmp, 10)); + tt_int_op(0, ==, memcmp(tmp, "Still they", 10)); + + /* Now copy more than a little from the beginning */ + memset(tmp, 0, sizeof(tmp)); + tt_int_op(100, ==, evbuffer_copyout(buf, tmp, 100)); + tt_int_op(0, ==, memcmp(tmp, string, 100)); + + /* Copy too much; ensure truncation. */ + memset(tmp, 0, sizeof(tmp)); + tt_int_op(206, ==, evbuffer_copyout(buf, tmp, 230)); + tt_int_op(0, ==, memcmp(tmp, string, 206)); + + /* That was supposed to be nondestructive, btw */ + tt_int_op(206, ==, evbuffer_get_length(buf)); + + /* Now it's time to test copyout_from! First, let's start in the + * first chain. */ + evbuffer_ptr_set(buf, &ptr, 15, EVBUFFER_PTR_SET); + memset(tmp, 0, sizeof(tmp)); + tt_int_op(10, ==, evbuffer_copyout_from(buf, &ptr, tmp, 10)); + tt_int_op(0, ==, memcmp(tmp, "mish to an", 10)); + + /* Right up to the end of the first chain */ + memset(tmp, 0, sizeof(tmp)); + tt_int_op(65, ==, evbuffer_copyout_from(buf, &ptr, tmp, 65)); + tt_int_op(0, ==, memcmp(tmp, string+15, 65)); + + /* Span into the second chain */ + memset(tmp, 0, sizeof(tmp)); + tt_int_op(90, ==, evbuffer_copyout_from(buf, &ptr, tmp, 90)); + tt_int_op(0, ==, memcmp(tmp, string+15, 90)); + + /* Span into the third chain */ + memset(tmp, 0, sizeof(tmp)); + tt_int_op(160, ==, evbuffer_copyout_from(buf, &ptr, tmp, 160)); + tt_int_op(0, ==, memcmp(tmp, string+15, 160)); + + /* Overrun */ + memset(tmp, 0, sizeof(tmp)); + tt_int_op(206-15, ==, evbuffer_copyout_from(buf, &ptr, tmp, 999)); + tt_int_op(0, ==, memcmp(tmp, string+15, 206-15)); + + /* That was supposed to be nondestructive, too */ + tt_int_op(206, ==, evbuffer_get_length(buf)); + +end: + if (buf) + evbuffer_free(buf); +} + +static void * +setup_passthrough(const struct testcase_t *testcase) +{ + return testcase->setup_data; +} +static int +cleanup_passthrough(const struct testcase_t *testcase, void *ptr) +{ + (void) ptr; + return 1; +} + +static const struct testcase_setup_t nil_setup = { + setup_passthrough, + cleanup_passthrough +}; + +struct testcase_t evbuffer_testcases[] = { + { "evbuffer", test_evbuffer, 0, NULL, NULL }, + { "remove_buffer_with_empty", test_evbuffer_remove_buffer_with_empty, 0, NULL, NULL }, + { "reserve2", test_evbuffer_reserve2, 0, NULL, NULL }, + { "reserve_many", test_evbuffer_reserve_many, 0, NULL, NULL }, + { "reserve_many2", test_evbuffer_reserve_many, 0, &nil_setup, (void*)"add" }, + { "reserve_many3", test_evbuffer_reserve_many, 0, &nil_setup, (void*)"fill" }, + { "expand", test_evbuffer_expand, 0, NULL, NULL }, + { "reference", test_evbuffer_reference, 0, NULL, NULL }, + { "iterative", test_evbuffer_iterative, 0, NULL, NULL }, + { "readln", test_evbuffer_readln, TT_NO_LOGS, &basic_setup, NULL }, + { "search_eol", test_evbuffer_search_eol, 0, NULL, NULL }, + { "find", test_evbuffer_find, 0, NULL, NULL }, + { "ptr_set", test_evbuffer_ptr_set, 0, NULL, NULL }, + { "search", test_evbuffer_search, 0, NULL, NULL }, + { "callbacks", test_evbuffer_callbacks, 0, NULL, NULL }, + { "add_reference", test_evbuffer_add_reference, 0, NULL, NULL }, + { "multicast", test_evbuffer_multicast, 0, NULL, NULL }, + { "multicast_drain", test_evbuffer_multicast_drain, 0, NULL, NULL }, + { "prepend", test_evbuffer_prepend, TT_FORK, NULL, NULL }, + { "peek", test_evbuffer_peek, 0, NULL, NULL }, + { "peek_first_gt", test_evbuffer_peek_first_gt, 0, NULL, NULL }, + { "freeze_start", test_evbuffer_freeze, 0, &nil_setup, (void*)"start" }, + { "freeze_end", test_evbuffer_freeze, 0, &nil_setup, (void*)"end" }, + { "add_iovec", test_evbuffer_add_iovec, 0, NULL, NULL}, + { "copyout", test_evbuffer_copyout, 0, NULL, NULL}, + { "file_segment_add_cleanup_cb", test_evbuffer_file_segment_add_cleanup_cb, 0, NULL, NULL }, + +#define ADDFILE_TEST(name, parameters) \ + { name, test_evbuffer_add_file, TT_FORK|TT_NEED_BASE, \ + &basic_setup, (void*)(parameters) } + +#define ADDFILE_TEST_GROUP(name, parameters) \ + ADDFILE_TEST(name "_sendfile", "sendfile " parameters), \ + ADDFILE_TEST(name "_mmap", "mmap " parameters), \ + ADDFILE_TEST(name "_linear", "linear " parameters) + + ADDFILE_TEST_GROUP("add_file", ""), + ADDFILE_TEST("add_file_nosegment", "default nosegment"), + + ADDFILE_TEST_GROUP("add_big_file", "bigfile"), + ADDFILE_TEST("add_big_file_nosegment", "default nosegment bigfile"), + + ADDFILE_TEST_GROUP("add_file_offset", "bigfile map_offset"), + ADDFILE_TEST("add_file_offset_nosegment", + "default nosegment bigfile map_offset"), + + ADDFILE_TEST_GROUP("add_file_offset2", "bigfile offset_in_segment"), + + ADDFILE_TEST_GROUP("add_file_offset3", + "bigfile offset_in_segment map_offset"), + + END_OF_TESTCASES +}; diff --git a/libs/libevent/docs/test/regress_bufferevent.c b/libs/libevent/docs/test/regress_bufferevent.c new file mode 100644 index 0000000000..68e6876405 --- /dev/null +++ b/libs/libevent/docs/test/regress_bufferevent.c @@ -0,0 +1,1284 @@ +/* + * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "util-internal.h" + +/* The old tests here need assertions to work. */ +#undef NDEBUG + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#endif + +#include "event2/event-config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/queue.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <sys/wait.h> +#include <signal.h> +#include <unistd.h> +#include <netdb.h> +#include <netinet/in.h> +#endif +#include <fcntl.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#ifdef EVENT__HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#include "event2/event-config.h" +#include "event2/event.h" +#include "event2/event_struct.h" +#include "event2/event_compat.h" +#include "event2/tag.h" +#include "event2/buffer.h" +#include "event2/bufferevent.h" +#include "event2/bufferevent_compat.h" +#include "event2/bufferevent_struct.h" +#include "event2/listener.h" +#include "event2/util.h" + +#include "bufferevent-internal.h" +#include "evthread-internal.h" +#include "util-internal.h" +#ifdef _WIN32 +#include "iocp-internal.h" +#endif + +#include "regress.h" +#include "regress_testutils.h" + +/* + * simple bufferevent test + */ + +static void +readcb(struct bufferevent *bev, void *arg) +{ + if (evbuffer_get_length(bev->input) == 8333) { + struct evbuffer *evbuf = evbuffer_new(); + assert(evbuf != NULL); + + /* gratuitous test of bufferevent_read_buffer */ + bufferevent_read_buffer(bev, evbuf); + + bufferevent_disable(bev, EV_READ); + + if (evbuffer_get_length(evbuf) == 8333) { + test_ok++; + } + + evbuffer_free(evbuf); + } +} + +static void +writecb(struct bufferevent *bev, void *arg) +{ + if (evbuffer_get_length(bev->output) == 0) { + test_ok++; + } +} + +static void +errorcb(struct bufferevent *bev, short what, void *arg) +{ + test_ok = -2; +} + +static void +test_bufferevent_impl(int use_pair, int flush) +{ + struct bufferevent *bev1 = NULL, *bev2 = NULL; + char buffer[8333]; + int i; + int expected = 2; + + if (use_pair) { + struct bufferevent *pair[2]; + tt_assert(0 == bufferevent_pair_new(NULL, 0, pair)); + bev1 = pair[0]; + bev2 = pair[1]; + bufferevent_setcb(bev1, readcb, writecb, errorcb, bev1); + bufferevent_setcb(bev2, readcb, writecb, errorcb, NULL); + tt_int_op(bufferevent_getfd(bev1), ==, -1); + tt_ptr_op(bufferevent_get_underlying(bev1), ==, NULL); + tt_ptr_op(bufferevent_pair_get_partner(bev1), ==, bev2); + tt_ptr_op(bufferevent_pair_get_partner(bev2), ==, bev1); + } else { + bev1 = bufferevent_new(pair[0], readcb, writecb, errorcb, NULL); + bev2 = bufferevent_new(pair[1], readcb, writecb, errorcb, NULL); + tt_int_op(bufferevent_getfd(bev1), ==, pair[0]); + tt_ptr_op(bufferevent_get_underlying(bev1), ==, NULL); + tt_ptr_op(bufferevent_pair_get_partner(bev1), ==, NULL); + tt_ptr_op(bufferevent_pair_get_partner(bev2), ==, NULL); + } + + { + /* Test getcb. */ + bufferevent_data_cb r, w; + bufferevent_event_cb e; + void *a; + bufferevent_getcb(bev1, &r, &w, &e, &a); + tt_ptr_op(r, ==, readcb); + tt_ptr_op(w, ==, writecb); + tt_ptr_op(e, ==, errorcb); + tt_ptr_op(a, ==, use_pair ? bev1 : NULL); + } + + bufferevent_disable(bev1, EV_READ); + bufferevent_enable(bev2, EV_READ); + + tt_int_op(bufferevent_get_enabled(bev1), ==, EV_WRITE); + tt_int_op(bufferevent_get_enabled(bev2), ==, EV_WRITE|EV_READ); + + for (i = 0; i < (int)sizeof(buffer); i++) + buffer[i] = i; + + bufferevent_write(bev1, buffer, sizeof(buffer)); + if (flush >= 0) { + tt_int_op(bufferevent_flush(bev1, EV_WRITE, flush), >=, 0); + } + + event_dispatch(); + + bufferevent_free(bev2); + tt_ptr_op(bufferevent_pair_get_partner(bev1), ==, NULL); + bufferevent_free(bev1); + + /** Only pair call errorcb for BEV_FINISHED */ + if (use_pair && flush == BEV_FINISHED) { + expected = -1; + } + if (test_ok != expected) + test_ok = 0; +end: + ; +} + +static void test_bufferevent(void) { test_bufferevent_impl(0, -1); } +static void test_bufferevent_pair(void) { test_bufferevent_impl(1, -1); } + +static void test_bufferevent_flush_normal(void) { test_bufferevent_impl(0, BEV_NORMAL); } +static void test_bufferevent_flush_flush(void) { test_bufferevent_impl(0, BEV_FLUSH); } +static void test_bufferevent_flush_finished(void) { test_bufferevent_impl(0, BEV_FINISHED); } + +static void test_bufferevent_pair_flush_normal(void) { test_bufferevent_impl(1, BEV_NORMAL); } +static void test_bufferevent_pair_flush_flush(void) { test_bufferevent_impl(1, BEV_FLUSH); } +static void test_bufferevent_pair_flush_finished(void) { test_bufferevent_impl(1, BEV_FINISHED); } + +#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) +/** + * Trace lock/unlock/alloc/free for locks. + * (More heavier then evthread_debug*) + */ +typedef struct +{ + void *lock; + enum { + ALLOC, FREE, + } status; + size_t locked /** allow recursive locking */; +} lock_wrapper; +struct lock_unlock_base +{ + /* Original callbacks */ + struct evthread_lock_callbacks cbs; + /* Map of locks */ + lock_wrapper *locks; + size_t nr_locks; +} lu_base = { + .locks = NULL, +}; + +static lock_wrapper *lu_find(void *lock_) +{ + size_t i; + for (i = 0; i < lu_base.nr_locks; ++i) { + lock_wrapper *lock = &lu_base.locks[i]; + if (lock->lock == lock_) + return lock; + } + return NULL; +} + +static void *trace_lock_alloc(unsigned locktype) +{ + void *lock; + ++lu_base.nr_locks; + lu_base.locks = realloc(lu_base.locks, + sizeof(lock_wrapper) * lu_base.nr_locks); + lock = lu_base.cbs.alloc(locktype); + lu_base.locks[lu_base.nr_locks - 1] = (lock_wrapper){ lock, ALLOC, 0 }; + return lock; +} +static void trace_lock_free(void *lock_, unsigned locktype) +{ + lock_wrapper *lock = lu_find(lock_); + if (!lock || lock->status == FREE || lock->locked) { + TT_FAIL(("lock: free error")); + } else { + lock->status = FREE; + lu_base.cbs.free(lock_, locktype); + } +} +static int trace_lock_lock(unsigned mode, void *lock_) +{ + lock_wrapper *lock = lu_find(lock_); + if (!lock || lock->status == FREE) { + TT_FAIL(("lock: lock error")); + return -1; + } else { + ++lock->locked; + return lu_base.cbs.lock(mode, lock_); + } +} +static int trace_lock_unlock(unsigned mode, void *lock_) +{ + lock_wrapper *lock = lu_find(lock_); + if (!lock || lock->status == FREE || !lock->locked) { + TT_FAIL(("lock: unlock error")); + return -1; + } else { + --lock->locked; + return lu_base.cbs.unlock(mode, lock_); + } +} +static void lock_unlock_free_thread_cbs(void) +{ + event_base_free(NULL); + + if (libevent_tests_running_in_debug_mode) + libevent_global_shutdown(); + + /** drop immutable flag */ + evthread_set_lock_callbacks(NULL); + /** avoid calling of event_global_setup_locks_() for new cbs */ + libevent_global_shutdown(); + /** drop immutable flag for non-debug ops (since called after shutdown) */ + evthread_set_lock_callbacks(NULL); +} + +static int use_lock_unlock_profiler(void) +{ + struct evthread_lock_callbacks cbs = { + EVTHREAD_LOCK_API_VERSION, + EVTHREAD_LOCKTYPE_RECURSIVE, + trace_lock_alloc, + trace_lock_free, + trace_lock_lock, + trace_lock_unlock, + }; + memcpy(&lu_base.cbs, evthread_get_lock_callbacks(), + sizeof(lu_base.cbs)); + { + lock_unlock_free_thread_cbs(); + + evthread_set_lock_callbacks(&cbs); + /** re-create debug locks correctly */ + evthread_enable_lock_debugging(); + + event_init(); + } + return 0; +} +static void free_lock_unlock_profiler(struct basic_test_data *data) +{ + /** fix "held_by" for kqueue */ + evthread_set_lock_callbacks(NULL); + + lock_unlock_free_thread_cbs(); + free(lu_base.locks); + data->base = NULL; +} + +static void test_bufferevent_pair_release_lock(void *arg) +{ + struct basic_test_data *data = arg; + use_lock_unlock_profiler(); + { + struct bufferevent *pair[2]; + if (!bufferevent_pair_new(NULL, BEV_OPT_THREADSAFE, pair)) { + bufferevent_free(pair[0]); + bufferevent_free(pair[1]); + } else + tt_abort_perror("bufferevent_pair_new"); + } + free_lock_unlock_profiler(data); +end: + ; +} +#endif + +/* + * test watermarks and bufferevent + */ + +static void +wm_readcb(struct bufferevent *bev, void *arg) +{ + struct evbuffer *evbuf = evbuffer_new(); + int len = (int)evbuffer_get_length(bev->input); + static int nread; + + assert(len >= 10 && len <= 20); + + assert(evbuf != NULL); + + /* gratuitous test of bufferevent_read_buffer */ + bufferevent_read_buffer(bev, evbuf); + + nread += len; + if (nread == 65000) { + bufferevent_disable(bev, EV_READ); + test_ok++; + } + + evbuffer_free(evbuf); +} + +static void +wm_writecb(struct bufferevent *bev, void *arg) +{ + assert(evbuffer_get_length(bev->output) <= 100); + if (evbuffer_get_length(bev->output) == 0) { + evbuffer_drain(bev->output, evbuffer_get_length(bev->output)); + test_ok++; + } +} + +static void +wm_errorcb(struct bufferevent *bev, short what, void *arg) +{ + test_ok = -2; +} + +static void +test_bufferevent_watermarks_impl(int use_pair) +{ + struct bufferevent *bev1 = NULL, *bev2 = NULL; + char buffer[65000]; + size_t low, high; + int i; + test_ok = 0; + + if (use_pair) { + struct bufferevent *pair[2]; + tt_assert(0 == bufferevent_pair_new(NULL, 0, pair)); + bev1 = pair[0]; + bev2 = pair[1]; + bufferevent_setcb(bev1, NULL, wm_writecb, errorcb, NULL); + bufferevent_setcb(bev2, wm_readcb, NULL, errorcb, NULL); + } else { + bev1 = bufferevent_new(pair[0], NULL, wm_writecb, wm_errorcb, NULL); + bev2 = bufferevent_new(pair[1], wm_readcb, NULL, wm_errorcb, NULL); + } + tt_assert(bev1); + tt_assert(bev2); + bufferevent_disable(bev1, EV_READ); + bufferevent_enable(bev2, EV_READ); + + /* By default, low watermarks are set to 0 */ + bufferevent_getwatermark(bev1, EV_READ, &low, NULL); + tt_int_op(low, ==, 0); + bufferevent_getwatermark(bev2, EV_WRITE, &low, NULL); + tt_int_op(low, ==, 0); + + for (i = 0; i < (int)sizeof(buffer); i++) + buffer[i] = (char)i; + + /* limit the reading on the receiving bufferevent */ + bufferevent_setwatermark(bev2, EV_READ, 10, 20); + + bufferevent_getwatermark(bev2, EV_READ, &low, &high); + tt_int_op(low, ==, 10); + tt_int_op(high, ==, 20); + + /* Tell the sending bufferevent not to notify us till it's down to + 100 bytes. */ + bufferevent_setwatermark(bev1, EV_WRITE, 100, 2000); + + bufferevent_getwatermark(bev1, EV_WRITE, &low, &high); + tt_int_op(low, ==, 100); + tt_int_op(high, ==, 2000); + + { + int r = bufferevent_getwatermark(bev1, EV_WRITE | EV_READ, &low, &high); + tt_int_op(r, !=, 0); + } + + bufferevent_write(bev1, buffer, sizeof(buffer)); + + event_dispatch(); + + tt_int_op(test_ok, ==, 2); + + /* The write callback drained all the data from outbuf, so we + * should have removed the write event... */ + tt_assert(!event_pending(&bev2->ev_write, EV_WRITE, NULL)); + +end: + if (bev1) + bufferevent_free(bev1); + if (bev2) + bufferevent_free(bev2); +} + +static void +test_bufferevent_watermarks(void) +{ + test_bufferevent_watermarks_impl(0); +} + +static void +test_bufferevent_pair_watermarks(void) +{ + test_bufferevent_watermarks_impl(1); +} + +/* + * Test bufferevent filters + */ + +/* strip an 'x' from each byte */ + +static enum bufferevent_filter_result +bufferevent_input_filter(struct evbuffer *src, struct evbuffer *dst, + ev_ssize_t lim, enum bufferevent_flush_mode state, void *ctx) +{ + const unsigned char *buffer; + unsigned i; + + buffer = evbuffer_pullup(src, evbuffer_get_length(src)); + for (i = 0; i < evbuffer_get_length(src); i += 2) { + assert(buffer[i] == 'x'); + evbuffer_add(dst, buffer + i + 1, 1); + + if (i + 2 > evbuffer_get_length(src)) + break; + } + + evbuffer_drain(src, i); + return (BEV_OK); +} + +/* add an 'x' before each byte */ + +static enum bufferevent_filter_result +bufferevent_output_filter(struct evbuffer *src, struct evbuffer *dst, + ev_ssize_t lim, enum bufferevent_flush_mode state, void *ctx) +{ + const unsigned char *buffer; + unsigned i; + + buffer = evbuffer_pullup(src, evbuffer_get_length(src)); + for (i = 0; i < evbuffer_get_length(src); ++i) { + evbuffer_add(dst, "x", 1); + evbuffer_add(dst, buffer + i, 1); + } + + evbuffer_drain(src, evbuffer_get_length(src)); + return (BEV_OK); +} + +static void +test_bufferevent_filters_impl(int use_pair) +{ + struct bufferevent *bev1 = NULL, *bev2 = NULL; + struct bufferevent *bev1_base = NULL, *bev2_base = NULL; + char buffer[8333]; + int i; + + test_ok = 0; + + if (use_pair) { + struct bufferevent *pair[2]; + tt_assert(0 == bufferevent_pair_new(NULL, 0, pair)); + bev1 = pair[0]; + bev2 = pair[1]; + } else { + bev1 = bufferevent_socket_new(NULL, pair[0], 0); + bev2 = bufferevent_socket_new(NULL, pair[1], 0); + } + bev1_base = bev1; + bev2_base = bev2; + + for (i = 0; i < (int)sizeof(buffer); i++) + buffer[i] = i; + + bev1 = bufferevent_filter_new(bev1, NULL, bufferevent_output_filter, + BEV_OPT_CLOSE_ON_FREE, NULL, NULL); + + bev2 = bufferevent_filter_new(bev2, bufferevent_input_filter, + NULL, BEV_OPT_CLOSE_ON_FREE, NULL, NULL); + bufferevent_setcb(bev1, NULL, writecb, errorcb, NULL); + bufferevent_setcb(bev2, readcb, NULL, errorcb, NULL); + + tt_ptr_op(bufferevent_get_underlying(bev1), ==, bev1_base); + tt_ptr_op(bufferevent_get_underlying(bev2), ==, bev2_base); + tt_int_op(bufferevent_getfd(bev1), ==, -1); + tt_int_op(bufferevent_getfd(bev2), ==, -1); + + bufferevent_disable(bev1, EV_READ); + bufferevent_enable(bev2, EV_READ); + /* insert some filters */ + bufferevent_write(bev1, buffer, sizeof(buffer)); + + event_dispatch(); + + if (test_ok != 2) + test_ok = 0; + +end: + if (bev1) + bufferevent_free(bev1); + if (bev2) + bufferevent_free(bev2); + +} + +static void +test_bufferevent_filters(void) +{ + test_bufferevent_filters_impl(0); +} + +static void +test_bufferevent_pair_filters(void) +{ + test_bufferevent_filters_impl(1); +} + + +static void +sender_writecb(struct bufferevent *bev, void *ctx) +{ + if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) { + bufferevent_disable(bev,EV_READ|EV_WRITE); + TT_BLATHER(("Flushed %d: freeing it.", (int)bufferevent_getfd(bev))); + bufferevent_free(bev); + } +} + +static void +sender_errorcb(struct bufferevent *bev, short what, void *ctx) +{ + TT_FAIL(("Got sender error %d",(int)what)); +} + +static int bufferevent_connect_test_flags = 0; +static int bufferevent_trigger_test_flags = 0; +static int n_strings_read = 0; +static int n_reads_invoked = 0; +static int n_events_invoked = 0; + +#define TEST_STR "Now is the time for all good events to signal for " \ + "the good of their protocol" +static void +listen_cb(struct evconnlistener *listener, evutil_socket_t fd, + struct sockaddr *sa, int socklen, void *arg) +{ + struct event_base *base = arg; + struct bufferevent *bev; + const char s[] = TEST_STR; + TT_BLATHER(("Got a request on socket %d", (int)fd )); + bev = bufferevent_socket_new(base, fd, bufferevent_connect_test_flags); + tt_assert(bev); + bufferevent_setcb(bev, NULL, sender_writecb, sender_errorcb, NULL); + bufferevent_write(bev, s, sizeof(s)); +end: + ; +} + +static int +fake_listener_create(struct sockaddr_in *localhost) +{ + struct sockaddr *sa = (struct sockaddr *)localhost; + evutil_socket_t fd = -1; + ev_socklen_t slen = sizeof(*localhost); + + memset(localhost, 0, sizeof(*localhost)); + localhost->sin_port = 0; /* have the kernel pick a port */ + localhost->sin_addr.s_addr = htonl(0x7f000001L); + localhost->sin_family = AF_INET; + + /* bind, but don't listen or accept. should trigger + "Connection refused" reliably on most platforms. */ + fd = socket(localhost->sin_family, SOCK_STREAM, 0); + tt_assert(fd >= 0); + tt_assert(bind(fd, sa, slen) == 0); + tt_assert(getsockname(fd, sa, &slen) == 0); + + return fd; + +end: + return -1; +} + +static void +reader_eventcb(struct bufferevent *bev, short what, void *ctx) +{ + struct event_base *base = ctx; + if (what & BEV_EVENT_ERROR) { + perror("foobar"); + TT_FAIL(("got connector error %d", (int)what)); + return; + } + if (what & BEV_EVENT_CONNECTED) { + TT_BLATHER(("connected on %d", (int)bufferevent_getfd(bev))); + bufferevent_enable(bev, EV_READ); + } + if (what & BEV_EVENT_EOF) { + char buf[512]; + size_t n; + n = bufferevent_read(bev, buf, sizeof(buf)-1); + tt_int_op(n, >=, 0); + buf[n] = '\0'; + tt_str_op(buf, ==, TEST_STR); + if (++n_strings_read == 2) + event_base_loopexit(base, NULL); + TT_BLATHER(("EOF on %d: %d strings read.", + (int)bufferevent_getfd(bev), n_strings_read)); + } +end: + ; +} + +static void +reader_eventcb_simple(struct bufferevent *bev, short what, void *ctx) +{ + TT_BLATHER(("Read eventcb simple invoked on %d.", + (int)bufferevent_getfd(bev))); + n_events_invoked++; +} + +static void +reader_readcb(struct bufferevent *bev, void *ctx) +{ + TT_BLATHER(("Read invoked on %d.", (int)bufferevent_getfd(bev))); + n_reads_invoked++; +} + +static void +test_bufferevent_connect(void *arg) +{ + struct basic_test_data *data = arg; + struct evconnlistener *lev=NULL; + struct bufferevent *bev1=NULL, *bev2=NULL; + struct sockaddr_in localhost; + struct sockaddr_storage ss; + struct sockaddr *sa; + ev_socklen_t slen; + + int be_flags=BEV_OPT_CLOSE_ON_FREE; + + if (strstr((char*)data->setup_data, "defer")) { + be_flags |= BEV_OPT_DEFER_CALLBACKS; + } + if (strstr((char*)data->setup_data, "unlocked")) { + be_flags |= BEV_OPT_UNLOCK_CALLBACKS; + } + if (strstr((char*)data->setup_data, "lock")) { + be_flags |= BEV_OPT_THREADSAFE; + } + bufferevent_connect_test_flags = be_flags; +#ifdef _WIN32 + if (!strcmp((char*)data->setup_data, "unset_connectex")) { + struct win32_extension_fns *ext = + (struct win32_extension_fns *) + event_get_win32_extension_fns_(); + ext->ConnectEx = NULL; + } +#endif + + memset(&localhost, 0, sizeof(localhost)); + + localhost.sin_port = 0; /* pick-a-port */ + localhost.sin_addr.s_addr = htonl(0x7f000001L); + localhost.sin_family = AF_INET; + sa = (struct sockaddr *)&localhost; + lev = evconnlistener_new_bind(data->base, listen_cb, data->base, + LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, + 16, sa, sizeof(localhost)); + tt_assert(lev); + + sa = (struct sockaddr *)&ss; + slen = sizeof(ss); + if (regress_get_listener_addr(lev, sa, &slen) < 0) { + tt_abort_perror("getsockname"); + } + + tt_assert(!evconnlistener_enable(lev)); + bev1 = bufferevent_socket_new(data->base, -1, be_flags); + bev2 = bufferevent_socket_new(data->base, -1, be_flags); + tt_assert(bev1); + tt_assert(bev2); + bufferevent_setcb(bev1, reader_readcb,NULL, reader_eventcb, data->base); + bufferevent_setcb(bev2, reader_readcb,NULL, reader_eventcb, data->base); + + bufferevent_enable(bev1, EV_READ); + bufferevent_enable(bev2, EV_READ); + + tt_want(!bufferevent_socket_connect(bev1, sa, sizeof(localhost))); + tt_want(!bufferevent_socket_connect(bev2, sa, sizeof(localhost))); + + event_base_dispatch(data->base); + + tt_int_op(n_strings_read, ==, 2); + tt_int_op(n_reads_invoked, >=, 2); +end: + if (lev) + evconnlistener_free(lev); + + if (bev1) + bufferevent_free(bev1); + + if (bev2) + bufferevent_free(bev2); +} + +static void +test_bufferevent_connect_fail_eventcb(void *arg) +{ + struct basic_test_data *data = arg; + int flags = BEV_OPT_CLOSE_ON_FREE | (long)data->setup_data; + struct bufferevent *bev = NULL; + struct evconnlistener *lev = NULL; + struct sockaddr_in localhost; + ev_socklen_t slen = sizeof(localhost); + evutil_socket_t fake_listener = -1; + + fake_listener = fake_listener_create(&localhost); + + tt_int_op(n_events_invoked, ==, 0); + + bev = bufferevent_socket_new(data->base, -1, flags); + tt_assert(bev); + bufferevent_setcb(bev, reader_readcb, reader_readcb, + reader_eventcb_simple, data->base); + bufferevent_enable(bev, EV_READ|EV_WRITE); + tt_int_op(n_events_invoked, ==, 0); + tt_int_op(n_reads_invoked, ==, 0); + /** @see also test_bufferevent_connect_fail() */ + bufferevent_socket_connect(bev, (struct sockaddr *)&localhost, slen); + tt_int_op(n_events_invoked, ==, 0); + tt_int_op(n_reads_invoked, ==, 0); + event_base_dispatch(data->base); + tt_int_op(n_events_invoked, ==, 1); + tt_int_op(n_reads_invoked, ==, 0); + +end: + if (lev) + evconnlistener_free(lev); + if (bev) + bufferevent_free(bev); + if (fake_listener >= 0) + evutil_closesocket(fake_listener); +} + +static void +want_fail_eventcb(struct bufferevent *bev, short what, void *ctx) +{ + struct event_base *base = ctx; + const char *err; + evutil_socket_t s; + + if (what & BEV_EVENT_ERROR) { + s = bufferevent_getfd(bev); + err = evutil_socket_error_to_string(evutil_socket_geterror(s)); + TT_BLATHER(("connection failure on "EV_SOCK_FMT": %s", + EV_SOCK_ARG(s), err)); + test_ok = 1; + } else { + TT_FAIL(("didn't fail? what %hd", what)); + } + + event_base_loopexit(base, NULL); +} + +static void +close_socket_cb(evutil_socket_t fd, short what, void *arg) +{ + evutil_socket_t *fdp = arg; + if (*fdp >= 0) { + evutil_closesocket(*fdp); + *fdp = -1; + } +} + +static void +test_bufferevent_connect_fail(void *arg) +{ + struct basic_test_data *data = (struct basic_test_data *)arg; + struct bufferevent *bev=NULL; + struct event close_listener_event; + int close_listener_event_added = 0; + struct timeval one_second = { 1, 0 }; + struct sockaddr_in localhost; + ev_socklen_t slen = sizeof(localhost); + evutil_socket_t fake_listener = -1; + int r; + + test_ok = 0; + + fake_listener = fake_listener_create(&localhost); + bev = bufferevent_socket_new(data->base, -1, + BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); + tt_assert(bev); + bufferevent_setcb(bev, NULL, NULL, want_fail_eventcb, data->base); + + r = bufferevent_socket_connect(bev, (struct sockaddr *)&localhost, slen); + /* XXXX we'd like to test the '0' case everywhere, but FreeBSD tells + * detects the error immediately, which is not really wrong of it. */ + tt_want(r == 0 || r == -1); + + /* Close the listener socket after a second. This should trigger + "connection refused" on some other platforms, including OSX. */ + evtimer_assign(&close_listener_event, data->base, close_socket_cb, + &fake_listener); + event_add(&close_listener_event, &one_second); + close_listener_event_added = 1; + + event_base_dispatch(data->base); + + tt_int_op(test_ok, ==, 1); + +end: + if (fake_listener >= 0) + evutil_closesocket(fake_listener); + + if (bev) + bufferevent_free(bev); + + if (close_listener_event_added) + event_del(&close_listener_event); +} + +struct timeout_cb_result { + struct timeval read_timeout_at; + struct timeval write_timeout_at; + struct timeval last_wrote_at; + int n_read_timeouts; + int n_write_timeouts; + int total_calls; +}; + +static void +bev_timeout_write_cb(struct bufferevent *bev, void *arg) +{ + struct timeout_cb_result *res = arg; + evutil_gettimeofday(&res->last_wrote_at, NULL); +} + +static void +bev_timeout_event_cb(struct bufferevent *bev, short what, void *arg) +{ + struct timeout_cb_result *res = arg; + ++res->total_calls; + + if ((what & (BEV_EVENT_READING|BEV_EVENT_TIMEOUT)) + == (BEV_EVENT_READING|BEV_EVENT_TIMEOUT)) { + evutil_gettimeofday(&res->read_timeout_at, NULL); + ++res->n_read_timeouts; + } + if ((what & (BEV_EVENT_WRITING|BEV_EVENT_TIMEOUT)) + == (BEV_EVENT_WRITING|BEV_EVENT_TIMEOUT)) { + evutil_gettimeofday(&res->write_timeout_at, NULL); + ++res->n_write_timeouts; + } +} + +static void +test_bufferevent_timeouts(void *arg) +{ + /* "arg" is a string containing "pair" and/or "filter". */ + struct bufferevent *bev1 = NULL, *bev2 = NULL; + struct basic_test_data *data = arg; + int use_pair = 0, use_filter = 0; + struct timeval tv_w, tv_r, started_at; + struct timeout_cb_result res1, res2; + char buf[1024]; + + memset(&res1, 0, sizeof(res1)); + memset(&res2, 0, sizeof(res2)); + + if (strstr((char*)data->setup_data, "pair")) + use_pair = 1; + if (strstr((char*)data->setup_data, "filter")) + use_filter = 1; + + if (use_pair) { + struct bufferevent *p[2]; + tt_int_op(0, ==, bufferevent_pair_new(data->base, 0, p)); + bev1 = p[0]; + bev2 = p[1]; + } else { + bev1 = bufferevent_socket_new(data->base, data->pair[0], 0); + bev2 = bufferevent_socket_new(data->base, data->pair[1], 0); + } + + tt_assert(bev1); + tt_assert(bev2); + + if (use_filter) { + struct bufferevent *bevf1, *bevf2; + bevf1 = bufferevent_filter_new(bev1, NULL, NULL, + BEV_OPT_CLOSE_ON_FREE, NULL, NULL); + bevf2 = bufferevent_filter_new(bev2, NULL, NULL, + BEV_OPT_CLOSE_ON_FREE, NULL, NULL); + tt_assert(bevf1); + tt_assert(bevf2); + bev1 = bevf1; + bev2 = bevf2; + } + + /* Do this nice and early. */ + bufferevent_disable(bev2, EV_READ); + + /* bev1 will try to write and read. Both will time out. */ + evutil_gettimeofday(&started_at, NULL); + tv_w.tv_sec = tv_r.tv_sec = 0; + tv_w.tv_usec = 100*1000; + tv_r.tv_usec = 150*1000; + bufferevent_setcb(bev1, NULL, bev_timeout_write_cb, + bev_timeout_event_cb, &res1); + bufferevent_setwatermark(bev1, EV_WRITE, 1024*1024+10, 0); + bufferevent_set_timeouts(bev1, &tv_r, &tv_w); + if (use_pair) { + /* For a pair, the fact that the other side isn't reading + * makes the writer stall */ + bufferevent_write(bev1, "ABCDEFG", 7); + } else { + /* For a real socket, the kernel's TCP buffers can eat a + * fair number of bytes; make sure that at some point we + * have some bytes that will stall. */ + struct evbuffer *output = bufferevent_get_output(bev1); + int i; + memset(buf, 0xbb, sizeof(buf)); + for (i=0;i<1024;++i) { + evbuffer_add_reference(output, buf, sizeof(buf), + NULL, NULL); + } + } + bufferevent_enable(bev1, EV_READ|EV_WRITE); + + /* bev2 has nothing to say, and isn't listening. */ + bufferevent_setcb(bev2, NULL, bev_timeout_write_cb, + bev_timeout_event_cb, &res2); + tv_w.tv_sec = tv_r.tv_sec = 0; + tv_w.tv_usec = 200*1000; + tv_r.tv_usec = 100*1000; + bufferevent_set_timeouts(bev2, &tv_r, &tv_w); + bufferevent_enable(bev2, EV_WRITE); + + tv_r.tv_sec = 0; + tv_r.tv_usec = 350000; + + event_base_loopexit(data->base, &tv_r); + event_base_dispatch(data->base); + + /* XXXX Test that actually reading or writing a little resets the + * timeouts. */ + + /* Each buf1 timeout happens, and happens only once. */ + tt_want(res1.n_read_timeouts); + tt_want(res1.n_write_timeouts); + tt_want(res1.n_read_timeouts == 1); + tt_want(res1.n_write_timeouts == 1); + + test_timeval_diff_eq(&started_at, &res1.read_timeout_at, 150); + test_timeval_diff_eq(&started_at, &res1.write_timeout_at, 100); + +end: + if (bev1) + bufferevent_free(bev1); + if (bev2) + bufferevent_free(bev2); +} + +static void +trigger_failure_cb(evutil_socket_t fd, short what, void *ctx) +{ + TT_FAIL(("The triggered callback did not fire or the machine is really slow (try increasing timeout).")); +} + +static void +trigger_eventcb(struct bufferevent *bev, short what, void *ctx) +{ + struct event_base *base = ctx; + if (what == ~0) { + TT_BLATHER(("Event successfully triggered.")); + event_base_loopexit(base, NULL); + return; + } + reader_eventcb(bev, what, ctx); +} + +static void +trigger_readcb_triggered(struct bufferevent *bev, void *ctx) +{ + TT_BLATHER(("Read successfully triggered.")); + n_reads_invoked++; + bufferevent_trigger_event(bev, ~0, bufferevent_trigger_test_flags); +} + +static void +trigger_readcb(struct bufferevent *bev, void *ctx) +{ + struct timeval timeout = { 30, 0 }; + struct event_base *base = ctx; + size_t low, high, len; + int expected_reads; + + TT_BLATHER(("Read invoked on %d.", (int)bufferevent_getfd(bev))); + expected_reads = ++n_reads_invoked; + + bufferevent_setcb(bev, trigger_readcb_triggered, NULL, trigger_eventcb, ctx); + + bufferevent_getwatermark(bev, EV_READ, &low, &high); + len = evbuffer_get_length(bufferevent_get_input(bev)); + + bufferevent_setwatermark(bev, EV_READ, len + 1, 0); + bufferevent_trigger(bev, EV_READ, bufferevent_trigger_test_flags); + /* no callback expected */ + tt_int_op(n_reads_invoked, ==, expected_reads); + + if ((bufferevent_trigger_test_flags & BEV_TRIG_DEFER_CALLBACKS) || + (bufferevent_connect_test_flags & BEV_OPT_DEFER_CALLBACKS)) { + /* will be deferred */ + } else { + expected_reads++; + } + + event_base_once(base, -1, EV_TIMEOUT, trigger_failure_cb, NULL, &timeout); + + bufferevent_trigger(bev, EV_READ, + bufferevent_trigger_test_flags | BEV_TRIG_IGNORE_WATERMARKS); + tt_int_op(n_reads_invoked, ==, expected_reads); + + bufferevent_setwatermark(bev, EV_READ, low, high); +end: + ; +} + +static void +test_bufferevent_trigger(void *arg) +{ + struct basic_test_data *data = arg; + struct evconnlistener *lev=NULL; + struct bufferevent *bev=NULL; + struct sockaddr_in localhost; + struct sockaddr_storage ss; + struct sockaddr *sa; + ev_socklen_t slen; + + int be_flags=BEV_OPT_CLOSE_ON_FREE; + int trig_flags=0; + + if (strstr((char*)data->setup_data, "defer")) { + be_flags |= BEV_OPT_DEFER_CALLBACKS; + } + bufferevent_connect_test_flags = be_flags; + + if (strstr((char*)data->setup_data, "postpone")) { + trig_flags |= BEV_TRIG_DEFER_CALLBACKS; + } + bufferevent_trigger_test_flags = trig_flags; + + memset(&localhost, 0, sizeof(localhost)); + + localhost.sin_port = 0; /* pick-a-port */ + localhost.sin_addr.s_addr = htonl(0x7f000001L); + localhost.sin_family = AF_INET; + sa = (struct sockaddr *)&localhost; + lev = evconnlistener_new_bind(data->base, listen_cb, data->base, + LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, + 16, sa, sizeof(localhost)); + tt_assert(lev); + + sa = (struct sockaddr *)&ss; + slen = sizeof(ss); + if (regress_get_listener_addr(lev, sa, &slen) < 0) { + tt_abort_perror("getsockname"); + } + + tt_assert(!evconnlistener_enable(lev)); + bev = bufferevent_socket_new(data->base, -1, be_flags); + tt_assert(bev); + bufferevent_setcb(bev, trigger_readcb, NULL, trigger_eventcb, data->base); + + bufferevent_enable(bev, EV_READ); + + tt_want(!bufferevent_socket_connect(bev, sa, sizeof(localhost))); + + event_base_dispatch(data->base); + + tt_int_op(n_reads_invoked, ==, 2); +end: + if (lev) + evconnlistener_free(lev); + + if (bev) + bufferevent_free(bev); +} + +static void +test_bufferevent_socket_filter_inactive(void *arg) +{ + struct basic_test_data *data = arg; + struct bufferevent *bev = NULL, *bevf = NULL; + + bev = bufferevent_socket_new(data->base, -1, 0); + tt_assert(bev); + bevf = bufferevent_filter_new(bev, NULL, NULL, 0, NULL, NULL); + tt_assert(bevf); + +end: + if (bevf) + bufferevent_free(bevf); + if (bev) + bufferevent_free(bev); +} + + +struct testcase_t bufferevent_testcases[] = { + + LEGACY(bufferevent, TT_ISOLATED), + LEGACY(bufferevent_pair, TT_ISOLATED), + LEGACY(bufferevent_flush_normal, TT_ISOLATED), + LEGACY(bufferevent_flush_flush, TT_ISOLATED), + LEGACY(bufferevent_flush_finished, TT_ISOLATED), + LEGACY(bufferevent_pair_flush_normal, TT_ISOLATED), + LEGACY(bufferevent_pair_flush_flush, TT_ISOLATED), + LEGACY(bufferevent_pair_flush_finished, TT_ISOLATED), +#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) + { "bufferevent_pair_release_lock", test_bufferevent_pair_release_lock, + TT_FORK|TT_ISOLATED|TT_NEED_THREADS|TT_NEED_BASE|TT_LEGACY, + &basic_setup, NULL }, +#endif + LEGACY(bufferevent_watermarks, TT_ISOLATED), + LEGACY(bufferevent_pair_watermarks, TT_ISOLATED), + LEGACY(bufferevent_filters, TT_ISOLATED), + LEGACY(bufferevent_pair_filters, TT_ISOLATED), + { "bufferevent_connect", test_bufferevent_connect, TT_FORK|TT_NEED_BASE, + &basic_setup, (void*)"" }, + { "bufferevent_connect_defer", test_bufferevent_connect, + TT_FORK|TT_NEED_BASE, &basic_setup, (void*)"defer" }, + { "bufferevent_connect_lock", test_bufferevent_connect, + TT_FORK|TT_NEED_BASE|TT_NEED_THREADS, &basic_setup, (void*)"lock" }, + { "bufferevent_connect_lock_defer", test_bufferevent_connect, + TT_FORK|TT_NEED_BASE|TT_NEED_THREADS, &basic_setup, + (void*)"defer lock" }, + { "bufferevent_connect_unlocked_cbs", test_bufferevent_connect, + TT_FORK|TT_NEED_BASE|TT_NEED_THREADS, &basic_setup, + (void*)"lock defer unlocked" }, + { "bufferevent_connect_fail", test_bufferevent_connect_fail, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + { "bufferevent_timeout", test_bufferevent_timeouts, + TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR, &basic_setup, (void*)"" }, + { "bufferevent_timeout_pair", test_bufferevent_timeouts, + TT_FORK|TT_NEED_BASE, &basic_setup, (void*)"pair" }, + { "bufferevent_timeout_filter", test_bufferevent_timeouts, + TT_FORK|TT_NEED_BASE, &basic_setup, (void*)"filter" }, + { "bufferevent_timeout_filter_pair", test_bufferevent_timeouts, + TT_FORK|TT_NEED_BASE, &basic_setup, (void*)"filter pair" }, + { "bufferevent_trigger", test_bufferevent_trigger, TT_FORK|TT_NEED_BASE, + &basic_setup, (void*)"" }, + { "bufferevent_trigger_defer", test_bufferevent_trigger, + TT_FORK|TT_NEED_BASE, &basic_setup, (void*)"defer" }, + { "bufferevent_trigger_postpone", test_bufferevent_trigger, + TT_FORK|TT_NEED_BASE|TT_NEED_THREADS, &basic_setup, + (void*)"postpone" }, + { "bufferevent_trigger_defer_postpone", test_bufferevent_trigger, + TT_FORK|TT_NEED_BASE|TT_NEED_THREADS, &basic_setup, + (void*)"defer postpone" }, +#ifdef EVENT__HAVE_LIBZ + LEGACY(bufferevent_zlib, TT_ISOLATED), +#else + { "bufferevent_zlib", NULL, TT_SKIP, NULL, NULL }, +#endif + + { "bufferevent_connect_fail_eventcb_defer", + test_bufferevent_connect_fail_eventcb, + TT_FORK|TT_NEED_BASE, &basic_setup, (void*)BEV_OPT_DEFER_CALLBACKS }, + { "bufferevent_connect_fail_eventcb", + test_bufferevent_connect_fail_eventcb, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + + { "bufferevent_socket_filter_inactive", + test_bufferevent_socket_filter_inactive, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + + END_OF_TESTCASES, +}; + +struct testcase_t bufferevent_iocp_testcases[] = { + + LEGACY(bufferevent, TT_ISOLATED|TT_ENABLE_IOCP), + LEGACY(bufferevent_flush_normal, TT_ISOLATED), + LEGACY(bufferevent_flush_flush, TT_ISOLATED), + LEGACY(bufferevent_flush_finished, TT_ISOLATED), + LEGACY(bufferevent_watermarks, TT_ISOLATED|TT_ENABLE_IOCP), + LEGACY(bufferevent_filters, TT_ISOLATED|TT_ENABLE_IOCP), + { "bufferevent_connect", test_bufferevent_connect, + TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, (void*)"" }, + { "bufferevent_connect_defer", test_bufferevent_connect, + TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, (void*)"defer" }, + { "bufferevent_connect_lock", test_bufferevent_connect, + TT_FORK|TT_NEED_BASE|TT_NEED_THREADS|TT_ENABLE_IOCP, &basic_setup, + (void*)"lock" }, + { "bufferevent_connect_lock_defer", test_bufferevent_connect, + TT_FORK|TT_NEED_BASE|TT_NEED_THREADS|TT_ENABLE_IOCP, &basic_setup, + (void*)"defer lock" }, + { "bufferevent_connect_fail", test_bufferevent_connect_fail, + TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, NULL }, + { "bufferevent_connect_nonblocking", test_bufferevent_connect, + TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, + (void*)"unset_connectex" }, + + { "bufferevent_connect_fail_eventcb_defer", + test_bufferevent_connect_fail_eventcb, + TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, + (void*)BEV_OPT_DEFER_CALLBACKS }, + { "bufferevent_connect_fail", + test_bufferevent_connect_fail_eventcb, + TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, NULL }, + + END_OF_TESTCASES, +}; diff --git a/libs/libevent/docs/test/regress_dns.c b/libs/libevent/docs/test/regress_dns.c new file mode 100644 index 0000000000..1873636245 --- /dev/null +++ b/libs/libevent/docs/test/regress_dns.c @@ -0,0 +1,2151 @@ +/* + * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "../util-internal.h" + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#include <ws2tcpip.h> +#endif + +#include "event2/event-config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/queue.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <signal.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#endif +#ifdef EVENT__HAVE_NETINET_IN6_H +#include <netinet/in6.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "event2/dns.h" +#include "event2/dns_compat.h" +#include "event2/dns_struct.h" +#include "event2/event.h" +#include "event2/event_compat.h" +#include "event2/event_struct.h" +#include "event2/util.h" +#include "event2/listener.h" +#include "event2/bufferevent.h" +#include "log-internal.h" +#include "regress.h" +#include "regress_testutils.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +static int dns_ok = 0; +static int dns_got_cancel = 0; +static int dns_err = 0; + + +static void +dns_gethostbyname_cb(int result, char type, int count, int ttl, + void *addresses, void *arg) +{ + dns_ok = dns_err = 0; + + if (result == DNS_ERR_TIMEOUT) { + printf("[Timed out] "); + dns_err = result; + goto out; + } + + if (result != DNS_ERR_NONE) { + printf("[Error code %d] ", result); + goto out; + } + + TT_BLATHER(("type: %d, count: %d, ttl: %d: ", type, count, ttl)); + + switch (type) { + case DNS_IPv6_AAAA: { +#if defined(EVENT__HAVE_STRUCT_IN6_ADDR) && defined(EVENT__HAVE_INET_NTOP) && defined(INET6_ADDRSTRLEN) + struct in6_addr *in6_addrs = addresses; + char buf[INET6_ADDRSTRLEN+1]; + int i; + /* a resolution that's not valid does not help */ + if (ttl < 0) + goto out; + for (i = 0; i < count; ++i) { + const char *b = evutil_inet_ntop(AF_INET6, &in6_addrs[i], buf,sizeof(buf)); + if (b) + TT_BLATHER(("%s ", b)); + else + TT_BLATHER(("%s ", strerror(errno))); + } +#endif + break; + } + case DNS_IPv4_A: { + struct in_addr *in_addrs = addresses; + int i; + /* a resolution that's not valid does not help */ + if (ttl < 0) + goto out; + for (i = 0; i < count; ++i) + TT_BLATHER(("%s ", inet_ntoa(in_addrs[i]))); + break; + } + case DNS_PTR: + /* may get at most one PTR */ + if (count != 1) + goto out; + + TT_BLATHER(("%s ", *(char **)addresses)); + break; + default: + goto out; + } + + dns_ok = type; + +out: + if (arg == NULL) + event_loopexit(NULL); + else + event_base_loopexit((struct event_base *)arg, NULL); +} + +static void +dns_gethostbyname(void) +{ + dns_ok = 0; + evdns_resolve_ipv4("www.monkey.org", 0, dns_gethostbyname_cb, NULL); + event_dispatch(); + + tt_int_op(dns_ok, ==, DNS_IPv4_A); + test_ok = dns_ok; +end: + ; +} + +static void +dns_gethostbyname6(void) +{ + dns_ok = 0; + evdns_resolve_ipv6("www.ietf.org", 0, dns_gethostbyname_cb, NULL); + event_dispatch(); + + if (!dns_ok && dns_err == DNS_ERR_TIMEOUT) { + tt_skip(); + } + + tt_int_op(dns_ok, ==, DNS_IPv6_AAAA); + test_ok = 1; +end: + ; +} + +static void +dns_gethostbyaddr(void) +{ + struct in_addr in; + in.s_addr = htonl(0x7f000001ul); /* 127.0.0.1 */ + dns_ok = 0; + evdns_resolve_reverse(&in, 0, dns_gethostbyname_cb, NULL); + event_dispatch(); + + tt_int_op(dns_ok, ==, DNS_PTR); + test_ok = dns_ok; +end: + ; +} + +static void +dns_resolve_reverse(void *ptr) +{ + struct in_addr in; + struct event_base *base = event_base_new(); + struct evdns_base *dns = evdns_base_new(base, 1/* init name servers */); + struct evdns_request *req = NULL; + + tt_assert(base); + tt_assert(dns); + in.s_addr = htonl(0x7f000001ul); /* 127.0.0.1 */ + dns_ok = 0; + + req = evdns_base_resolve_reverse( + dns, &in, 0, dns_gethostbyname_cb, base); + tt_assert(req); + + event_base_dispatch(base); + + tt_int_op(dns_ok, ==, DNS_PTR); + +end: + if (dns) + evdns_base_free(dns, 0); + if (base) + event_base_free(base); +} + +static int n_server_responses = 0; + +static void +dns_server_request_cb(struct evdns_server_request *req, void *data) +{ + int i, r; + const char TEST_ARPA[] = "11.11.168.192.in-addr.arpa"; + const char TEST_IN6[] = + "f.e.f.e." "0.0.0.0." "0.0.0.0." "1.1.1.1." + "a.a.a.a." "0.0.0.0." "0.0.0.0." "0.f.f.f.ip6.arpa"; + + for (i = 0; i < req->nquestions; ++i) { + const int qtype = req->questions[i]->type; + const int qclass = req->questions[i]->dns_question_class; + const char *qname = req->questions[i]->name; + + struct in_addr ans; + ans.s_addr = htonl(0xc0a80b0bUL); /* 192.168.11.11 */ + if (qtype == EVDNS_TYPE_A && + qclass == EVDNS_CLASS_INET && + !evutil_ascii_strcasecmp(qname, "zz.example.com")) { + r = evdns_server_request_add_a_reply(req, qname, + 1, &ans.s_addr, 12345); + if (r<0) + dns_ok = 0; + } else if (qtype == EVDNS_TYPE_AAAA && + qclass == EVDNS_CLASS_INET && + !evutil_ascii_strcasecmp(qname, "zz.example.com")) { + char addr6[17] = "abcdefghijklmnop"; + r = evdns_server_request_add_aaaa_reply(req, + qname, 1, addr6, 123); + if (r<0) + dns_ok = 0; + } else if (qtype == EVDNS_TYPE_PTR && + qclass == EVDNS_CLASS_INET && + !evutil_ascii_strcasecmp(qname, TEST_ARPA)) { + r = evdns_server_request_add_ptr_reply(req, NULL, + qname, "ZZ.EXAMPLE.COM", 54321); + if (r<0) + dns_ok = 0; + } else if (qtype == EVDNS_TYPE_PTR && + qclass == EVDNS_CLASS_INET && + !evutil_ascii_strcasecmp(qname, TEST_IN6)){ + r = evdns_server_request_add_ptr_reply(req, NULL, + qname, + "ZZ-INET6.EXAMPLE.COM", 54322); + if (r<0) + dns_ok = 0; + } else if (qtype == EVDNS_TYPE_A && + qclass == EVDNS_CLASS_INET && + !evutil_ascii_strcasecmp(qname, "drop.example.com")) { + if (evdns_server_request_drop(req)<0) + dns_ok = 0; + return; + } else { + printf("Unexpected question %d %d \"%s\" ", + qtype, qclass, qname); + dns_ok = 0; + } + } + r = evdns_server_request_respond(req, 0); + if (r<0) { + printf("Couldn't send reply. "); + dns_ok = 0; + } +} + +static void +dns_server_gethostbyname_cb(int result, char type, int count, int ttl, + void *addresses, void *arg) +{ + if (result == DNS_ERR_CANCEL) { + if (arg != (void*)(char*)90909) { + printf("Unexpected cancelation"); + dns_ok = 0; + } + dns_got_cancel = 1; + goto out; + } + if (result != DNS_ERR_NONE) { + printf("Unexpected result %d. ", result); + dns_ok = 0; + goto out; + } + if (count != 1) { + printf("Unexpected answer count %d. ", count); + dns_ok = 0; + goto out; + } + switch (type) { + case DNS_IPv4_A: { + struct in_addr *in_addrs = addresses; + if (in_addrs[0].s_addr != htonl(0xc0a80b0bUL) || ttl != 12345) { + printf("Bad IPv4 response \"%s\" %d. ", + inet_ntoa(in_addrs[0]), ttl); + dns_ok = 0; + goto out; + } + break; + } + case DNS_IPv6_AAAA: { +#if defined (EVENT__HAVE_STRUCT_IN6_ADDR) && defined(EVENT__HAVE_INET_NTOP) && defined(INET6_ADDRSTRLEN) + struct in6_addr *in6_addrs = addresses; + char buf[INET6_ADDRSTRLEN+1]; + if (memcmp(&in6_addrs[0].s6_addr, "abcdefghijklmnop", 16) + || ttl != 123) { + const char *b = evutil_inet_ntop(AF_INET6, &in6_addrs[0],buf,sizeof(buf)); + printf("Bad IPv6 response \"%s\" %d. ", b, ttl); + dns_ok = 0; + goto out; + } +#endif + break; + } + case DNS_PTR: { + char **addrs = addresses; + if (arg != (void*)6) { + if (strcmp(addrs[0], "ZZ.EXAMPLE.COM") || + ttl != 54321) { + printf("Bad PTR response \"%s\" %d. ", + addrs[0], ttl); + dns_ok = 0; + goto out; + } + } else { + if (strcmp(addrs[0], "ZZ-INET6.EXAMPLE.COM") || + ttl != 54322) { + printf("Bad ipv6 PTR response \"%s\" %d. ", + addrs[0], ttl); + dns_ok = 0; + goto out; + } + } + break; + } + default: + printf("Bad response type %d. ", type); + dns_ok = 0; + } + out: + if (++n_server_responses == 3) { + event_loopexit(NULL); + } +} + +static void +dns_server(void) +{ + evutil_socket_t sock=-1; + struct sockaddr_in my_addr; + struct sockaddr_storage ss; + ev_socklen_t slen; + struct evdns_server_port *port=NULL; + struct in_addr resolve_addr; + struct in6_addr resolve_addr6; + struct evdns_base *base=NULL; + struct evdns_request *req=NULL; + + dns_ok = 1; + + base = evdns_base_new(NULL, 0); + + /* Now configure a nameserver port. */ + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock<0) { + tt_abort_perror("socket"); + } + + evutil_make_socket_nonblocking(sock); + + memset(&my_addr, 0, sizeof(my_addr)); + my_addr.sin_family = AF_INET; + my_addr.sin_port = 0; /* kernel picks */ + my_addr.sin_addr.s_addr = htonl(0x7f000001UL); + if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) < 0) { + tt_abort_perror("bind"); + } + slen = sizeof(ss); + if (getsockname(sock, (struct sockaddr*)&ss, &slen) < 0) { + tt_abort_perror("getsockname"); + } + + port = evdns_add_server_port(sock, 0, dns_server_request_cb, NULL); + + /* Add ourself as the only nameserver, and make sure we really are + * the only nameserver. */ + evdns_base_nameserver_sockaddr_add(base, (struct sockaddr*)&ss, slen, 0); + tt_int_op(evdns_base_count_nameservers(base), ==, 1); + { + struct sockaddr_storage ss2; + int slen2; + + memset(&ss2, 0, sizeof(ss2)); + + slen2 = evdns_base_get_nameserver_addr(base, 0, (struct sockaddr *)&ss2, 3); + tt_int_op(slen2, ==, slen); + tt_int_op(ss2.ss_family, ==, 0); + slen2 = evdns_base_get_nameserver_addr(base, 0, (struct sockaddr *)&ss2, sizeof(ss2)); + tt_int_op(slen2, ==, slen); + tt_mem_op(&ss2, ==, &ss, slen); + + slen2 = evdns_base_get_nameserver_addr(base, 1, (struct sockaddr *)&ss2, sizeof(ss2)); + tt_int_op(-1, ==, slen2); + } + + /* Send some queries. */ + evdns_base_resolve_ipv4(base, "zz.example.com", DNS_QUERY_NO_SEARCH, + dns_server_gethostbyname_cb, NULL); + evdns_base_resolve_ipv6(base, "zz.example.com", DNS_QUERY_NO_SEARCH, + dns_server_gethostbyname_cb, NULL); + resolve_addr.s_addr = htonl(0xc0a80b0bUL); /* 192.168.11.11 */ + evdns_base_resolve_reverse(base, &resolve_addr, 0, + dns_server_gethostbyname_cb, NULL); + memcpy(resolve_addr6.s6_addr, + "\xff\xf0\x00\x00\x00\x00\xaa\xaa" + "\x11\x11\x00\x00\x00\x00\xef\xef", 16); + evdns_base_resolve_reverse_ipv6(base, &resolve_addr6, 0, + dns_server_gethostbyname_cb, (void*)6); + + req = evdns_base_resolve_ipv4(base, + "drop.example.com", DNS_QUERY_NO_SEARCH, + dns_server_gethostbyname_cb, (void*)(char*)90909); + + evdns_cancel_request(base, req); + + event_dispatch(); + + tt_assert(dns_got_cancel); + test_ok = dns_ok; + +end: + if (port) + evdns_close_server_port(port); + if (sock >= 0) + evutil_closesocket(sock); + if (base) + evdns_base_free(base, 0); +} + +static int n_replies_left; +static struct event_base *exit_base; +static struct evdns_server_port *exit_port; + +struct generic_dns_callback_result { + int result; + char type; + int count; + int ttl; + size_t addrs_len; + void *addrs; + char addrs_buf[256]; +}; + +static void +generic_dns_callback(int result, char type, int count, int ttl, void *addresses, + void *arg) +{ + size_t len; + struct generic_dns_callback_result *res = arg; + res->result = result; + res->type = type; + res->count = count; + res->ttl = ttl; + + if (type == DNS_IPv4_A) + len = count * 4; + else if (type == DNS_IPv6_AAAA) + len = count * 16; + else if (type == DNS_PTR) + len = strlen(addresses)+1; + else { + res->addrs_len = len = 0; + res->addrs = NULL; + } + if (len) { + res->addrs_len = len; + if (len > 256) + len = 256; + memcpy(res->addrs_buf, addresses, len); + res->addrs = res->addrs_buf; + } + + --n_replies_left; + if (n_replies_left == 0) { + if (exit_port) { + evdns_close_server_port(exit_port); + exit_port = NULL; + } else + event_base_loopexit(exit_base, NULL); + } +} + +static struct regress_dns_server_table search_table[] = { + { "host.a.example.com", "err", "3", 0, 0 }, + { "host.b.example.com", "err", "3", 0, 0 }, + { "host.c.example.com", "A", "11.22.33.44", 0, 0 }, + { "host2.a.example.com", "err", "3", 0, 0 }, + { "host2.b.example.com", "A", "200.100.0.100", 0, 0 }, + { "host2.c.example.com", "err", "3", 0, 0 }, + { "hostn.a.example.com", "errsoa", "0", 0, 0 }, + { "hostn.b.example.com", "errsoa", "3", 0, 0 }, + { "hostn.c.example.com", "err", "0", 0, 0 }, + + { "host", "err", "3", 0, 0 }, + { "host2", "err", "3", 0, 0 }, + { "*", "err", "3", 0, 0 }, + { NULL, NULL, NULL, 0, 0 } +}; +static void +dns_search_test_impl(void *arg, int lower) +{ + struct regress_dns_server_table table[ARRAY_SIZE(search_table)]; + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evdns_base *dns = NULL; + ev_uint16_t portnum = 0; + char buf[64]; + + struct generic_dns_callback_result r[8]; + size_t i; + + for (i = 0; i < ARRAY_SIZE(table); ++i) { + table[i] = search_table[i]; + table[i].lower = lower; + } + + tt_assert(regress_dnsserver(base, &portnum, table)); + evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum); + + dns = evdns_base_new(base, 0); + tt_assert(!evdns_base_nameserver_ip_add(dns, buf)); + + evdns_base_search_add(dns, "a.example.com"); + evdns_base_search_add(dns, "b.example.com"); + evdns_base_search_add(dns, "c.example.com"); + + n_replies_left = ARRAY_SIZE(r); + exit_base = base; + + evdns_base_resolve_ipv4(dns, "host", 0, generic_dns_callback, &r[0]); + evdns_base_resolve_ipv4(dns, "host2", 0, generic_dns_callback, &r[1]); + evdns_base_resolve_ipv4(dns, "host", DNS_NO_SEARCH, generic_dns_callback, &r[2]); + evdns_base_resolve_ipv4(dns, "host2", DNS_NO_SEARCH, generic_dns_callback, &r[3]); + evdns_base_resolve_ipv4(dns, "host3", 0, generic_dns_callback, &r[4]); + evdns_base_resolve_ipv4(dns, "hostn.a.example.com", DNS_NO_SEARCH, generic_dns_callback, &r[5]); + evdns_base_resolve_ipv4(dns, "hostn.b.example.com", DNS_NO_SEARCH, generic_dns_callback, &r[6]); + evdns_base_resolve_ipv4(dns, "hostn.c.example.com", DNS_NO_SEARCH, generic_dns_callback, &r[7]); + + event_base_dispatch(base); + + tt_int_op(r[0].type, ==, DNS_IPv4_A); + tt_int_op(r[0].count, ==, 1); + tt_int_op(((ev_uint32_t*)r[0].addrs)[0], ==, htonl(0x0b16212c)); + tt_int_op(r[1].type, ==, DNS_IPv4_A); + tt_int_op(r[1].count, ==, 1); + tt_int_op(((ev_uint32_t*)r[1].addrs)[0], ==, htonl(0xc8640064)); + tt_int_op(r[2].result, ==, DNS_ERR_NOTEXIST); + tt_int_op(r[3].result, ==, DNS_ERR_NOTEXIST); + tt_int_op(r[4].result, ==, DNS_ERR_NOTEXIST); + tt_int_op(r[5].result, ==, DNS_ERR_NODATA); + tt_int_op(r[5].ttl, ==, 42); + tt_int_op(r[6].result, ==, DNS_ERR_NOTEXIST); + tt_int_op(r[6].ttl, ==, 42); + tt_int_op(r[7].result, ==, DNS_ERR_NODATA); + tt_int_op(r[7].ttl, ==, 0); + +end: + if (dns) + evdns_base_free(dns, 0); + + regress_clean_dnsserver(); +} +static void +dns_search_test(void *arg) +{ + return dns_search_test_impl(arg, 0); +} +static void +dns_search_lower_test(void *arg) +{ + return dns_search_test_impl(arg, 1); +} + +static int request_count = 0; +static struct evdns_request *current_req = NULL; + +static void +search_cancel_server_cb(struct evdns_server_request *req, void *data) +{ + const char *question; + + if (req->nquestions != 1) + TT_DIE(("Only handling one question at a time; got %d", + req->nquestions)); + + question = req->questions[0]->name; + + TT_BLATHER(("got question, %s", question)); + + tt_assert(request_count > 0); + tt_assert(!evdns_server_request_respond(req, 3)); + + if (!--request_count) + evdns_cancel_request(NULL, current_req); + +end: + ; +} + +static void +dns_search_cancel_test(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evdns_base *dns = NULL; + struct evdns_server_port *port = NULL; + ev_uint16_t portnum = 0; + struct generic_dns_callback_result r1; + char buf[64]; + + port = regress_get_dnsserver(base, &portnum, NULL, + search_cancel_server_cb, NULL); + tt_assert(port); + evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum); + + dns = evdns_base_new(base, 0); + tt_assert(!evdns_base_nameserver_ip_add(dns, buf)); + + evdns_base_search_add(dns, "a.example.com"); + evdns_base_search_add(dns, "b.example.com"); + evdns_base_search_add(dns, "c.example.com"); + evdns_base_search_add(dns, "d.example.com"); + + exit_base = base; + request_count = 3; + n_replies_left = 1; + + current_req = evdns_base_resolve_ipv4(dns, "host", 0, + generic_dns_callback, &r1); + event_base_dispatch(base); + + tt_int_op(r1.result, ==, DNS_ERR_CANCEL); + +end: + if (port) + evdns_close_server_port(port); + if (dns) + evdns_base_free(dns, 0); +} + +static void +fail_server_cb(struct evdns_server_request *req, void *data) +{ + const char *question; + int *count = data; + struct in_addr in; + + /* Drop the first N requests that we get. */ + if (*count > 0) { + --*count; + tt_want(! evdns_server_request_drop(req)); + return; + } + + if (req->nquestions != 1) + TT_DIE(("Only handling one question at a time; got %d", + req->nquestions)); + + question = req->questions[0]->name; + + if (!evutil_ascii_strcasecmp(question, "google.com")) { + /* Detect a probe, and get out of the loop. */ + event_base_loopexit(exit_base, NULL); + } + + tt_assert(evutil_inet_pton(AF_INET, "16.32.64.128", &in)); + evdns_server_request_add_a_reply(req, question, 1, &in.s_addr, + 100); + tt_assert(! evdns_server_request_respond(req, 0)) + return; +end: + tt_want(! evdns_server_request_drop(req)); +} + +static void +dns_retry_test_impl(void *arg, int flags) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evdns_server_port *port = NULL; + struct evdns_base *dns = NULL; + int drop_count = 2; + ev_uint16_t portnum = 0; + char buf[64]; + + struct generic_dns_callback_result r1; + + port = regress_get_dnsserver(base, &portnum, NULL, + fail_server_cb, &drop_count); + tt_assert(port); + evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum); + + dns = evdns_base_new(base, flags); + tt_assert(!evdns_base_nameserver_ip_add(dns, buf)); + tt_assert(! evdns_base_set_option(dns, "timeout", "0.2")); + tt_assert(! evdns_base_set_option(dns, "max-timeouts:", "10")); + tt_assert(! evdns_base_set_option(dns, "initial-probe-timeout", "0.1")); + + evdns_base_resolve_ipv4(dns, "host.example.com", 0, + generic_dns_callback, &r1); + + n_replies_left = 1; + exit_base = base; + + event_base_dispatch(base); + + tt_int_op(drop_count, ==, 0); + + tt_int_op(r1.type, ==, DNS_IPv4_A); + tt_int_op(r1.count, ==, 1); + tt_int_op(((ev_uint32_t*)r1.addrs)[0], ==, htonl(0x10204080)); + + /* Now try again, but this time have the server get treated as + * failed, so we can send it a test probe. */ + drop_count = 4; + tt_assert(! evdns_base_set_option(dns, "max-timeouts:", "2")); + tt_assert(! evdns_base_set_option(dns, "attempts:", "3")); + memset(&r1, 0, sizeof(r1)); + + evdns_base_resolve_ipv4(dns, "host.example.com", 0, + generic_dns_callback, &r1); + + n_replies_left = 2; + + /* This will run until it answers the "google.com" probe request. */ + event_base_dispatch(base); + + /* We'll treat the server as failed here. */ + tt_int_op(r1.result, ==, DNS_ERR_TIMEOUT); + + /* It should work this time. */ + tt_int_op(drop_count, ==, 0); + evdns_base_resolve_ipv4(dns, "host.example.com", 0, + generic_dns_callback, &r1); + + event_base_dispatch(base); + tt_int_op(r1.result, ==, DNS_ERR_NONE); + tt_int_op(r1.type, ==, DNS_IPv4_A); + tt_int_op(r1.count, ==, 1); + tt_int_op(((ev_uint32_t*)r1.addrs)[0], ==, htonl(0x10204080)); + +end: + if (dns) + evdns_base_free(dns, 0); + if (port) + evdns_close_server_port(port); +} +static void +dns_retry_test(void *arg) +{ + dns_retry_test_impl(arg, 0); +} +static void +dns_retry_disable_when_inactive_test(void *arg) +{ + dns_retry_test_impl(arg, EVDNS_BASE_DISABLE_WHEN_INACTIVE); +} + +static struct regress_dns_server_table internal_error_table[] = { + /* Error 4 (NOTIMPL) makes us reissue the request to another server + if we can. + + XXXX we should reissue under a much wider set of circumstances! + */ + { "foof.example.com", "err", "4", 0, 0 }, + { NULL, NULL, NULL, 0, 0 } +}; + +static struct regress_dns_server_table reissue_table[] = { + { "foof.example.com", "A", "240.15.240.15", 0, 0 }, + { NULL, NULL, NULL, 0, 0 } +}; + +static void +dns_reissue_test_impl(void *arg, int flags) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evdns_server_port *port1 = NULL, *port2 = NULL; + struct evdns_base *dns = NULL; + struct generic_dns_callback_result r1; + ev_uint16_t portnum1 = 0, portnum2=0; + char buf1[64], buf2[64]; + + port1 = regress_get_dnsserver(base, &portnum1, NULL, + regress_dns_server_cb, internal_error_table); + tt_assert(port1); + port2 = regress_get_dnsserver(base, &portnum2, NULL, + regress_dns_server_cb, reissue_table); + tt_assert(port2); + evutil_snprintf(buf1, sizeof(buf1), "127.0.0.1:%d", (int)portnum1); + evutil_snprintf(buf2, sizeof(buf2), "127.0.0.1:%d", (int)portnum2); + + dns = evdns_base_new(base, flags); + tt_assert(!evdns_base_nameserver_ip_add(dns, buf1)); + tt_assert(! evdns_base_set_option(dns, "timeout:", "0.3")); + tt_assert(! evdns_base_set_option(dns, "max-timeouts:", "2")); + tt_assert(! evdns_base_set_option(dns, "attempts:", "5")); + + memset(&r1, 0, sizeof(r1)); + evdns_base_resolve_ipv4(dns, "foof.example.com", 0, + generic_dns_callback, &r1); + + /* Add this after, so that we are sure to get a reissue. */ + tt_assert(!evdns_base_nameserver_ip_add(dns, buf2)); + + n_replies_left = 1; + exit_base = base; + + event_base_dispatch(base); + tt_int_op(r1.result, ==, DNS_ERR_NONE); + tt_int_op(r1.type, ==, DNS_IPv4_A); + tt_int_op(r1.count, ==, 1); + tt_int_op(((ev_uint32_t*)r1.addrs)[0], ==, htonl(0xf00ff00f)); + + /* Make sure we dropped at least once. */ + tt_int_op(internal_error_table[0].seen, >, 0); + +end: + if (dns) + evdns_base_free(dns, 0); + if (port1) + evdns_close_server_port(port1); + if (port2) + evdns_close_server_port(port2); +} +static void +dns_reissue_test(void *arg) +{ + dns_reissue_test_impl(arg, 0); +} +static void +dns_reissue_disable_when_inactive_test(void *arg) +{ + dns_reissue_test_impl(arg, EVDNS_BASE_DISABLE_WHEN_INACTIVE); +} + +#if 0 +static void +dumb_bytes_fn(char *p, size_t n) +{ + unsigned i; + /* This gets us 6 bits of entropy per transaction ID, which means we + * will have probably have collisions and need to pick again. */ + for (i=0;i<n;++i) + p[i] = (char)(rand() & 7); +} +#endif + +static void +dns_inflight_test_impl(void *arg, int flags) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evdns_base *dns = NULL; + struct evdns_server_port *dns_port = NULL; + ev_uint16_t portnum = 0; + char buf[64]; + int disable_when_inactive = flags & EVDNS_BASE_DISABLE_WHEN_INACTIVE; + + struct generic_dns_callback_result r[20]; + int i; + + dns_port = regress_get_dnsserver(base, &portnum, NULL, + regress_dns_server_cb, reissue_table); + tt_assert(dns_port); + if (disable_when_inactive) { + exit_port = dns_port; + } + + evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum); + + dns = evdns_base_new(base, flags); + tt_assert(!evdns_base_nameserver_ip_add(dns, buf)); + tt_assert(! evdns_base_set_option(dns, "max-inflight:", "3")); + tt_assert(! evdns_base_set_option(dns, "randomize-case:", "0")); + + for (i=0;i<20;++i) + evdns_base_resolve_ipv4(dns, "foof.example.com", 0, generic_dns_callback, &r[i]); + + n_replies_left = 20; + exit_base = base; + + event_base_dispatch(base); + + for (i=0;i<20;++i) { + tt_int_op(r[i].type, ==, DNS_IPv4_A); + tt_int_op(r[i].count, ==, 1); + tt_int_op(((ev_uint32_t*)r[i].addrs)[0], ==, htonl(0xf00ff00f)); + } + +end: + if (dns) + evdns_base_free(dns, 0); + if (exit_port) { + evdns_close_server_port(exit_port); + exit_port = NULL; + } else if (! disable_when_inactive) { + evdns_close_server_port(dns_port); + } +} + +static void +dns_inflight_test(void *arg) +{ + dns_inflight_test_impl(arg, 0); +} + +static void +dns_disable_when_inactive_test(void *arg) +{ + dns_inflight_test_impl(arg, EVDNS_BASE_DISABLE_WHEN_INACTIVE); +} + +static void +dns_disable_when_inactive_no_ns_test(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base, *inactive_base; + struct evdns_base *dns = NULL; + ev_uint16_t portnum = 0; + char buf[64]; + struct generic_dns_callback_result r; + + inactive_base = event_base_new(); + tt_assert(inactive_base); + + /** Create dns server with inactive base, to avoid replying to clients */ + tt_assert(regress_dnsserver(inactive_base, &portnum, search_table)); + evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum); + + dns = evdns_base_new(base, EVDNS_BASE_DISABLE_WHEN_INACTIVE); + tt_assert(!evdns_base_nameserver_ip_add(dns, buf)); + tt_assert(! evdns_base_set_option(dns, "timeout:", "0.1")); + + evdns_base_resolve_ipv4(dns, "foof.example.com", 0, generic_dns_callback, &r); + n_replies_left = 1; + exit_base = base; + + event_base_dispatch(base); + + tt_int_op(n_replies_left, ==, 0); + + tt_int_op(r.result, ==, DNS_ERR_TIMEOUT); + tt_int_op(r.count, ==, 0); + tt_ptr_op(r.addrs, ==, NULL); + +end: + if (dns) + evdns_base_free(dns, 0); + regress_clean_dnsserver(); + if (inactive_base) + event_base_free(inactive_base); +} + +/* === Test for bufferevent_socket_connect_hostname */ + +static int total_connected_or_failed = 0; +static int total_n_accepted = 0; +static struct event_base *be_connect_hostname_base = NULL; + +/* Implements a DNS server for the connect_hostname test and the + * getaddrinfo_async test */ +static void +be_getaddrinfo_server_cb(struct evdns_server_request *req, void *data) +{ + int i; + int *n_got_p=data; + int added_any=0; + ++*n_got_p; + + for (i=0;i<req->nquestions;++i) { + const int qtype = req->questions[i]->type; + const int qclass = req->questions[i]->dns_question_class; + const char *qname = req->questions[i]->name; + struct in_addr ans; + struct in6_addr ans6; + memset(&ans6, 0, sizeof(ans6)); + + TT_BLATHER(("Got question about %s, type=%d", qname, qtype)); + + if (qtype == EVDNS_TYPE_A && + qclass == EVDNS_CLASS_INET && + !evutil_ascii_strcasecmp(qname, "nobodaddy.example.com")) { + ans.s_addr = htonl(0x7f000001); + evdns_server_request_add_a_reply(req, qname, + 1, &ans.s_addr, 2000); + added_any = 1; + } else if (!evutil_ascii_strcasecmp(qname, + "nosuchplace.example.com")) { + /* ok, just say notfound. */ + } else if (!evutil_ascii_strcasecmp(qname, + "both.example.com")) { + if (qtype == EVDNS_TYPE_A) { + ans.s_addr = htonl(0x50502020); + evdns_server_request_add_a_reply(req, qname, + 1, &ans.s_addr, 2000); + added_any = 1; + } else if (qtype == EVDNS_TYPE_AAAA) { + ans6.s6_addr[0] = 0x80; + ans6.s6_addr[1] = 0xff; + ans6.s6_addr[14] = 0xbb; + ans6.s6_addr[15] = 0xbb; + evdns_server_request_add_aaaa_reply(req, qname, + 1, &ans6.s6_addr, 2000); + added_any = 1; + } + evdns_server_request_add_cname_reply(req, qname, + "both-canonical.example.com", 1000); + } else if (!evutil_ascii_strcasecmp(qname, + "v4only.example.com") || + !evutil_ascii_strcasecmp(qname, "v4assert.example.com")) { + if (qtype == EVDNS_TYPE_A) { + ans.s_addr = htonl(0x12345678); + evdns_server_request_add_a_reply(req, qname, + 1, &ans.s_addr, 2000); + added_any = 1; + } else if (!evutil_ascii_strcasecmp(qname, + "v4assert.example.com")) { + TT_FAIL(("Got an AAAA request for v4assert")); + } + } else if (!evutil_ascii_strcasecmp(qname, + "v6only.example.com") || + !evutil_ascii_strcasecmp(qname, "v6assert.example.com")) { + if (qtype == EVDNS_TYPE_AAAA) { + ans6.s6_addr[0] = 0x0b; + ans6.s6_addr[1] = 0x0b; + ans6.s6_addr[14] = 0xf0; + ans6.s6_addr[15] = 0x0d; + evdns_server_request_add_aaaa_reply(req, qname, + 1, &ans6.s6_addr, 2000); + added_any = 1; + } else if (!evutil_ascii_strcasecmp(qname, + "v6assert.example.com")) { + TT_FAIL(("Got a A request for v6assert")); + } + } else if (!evutil_ascii_strcasecmp(qname, + "v6timeout.example.com")) { + if (qtype == EVDNS_TYPE_A) { + ans.s_addr = htonl(0xabcdef01); + evdns_server_request_add_a_reply(req, qname, + 1, &ans.s_addr, 2000); + added_any = 1; + } else if (qtype == EVDNS_TYPE_AAAA) { + /* Let the v6 request time out.*/ + evdns_server_request_drop(req); + return; + } + } else if (!evutil_ascii_strcasecmp(qname, + "v4timeout.example.com")) { + if (qtype == EVDNS_TYPE_AAAA) { + ans6.s6_addr[0] = 0x0a; + ans6.s6_addr[1] = 0x0a; + ans6.s6_addr[14] = 0xff; + ans6.s6_addr[15] = 0x01; + evdns_server_request_add_aaaa_reply(req, qname, + 1, &ans6.s6_addr, 2000); + added_any = 1; + } else if (qtype == EVDNS_TYPE_A) { + /* Let the v4 request time out.*/ + evdns_server_request_drop(req); + return; + } + } else if (!evutil_ascii_strcasecmp(qname, + "v6timeout-nonexist.example.com")) { + if (qtype == EVDNS_TYPE_A) { + /* Fall through, give an nexist. */ + } else if (qtype == EVDNS_TYPE_AAAA) { + /* Let the v6 request time out.*/ + evdns_server_request_drop(req); + return; + } + } else if (!evutil_ascii_strcasecmp(qname, + "all-timeout.example.com")) { + /* drop all requests */ + evdns_server_request_drop(req); + return; + } else { + TT_GRIPE(("Got weird request for %s",qname)); + } + } + if (added_any) { + TT_BLATHER(("answering")); + evdns_server_request_respond(req, 0); + } else { + TT_BLATHER(("saying nexist.")); + evdns_server_request_respond(req, 3); + } +} + +/* Implements a listener for connect_hostname test. */ +static void +nil_accept_cb(struct evconnlistener *l, evutil_socket_t fd, struct sockaddr *s, + int socklen, void *arg) +{ + int *p = arg; + (*p)++; + ++total_n_accepted; + /* don't do anything with the socket; let it close when we exit() */ + if (total_n_accepted >= 3 && total_connected_or_failed >= 5) + event_base_loopexit(be_connect_hostname_base, + NULL); +} + +struct be_conn_hostname_result { + int dnserr; + int what; +}; + +/* Bufferevent event callback for the connect_hostname test: remembers what + * event we got. */ +static void +be_connect_hostname_event_cb(struct bufferevent *bev, short what, void *ctx) +{ + struct be_conn_hostname_result *got = ctx; + if (!got->what) { + TT_BLATHER(("Got a bufferevent event %d", what)); + got->what = what; + + if ((what & BEV_EVENT_CONNECTED) || (what & BEV_EVENT_ERROR)) { + int r; + if ((r = bufferevent_socket_get_dns_error(bev))) { + got->dnserr = r; + TT_BLATHER(("DNS error %d: %s", r, + evutil_gai_strerror(r))); + } ++total_connected_or_failed; + TT_BLATHER(("Got %d connections or errors.", total_connected_or_failed)); + + if (total_n_accepted >= 3 && total_connected_or_failed >= 5) + event_base_loopexit(be_connect_hostname_base, + NULL); + } + } else { + TT_FAIL(("Two events on one bufferevent. %d,%d", + got->what, (int)what)); + } +} + +static void +test_bufferevent_connect_hostname(void *arg) +{ + struct basic_test_data *data = arg; + struct evconnlistener *listener = NULL; + struct bufferevent *be1=NULL, *be2=NULL, *be3=NULL, *be4=NULL, *be5=NULL; + struct be_conn_hostname_result be1_outcome={0,0}, be2_outcome={0,0}, + be3_outcome={0,0}, be4_outcome={0,0}, be5_outcome={0,0}; + int expect_err5; + struct evdns_base *dns=NULL; + struct evdns_server_port *port=NULL; + struct sockaddr_in sin; + int listener_port=-1; + ev_uint16_t dns_port=0; + int n_accept=0, n_dns=0; + char buf[128]; + + be_connect_hostname_base = data->base; + + /* Bind an address and figure out what port it's on. */ + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ + sin.sin_port = 0; + listener = evconnlistener_new_bind(data->base, nil_accept_cb, + &n_accept, + LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_EXEC, + -1, (struct sockaddr *)&sin, sizeof(sin)); + tt_assert(listener); + listener_port = regress_get_socket_port( + evconnlistener_get_fd(listener)); + + port = regress_get_dnsserver(data->base, &dns_port, NULL, + be_getaddrinfo_server_cb, &n_dns); + tt_assert(port); + tt_int_op(dns_port, >=, 0); + + /* Start an evdns_base that uses the server as its resolver. */ + dns = evdns_base_new(data->base, 0); + evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)dns_port); + evdns_base_nameserver_ip_add(dns, buf); + + /* Now, finally, at long last, launch the bufferevents. One should do + * a failing lookup IP, one should do a successful lookup by IP, + * and one should do a successful lookup by hostname. */ + be1 = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE); + be2 = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE); + be3 = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE); + be4 = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE); + be5 = bufferevent_socket_new(data->base, -1, BEV_OPT_CLOSE_ON_FREE); + + bufferevent_setcb(be1, NULL, NULL, be_connect_hostname_event_cb, + &be1_outcome); + bufferevent_setcb(be2, NULL, NULL, be_connect_hostname_event_cb, + &be2_outcome); + bufferevent_setcb(be3, NULL, NULL, be_connect_hostname_event_cb, + &be3_outcome); + bufferevent_setcb(be4, NULL, NULL, be_connect_hostname_event_cb, + &be4_outcome); + bufferevent_setcb(be5, NULL, NULL, be_connect_hostname_event_cb, + &be5_outcome); + + /* Launch an async resolve that will fail. */ + tt_assert(!bufferevent_socket_connect_hostname(be1, dns, AF_INET, + "nosuchplace.example.com", listener_port)); + /* Connect to the IP without resolving. */ + tt_assert(!bufferevent_socket_connect_hostname(be2, dns, AF_INET, + "127.0.0.1", listener_port)); + /* Launch an async resolve that will succeed. */ + tt_assert(!bufferevent_socket_connect_hostname(be3, dns, AF_INET, + "nobodaddy.example.com", listener_port)); + /* Use the blocking resolver. This one will fail if your resolver + * can't resolve localhost to 127.0.0.1 */ + tt_assert(!bufferevent_socket_connect_hostname(be4, NULL, AF_INET, + "localhost", listener_port)); + /* Use the blocking resolver with a nonexistent hostname. */ + tt_assert(!bufferevent_socket_connect_hostname(be5, NULL, AF_INET, + "nonesuch.nowhere.example.com", 80)); + { + /* The blocking resolver will use the system nameserver, which + * might tell us anything. (Yes, some twits even pretend that + * example.com is real.) Let's see what answer to expect. */ + struct evutil_addrinfo hints, *ai = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + expect_err5 = evutil_getaddrinfo( + "nonesuch.nowhere.example.com", "80", &hints, &ai); + } + + event_base_dispatch(data->base); + + tt_int_op(be1_outcome.what, ==, BEV_EVENT_ERROR); + tt_int_op(be1_outcome.dnserr, ==, EVUTIL_EAI_NONAME); + tt_int_op(be2_outcome.what, ==, BEV_EVENT_CONNECTED); + tt_int_op(be2_outcome.dnserr, ==, 0); + tt_int_op(be3_outcome.what, ==, BEV_EVENT_CONNECTED); + tt_int_op(be3_outcome.dnserr, ==, 0); + tt_int_op(be4_outcome.what, ==, BEV_EVENT_CONNECTED); + tt_int_op(be4_outcome.dnserr, ==, 0); + if (expect_err5) { + tt_int_op(be5_outcome.what, ==, BEV_EVENT_ERROR); + tt_int_op(be5_outcome.dnserr, ==, expect_err5); + } + + tt_int_op(n_accept, ==, 3); + tt_int_op(n_dns, ==, 2); + +end: + if (listener) + evconnlistener_free(listener); + if (port) + evdns_close_server_port(port); + if (dns) + evdns_base_free(dns, 0); + if (be1) + bufferevent_free(be1); + if (be2) + bufferevent_free(be2); + if (be3) + bufferevent_free(be3); + if (be4) + bufferevent_free(be4); + if (be5) + bufferevent_free(be5); +} + + +struct gai_outcome { + int err; + struct evutil_addrinfo *ai; +}; + +static int n_gai_results_pending = 0; +static struct event_base *exit_base_on_no_pending_results = NULL; + +static void +gai_cb(int err, struct evutil_addrinfo *res, void *ptr) +{ + struct gai_outcome *go = ptr; + go->err = err; + go->ai = res; + if (--n_gai_results_pending <= 0 && exit_base_on_no_pending_results) + event_base_loopexit(exit_base_on_no_pending_results, NULL); + if (n_gai_results_pending < 900) + TT_BLATHER(("Got an answer; expecting %d more.", + n_gai_results_pending)); +} + +static void +cancel_gai_cb(evutil_socket_t fd, short what, void *ptr) +{ + struct evdns_getaddrinfo_request *r = ptr; + evdns_getaddrinfo_cancel(r); +} + +static void +test_getaddrinfo_async(void *arg) +{ + struct basic_test_data *data = arg; + struct evutil_addrinfo hints, *a; + struct gai_outcome local_outcome; + struct gai_outcome a_out[12]; + int i; + struct evdns_getaddrinfo_request *r; + char buf[128]; + struct evdns_server_port *port = NULL; + ev_uint16_t dns_port = 0; + int n_dns_questions = 0; + struct evdns_base *dns_base; + + memset(a_out, 0, sizeof(a_out)); + memset(&local_outcome, 0, sizeof(local_outcome)); + + dns_base = evdns_base_new(data->base, 0); + tt_assert(dns_base); + + /* for localhost */ + evdns_base_load_hosts(dns_base, NULL); + + tt_assert(! evdns_base_set_option(dns_base, "timeout", "0.3")); + tt_assert(! evdns_base_set_option(dns_base, "getaddrinfo-allow-skew", "0.2")); + + n_gai_results_pending = 10000; /* don't think about exiting yet. */ + + /* 1. Try some cases that will never hit the asynchronous resolver. */ + /* 1a. Simple case with a symbolic service name */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + memset(&local_outcome, 0, sizeof(local_outcome)); + r = evdns_getaddrinfo(dns_base, "1.2.3.4", "http", + &hints, gai_cb, &local_outcome); + tt_assert(! r); + if (!local_outcome.err) { + tt_ptr_op(local_outcome.ai,!=,NULL); + test_ai_eq(local_outcome.ai, "1.2.3.4:80", SOCK_STREAM, IPPROTO_TCP); + evutil_freeaddrinfo(local_outcome.ai); + local_outcome.ai = NULL; + } else { + TT_BLATHER(("Apparently we have no getservbyname.")); + } + + /* 1b. EVUTIL_AI_NUMERICHOST is set */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_flags = EVUTIL_AI_NUMERICHOST; + memset(&local_outcome, 0, sizeof(local_outcome)); + r = evdns_getaddrinfo(dns_base, "www.google.com", "80", + &hints, gai_cb, &local_outcome); + tt_ptr_op(r,==,NULL); + tt_int_op(local_outcome.err,==,EVUTIL_EAI_NONAME); + tt_ptr_op(local_outcome.ai,==,NULL); + + /* 1c. We give a numeric address (ipv6) */ + memset(&hints, 0, sizeof(hints)); + memset(&local_outcome, 0, sizeof(local_outcome)); + hints.ai_family = PF_UNSPEC; + hints.ai_protocol = IPPROTO_TCP; + r = evdns_getaddrinfo(dns_base, "f::f", "8008", + &hints, gai_cb, &local_outcome); + tt_assert(!r); + tt_int_op(local_outcome.err,==,0); + tt_assert(local_outcome.ai); + tt_ptr_op(local_outcome.ai->ai_next,==,NULL); + test_ai_eq(local_outcome.ai, "[f::f]:8008", SOCK_STREAM, IPPROTO_TCP); + evutil_freeaddrinfo(local_outcome.ai); + local_outcome.ai = NULL; + + /* 1d. We give a numeric address (ipv4) */ + memset(&hints, 0, sizeof(hints)); + memset(&local_outcome, 0, sizeof(local_outcome)); + hints.ai_family = PF_UNSPEC; + r = evdns_getaddrinfo(dns_base, "5.6.7.8", NULL, + &hints, gai_cb, &local_outcome); + tt_assert(!r); + tt_int_op(local_outcome.err,==,0); + tt_assert(local_outcome.ai); + a = ai_find_by_protocol(local_outcome.ai, IPPROTO_TCP); + tt_assert(a); + test_ai_eq(a, "5.6.7.8", SOCK_STREAM, IPPROTO_TCP); + a = ai_find_by_protocol(local_outcome.ai, IPPROTO_UDP); + tt_assert(a); + test_ai_eq(a, "5.6.7.8", SOCK_DGRAM, IPPROTO_UDP); + evutil_freeaddrinfo(local_outcome.ai); + local_outcome.ai = NULL; + + /* 1e. nodename is NULL (bind) */ + memset(&hints, 0, sizeof(hints)); + memset(&local_outcome, 0, sizeof(local_outcome)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = EVUTIL_AI_PASSIVE; + r = evdns_getaddrinfo(dns_base, NULL, "9090", + &hints, gai_cb, &local_outcome); + tt_assert(!r); + tt_int_op(local_outcome.err,==,0); + tt_assert(local_outcome.ai); + /* we should get a v4 address of 0.0.0.0... */ + a = ai_find_by_family(local_outcome.ai, PF_INET); + tt_assert(a); + test_ai_eq(a, "0.0.0.0:9090", SOCK_DGRAM, IPPROTO_UDP); + /* ... and a v6 address of ::0 */ + a = ai_find_by_family(local_outcome.ai, PF_INET6); + tt_assert(a); + test_ai_eq(a, "[::]:9090", SOCK_DGRAM, IPPROTO_UDP); + evutil_freeaddrinfo(local_outcome.ai); + local_outcome.ai = NULL; + + /* 1f. nodename is NULL (connect) */ + memset(&hints, 0, sizeof(hints)); + memset(&local_outcome, 0, sizeof(local_outcome)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + r = evdns_getaddrinfo(dns_base, NULL, "2", + &hints, gai_cb, &local_outcome); + tt_assert(!r); + tt_int_op(local_outcome.err,==,0); + tt_assert(local_outcome.ai); + /* we should get a v4 address of 127.0.0.1 .... */ + a = ai_find_by_family(local_outcome.ai, PF_INET); + tt_assert(a); + test_ai_eq(a, "127.0.0.1:2", SOCK_STREAM, IPPROTO_TCP); + /* ... and a v6 address of ::1 */ + a = ai_find_by_family(local_outcome.ai, PF_INET6); + tt_assert(a); + test_ai_eq(a, "[::1]:2", SOCK_STREAM, IPPROTO_TCP); + evutil_freeaddrinfo(local_outcome.ai); + local_outcome.ai = NULL; + + /* 1g. We find localhost immediately. (pf_unspec) */ + memset(&hints, 0, sizeof(hints)); + memset(&local_outcome, 0, sizeof(local_outcome)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + r = evdns_getaddrinfo(dns_base, "LOCALHOST", "80", + &hints, gai_cb, &local_outcome); + tt_assert(!r); + tt_int_op(local_outcome.err,==,0); + tt_assert(local_outcome.ai); + /* we should get a v4 address of 127.0.0.1 .... */ + a = ai_find_by_family(local_outcome.ai, PF_INET); + tt_assert(a); + test_ai_eq(a, "127.0.0.1:80", SOCK_STREAM, IPPROTO_TCP); + /* ... and a v6 address of ::1 */ + a = ai_find_by_family(local_outcome.ai, PF_INET6); + tt_assert(a); + test_ai_eq(a, "[::1]:80", SOCK_STREAM, IPPROTO_TCP); + evutil_freeaddrinfo(local_outcome.ai); + local_outcome.ai = NULL; + + /* 1g. We find localhost immediately. (pf_inet6) */ + memset(&hints, 0, sizeof(hints)); + memset(&local_outcome, 0, sizeof(local_outcome)); + hints.ai_family = PF_INET6; + hints.ai_socktype = SOCK_STREAM; + r = evdns_getaddrinfo(dns_base, "LOCALHOST", "9999", + &hints, gai_cb, &local_outcome); + tt_assert(! r); + tt_int_op(local_outcome.err,==,0); + tt_assert(local_outcome.ai); + a = local_outcome.ai; + test_ai_eq(a, "[::1]:9999", SOCK_STREAM, IPPROTO_TCP); + tt_ptr_op(a->ai_next, ==, NULL); + evutil_freeaddrinfo(local_outcome.ai); + local_outcome.ai = NULL; + + /* 2. Okay, now we can actually test the asynchronous resolver. */ + /* Start a dummy local dns server... */ + port = regress_get_dnsserver(data->base, &dns_port, NULL, + be_getaddrinfo_server_cb, &n_dns_questions); + tt_assert(port); + tt_int_op(dns_port, >=, 0); + /* ... and tell the evdns_base about it. */ + evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", dns_port); + evdns_base_nameserver_ip_add(dns_base, buf); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = EVUTIL_AI_CANONNAME; + /* 0: Request for both.example.com should return both addresses. */ + r = evdns_getaddrinfo(dns_base, "both.example.com", "8000", + &hints, gai_cb, &a_out[0]); + tt_assert(r); + + /* 1: Request for v4only.example.com should return one address. */ + r = evdns_getaddrinfo(dns_base, "v4only.example.com", "8001", + &hints, gai_cb, &a_out[1]); + tt_assert(r); + + /* 2: Request for v6only.example.com should return one address. */ + hints.ai_flags = 0; + r = evdns_getaddrinfo(dns_base, "v6only.example.com", "8002", + &hints, gai_cb, &a_out[2]); + tt_assert(r); + + /* 3: PF_INET request for v4assert.example.com should not generate a + * v6 request. The server will fail the test if it does. */ + hints.ai_family = PF_INET; + r = evdns_getaddrinfo(dns_base, "v4assert.example.com", "8003", + &hints, gai_cb, &a_out[3]); + tt_assert(r); + + /* 4: PF_INET6 request for v6assert.example.com should not generate a + * v4 request. The server will fail the test if it does. */ + hints.ai_family = PF_INET6; + r = evdns_getaddrinfo(dns_base, "v6assert.example.com", "8004", + &hints, gai_cb, &a_out[4]); + tt_assert(r); + + /* 5: PF_INET request for nosuchplace.example.com should give NEXIST. */ + hints.ai_family = PF_INET; + r = evdns_getaddrinfo(dns_base, "nosuchplace.example.com", "8005", + &hints, gai_cb, &a_out[5]); + tt_assert(r); + + /* 6: PF_UNSPEC request for nosuchplace.example.com should give NEXIST. + */ + hints.ai_family = PF_UNSPEC; + r = evdns_getaddrinfo(dns_base, "nosuchplace.example.com", "8006", + &hints, gai_cb, &a_out[6]); + tt_assert(r); + + /* 7: PF_UNSPEC request for v6timeout.example.com should give an ipv4 + * address only. */ + hints.ai_family = PF_UNSPEC; + r = evdns_getaddrinfo(dns_base, "v6timeout.example.com", "8007", + &hints, gai_cb, &a_out[7]); + tt_assert(r); + + /* 8: PF_UNSPEC request for v6timeout-nonexist.example.com should give + * a NEXIST */ + hints.ai_family = PF_UNSPEC; + r = evdns_getaddrinfo(dns_base, "v6timeout-nonexist.example.com", + "8008", &hints, gai_cb, &a_out[8]); + tt_assert(r); + + /* 9: AI_ADDRCONFIG should at least not crash. Can't test it more + * without knowing what kind of internet we have. */ + hints.ai_flags |= EVUTIL_AI_ADDRCONFIG; + r = evdns_getaddrinfo(dns_base, "both.example.com", + "8009", &hints, gai_cb, &a_out[9]); + tt_assert(r); + + /* 10: PF_UNSPEC for v4timeout.example.com should give an ipv6 address + * only. */ + hints.ai_family = PF_UNSPEC; + hints.ai_flags = 0; + r = evdns_getaddrinfo(dns_base, "v4timeout.example.com", "8010", + &hints, gai_cb, &a_out[10]); + tt_assert(r); + + /* 11: timeout.example.com: cancel it after 100 msec. */ + r = evdns_getaddrinfo(dns_base, "all-timeout.example.com", "8011", + &hints, gai_cb, &a_out[11]); + tt_assert(r); + { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100*1000; /* 100 msec */ + event_base_once(data->base, -1, EV_TIMEOUT, cancel_gai_cb, + r, &tv); + } + + /* XXXXX There are more tests we could do, including: + + - A test to elicit NODATA. + + */ + + n_gai_results_pending = 12; + exit_base_on_no_pending_results = data->base; + + event_base_dispatch(data->base); + + /* 0: both.example.com */ + tt_int_op(a_out[0].err, ==, 0); + tt_assert(a_out[0].ai); + tt_assert(a_out[0].ai->ai_next); + tt_assert(!a_out[0].ai->ai_next->ai_next); + a = ai_find_by_family(a_out[0].ai, PF_INET); + tt_assert(a); + test_ai_eq(a, "80.80.32.32:8000", SOCK_STREAM, IPPROTO_TCP); + a = ai_find_by_family(a_out[0].ai, PF_INET6); + tt_assert(a); + test_ai_eq(a, "[80ff::bbbb]:8000", SOCK_STREAM, IPPROTO_TCP); + tt_assert(a_out[0].ai->ai_canonname); + tt_str_op(a_out[0].ai->ai_canonname, ==, "both-canonical.example.com"); + + /* 1: v4only.example.com */ + tt_int_op(a_out[1].err, ==, 0); + tt_assert(a_out[1].ai); + tt_assert(! a_out[1].ai->ai_next); + test_ai_eq(a_out[1].ai, "18.52.86.120:8001", SOCK_STREAM, IPPROTO_TCP); + tt_assert(a_out[1].ai->ai_canonname == NULL); + + + /* 2: v6only.example.com */ + tt_int_op(a_out[2].err, ==, 0); + tt_assert(a_out[2].ai); + tt_assert(! a_out[2].ai->ai_next); + test_ai_eq(a_out[2].ai, "[b0b::f00d]:8002", SOCK_STREAM, IPPROTO_TCP); + + /* 3: v4assert.example.com */ + tt_int_op(a_out[3].err, ==, 0); + tt_assert(a_out[3].ai); + tt_assert(! a_out[3].ai->ai_next); + test_ai_eq(a_out[3].ai, "18.52.86.120:8003", SOCK_STREAM, IPPROTO_TCP); + + /* 4: v6assert.example.com */ + tt_int_op(a_out[4].err, ==, 0); + tt_assert(a_out[4].ai); + tt_assert(! a_out[4].ai->ai_next); + test_ai_eq(a_out[4].ai, "[b0b::f00d]:8004", SOCK_STREAM, IPPROTO_TCP); + + /* 5: nosuchplace.example.com (inet) */ + tt_int_op(a_out[5].err, ==, EVUTIL_EAI_NONAME); + tt_assert(! a_out[5].ai); + + /* 6: nosuchplace.example.com (unspec) */ + tt_int_op(a_out[6].err, ==, EVUTIL_EAI_NONAME); + tt_assert(! a_out[6].ai); + + /* 7: v6timeout.example.com */ + tt_int_op(a_out[7].err, ==, 0); + tt_assert(a_out[7].ai); + tt_assert(! a_out[7].ai->ai_next); + test_ai_eq(a_out[7].ai, "171.205.239.1:8007", SOCK_STREAM, IPPROTO_TCP); + + /* 8: v6timeout-nonexist.example.com */ + tt_int_op(a_out[8].err, ==, EVUTIL_EAI_NONAME); + tt_assert(! a_out[8].ai); + + /* 9: both (ADDRCONFIG) */ + tt_int_op(a_out[9].err, ==, 0); + tt_assert(a_out[9].ai); + a = ai_find_by_family(a_out[9].ai, PF_INET); + if (a) + test_ai_eq(a, "80.80.32.32:8009", SOCK_STREAM, IPPROTO_TCP); + else + tt_assert(ai_find_by_family(a_out[9].ai, PF_INET6)); + a = ai_find_by_family(a_out[9].ai, PF_INET6); + if (a) + test_ai_eq(a, "[80ff::bbbb]:8009", SOCK_STREAM, IPPROTO_TCP); + else + tt_assert(ai_find_by_family(a_out[9].ai, PF_INET)); + + /* 10: v4timeout.example.com */ + tt_int_op(a_out[10].err, ==, 0); + tt_assert(a_out[10].ai); + tt_assert(! a_out[10].ai->ai_next); + test_ai_eq(a_out[10].ai, "[a0a::ff01]:8010", SOCK_STREAM, IPPROTO_TCP); + + /* 11: cancelled request. */ + tt_int_op(a_out[11].err, ==, EVUTIL_EAI_CANCEL); + tt_assert(a_out[11].ai == NULL); + +end: + if (local_outcome.ai) + evutil_freeaddrinfo(local_outcome.ai); + for (i=0;i<(int)ARRAY_SIZE(a_out);++i) { + if (a_out[i].ai) + evutil_freeaddrinfo(a_out[i].ai); + } + if (port) + evdns_close_server_port(port); + if (dns_base) + evdns_base_free(dns_base, 0); +} + +struct gaic_request_status { + int magic; + struct event_base *base; + struct evdns_base *dns_base; + struct evdns_getaddrinfo_request *request; + struct event cancel_event; + int canceled; +}; + +#define GAIC_MAGIC 0x1234abcd + +static int pending = 0; + +static void +gaic_cancel_request_cb(evutil_socket_t fd, short what, void *arg) +{ + struct gaic_request_status *status = arg; + + tt_assert(status->magic == GAIC_MAGIC); + status->canceled = 1; + evdns_getaddrinfo_cancel(status->request); + return; +end: + event_base_loopexit(status->base, NULL); +} + +static void +gaic_server_cb(struct evdns_server_request *req, void *arg) +{ + ev_uint32_t answer = 0x7f000001; + tt_assert(req->nquestions); + evdns_server_request_add_a_reply(req, req->questions[0]->name, 1, + &answer, 100); + evdns_server_request_respond(req, 0); + return; +end: + evdns_server_request_respond(req, DNS_ERR_REFUSED); +} + + +static void +gaic_getaddrinfo_cb(int result, struct evutil_addrinfo *res, void *arg) +{ + struct gaic_request_status *status = arg; + struct event_base *base = status->base; + tt_assert(status->magic == GAIC_MAGIC); + + if (result == EVUTIL_EAI_CANCEL) { + tt_assert(status->canceled); + } + event_del(&status->cancel_event); + + memset(status, 0xf0, sizeof(*status)); + free(status); + +end: + if (--pending <= 0) + event_base_loopexit(base, NULL); +} + +static void +gaic_launch(struct event_base *base, struct evdns_base *dns_base) +{ + struct gaic_request_status *status = calloc(1,sizeof(*status)); + struct timeval tv = { 0, 10000 }; + status->magic = GAIC_MAGIC; + status->base = base; + status->dns_base = dns_base; + event_assign(&status->cancel_event, base, -1, 0, gaic_cancel_request_cb, + status); + status->request = evdns_getaddrinfo(dns_base, + "foobar.bazquux.example.com", "80", NULL, gaic_getaddrinfo_cb, + status); + event_add(&status->cancel_event, &tv); + ++pending; +} + +#ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED +/* FIXME: We should move this to regress_main.c if anything else needs it.*/ + +/* Trivial replacements for malloc/free/realloc to check for memory leaks. + * Not threadsafe. */ +static int allocated_chunks = 0; + +static void * +cnt_malloc(size_t sz) +{ + allocated_chunks += 1; + return malloc(sz); +} + +static void * +cnt_realloc(void *old, size_t sz) +{ + if (!old) + allocated_chunks += 1; + if (!sz) + allocated_chunks -= 1; + return realloc(old, sz); +} + +static void +cnt_free(void *ptr) +{ + allocated_chunks -= 1; + free(ptr); +} + +struct testleak_env_t { + struct event_base *base; + struct evdns_base *dns_base; + struct evdns_request *req; + struct generic_dns_callback_result r; +}; + +static void * +testleak_setup(const struct testcase_t *testcase) +{ + struct testleak_env_t *env; + + allocated_chunks = 0; + + /* Reset allocation counter, to start allocations from the very beginning. + * (this will avoid false-positive negative numbers for allocated_chunks) + */ + libevent_global_shutdown(); + + event_set_mem_functions(cnt_malloc, cnt_realloc, cnt_free); + + event_enable_debug_mode(); + + /* not mm_calloc: we don't want to mess with the count. */ + env = calloc(1, sizeof(struct testleak_env_t)); + env->base = event_base_new(); + env->dns_base = evdns_base_new(env->base, 0); + env->req = evdns_base_resolve_ipv4( + env->dns_base, "example.com", DNS_QUERY_NO_SEARCH, + generic_dns_callback, &env->r); + return env; +} + +static int +testleak_cleanup(const struct testcase_t *testcase, void *env_) +{ + int ok = 0; + struct testleak_env_t *env = env_; + tt_assert(env); +#ifdef EVENT__DISABLE_DEBUG_MODE + tt_int_op(allocated_chunks, ==, 0); +#else + libevent_global_shutdown(); + tt_int_op(allocated_chunks, ==, 0); +#endif + ok = 1; +end: + if (env) { + if (env->dns_base) + evdns_base_free(env->dns_base, 0); + if (env->base) + event_base_free(env->base); + free(env); + } + return ok; +} + +static struct testcase_setup_t testleak_funcs = { + testleak_setup, testleak_cleanup +}; + +static void +test_dbg_leak_cancel(void *env_) +{ + /* cancel, loop, free/dns, free/base */ + struct testleak_env_t *env = env_; + int send_err_shutdown = 1; + evdns_cancel_request(env->dns_base, env->req); + env->req = 0; + + /* `req` is freed in callback, that's why one loop is required. */ + event_base_loop(env->base, EVLOOP_NONBLOCK); + + /* send_err_shutdown means nothing as soon as our request is + * already canceled */ + evdns_base_free(env->dns_base, send_err_shutdown); + env->dns_base = 0; + event_base_free(env->base); + env->base = 0; +} + +static void +dbg_leak_resume(void *env_, int cancel, int send_err_shutdown) +{ + /* cancel, loop, free/dns, free/base */ + struct testleak_env_t *env = env_; + if (cancel) { + evdns_cancel_request(env->dns_base, env->req); + tt_assert(!evdns_base_resume(env->dns_base)); + } else { + /* TODO: No nameservers, request can't be processed, must be errored */ + tt_assert(!evdns_base_resume(env->dns_base)); + } + + event_base_loop(env->base, EVLOOP_NONBLOCK); + /** + * Because we don't cancel request, and want our callback to recieve + * DNS_ERR_SHUTDOWN, we use deferred callback, and there was: + * - one extra malloc(), + * @see reply_schedule_callback() + * - and one missing free + * @see request_finished() (req->handle->pending_cb = 1) + * than we don't need to count in testleak_cleanup(), but we can clean them + * if we will run loop once again, but *after* evdns base freed. + */ + evdns_base_free(env->dns_base, send_err_shutdown); + env->dns_base = 0; + event_base_loop(env->base, EVLOOP_NONBLOCK); + +end: + event_base_free(env->base); + env->base = 0; +} + +#define IMPL_DBG_LEAK_RESUME(name, cancel, send_err_shutdown) \ + static void \ + test_dbg_leak_##name##_(void *env_) \ + { \ + dbg_leak_resume(env_, cancel, send_err_shutdown); \ + } +IMPL_DBG_LEAK_RESUME(resume, 0, 0) +IMPL_DBG_LEAK_RESUME(cancel_and_resume, 1, 0) +IMPL_DBG_LEAK_RESUME(resume_send_err, 0, 1) +IMPL_DBG_LEAK_RESUME(cancel_and_resume_send_err, 1, 1) + +static void +test_dbg_leak_shutdown(void *env_) +{ + /* free/dns, loop, free/base */ + struct testleak_env_t *env = env_; + int send_err_shutdown = 1; + + /* `req` is freed both with `send_err_shutdown` and without it, + * the only difference is `evdns_callback` call */ + env->req = 0; + + evdns_base_free(env->dns_base, send_err_shutdown); + env->dns_base = 0; + + /* `req` is freed in callback, that's why one loop is required */ + event_base_loop(env->base, EVLOOP_NONBLOCK); + event_base_free(env->base); + env->base = 0; +} +#endif + +static void +test_getaddrinfo_async_cancel_stress(void *ptr) +{ + struct event_base *base; + struct evdns_base *dns_base = NULL; + struct evdns_server_port *server = NULL; + evutil_socket_t fd = -1; + struct sockaddr_in sin; + struct sockaddr_storage ss; + ev_socklen_t slen; + int i; + + base = event_base_new(); + dns_base = evdns_base_new(base, 0); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = htonl(0x7f000001); + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + tt_abort_perror("socket"); + } + evutil_make_socket_nonblocking(fd); + if (bind(fd, (struct sockaddr*)&sin, sizeof(sin))<0) { + tt_abort_perror("bind"); + } + server = evdns_add_server_port_with_base(base, fd, 0, gaic_server_cb, + base); + + memset(&ss, 0, sizeof(ss)); + slen = sizeof(ss); + if (getsockname(fd, (struct sockaddr*)&ss, &slen)<0) { + tt_abort_perror("getsockname"); + } + evdns_base_nameserver_sockaddr_add(dns_base, + (struct sockaddr*)&ss, slen, 0); + + for (i = 0; i < 1000; ++i) { + gaic_launch(base, dns_base); + } + + event_base_dispatch(base); + +end: + if (dns_base) + evdns_base_free(dns_base, 1); + if (server) + evdns_close_server_port(server); + if (base) + event_base_free(base); + if (fd >= 0) + evutil_closesocket(fd); +} + +static void +dns_client_fail_requests_test(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evdns_base *dns = NULL; + struct evdns_server_port *dns_port = NULL; + ev_uint16_t portnum = 0; + char buf[64]; + + struct generic_dns_callback_result r[20]; + int i; + + dns_port = regress_get_dnsserver(base, &portnum, NULL, + regress_dns_server_cb, reissue_table); + tt_assert(dns_port); + + evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum); + + dns = evdns_base_new(base, EVDNS_BASE_DISABLE_WHEN_INACTIVE); + tt_assert(!evdns_base_nameserver_ip_add(dns, buf)); + + for (i = 0; i < 20; ++i) + evdns_base_resolve_ipv4(dns, "foof.example.com", 0, generic_dns_callback, &r[i]); + + n_replies_left = 20; + exit_base = base; + + evdns_base_free(dns, 1 /** fail requests */); + /** run defered callbacks, to trigger UAF */ + event_base_dispatch(base); + + tt_int_op(n_replies_left, ==, 0); + for (i = 0; i < 20; ++i) + tt_int_op(r[i].result, ==, DNS_ERR_SHUTDOWN); + +end: + evdns_close_server_port(dns_port); +} + +static void +getaddrinfo_cb(int err, struct evutil_addrinfo *res, void *ptr) +{ + generic_dns_callback(err, 0, 0, 0, NULL, ptr); +} +static void +dns_client_fail_requests_getaddrinfo_test(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evdns_base *dns = NULL; + struct evdns_server_port *dns_port = NULL; + ev_uint16_t portnum = 0; + char buf[64]; + + struct generic_dns_callback_result r[20]; + int i; + + dns_port = regress_get_dnsserver(base, &portnum, NULL, + regress_dns_server_cb, reissue_table); + tt_assert(dns_port); + + evutil_snprintf(buf, sizeof(buf), "127.0.0.1:%d", (int)portnum); + + dns = evdns_base_new(base, EVDNS_BASE_DISABLE_WHEN_INACTIVE); + tt_assert(!evdns_base_nameserver_ip_add(dns, buf)); + + for (i = 0; i < 20; ++i) + tt_assert(evdns_getaddrinfo(dns, "foof.example.com", "http", NULL, getaddrinfo_cb, &r[i])); + + n_replies_left = 20; + exit_base = base; + + evdns_base_free(dns, 1 /** fail requests */); + /** run defered callbacks, to trigger UAF */ + event_base_dispatch(base); + + tt_int_op(n_replies_left, ==, 0); + for (i = 0; i < 20; ++i) + tt_int_op(r[i].result, ==, EVUTIL_EAI_FAIL); + +end: + evdns_close_server_port(dns_port); +} + + +#define DNS_LEGACY(name, flags) \ + { #name, run_legacy_test_fn, flags|TT_LEGACY, &legacy_setup, \ + dns_##name } + +struct testcase_t dns_testcases[] = { + DNS_LEGACY(server, TT_FORK|TT_NEED_BASE), + DNS_LEGACY(gethostbyname, TT_FORK|TT_NEED_BASE|TT_NEED_DNS|TT_OFF_BY_DEFAULT), + DNS_LEGACY(gethostbyname6, TT_FORK|TT_NEED_BASE|TT_NEED_DNS|TT_OFF_BY_DEFAULT), + DNS_LEGACY(gethostbyaddr, TT_FORK|TT_NEED_BASE|TT_NEED_DNS|TT_OFF_BY_DEFAULT), + { "resolve_reverse", dns_resolve_reverse, TT_FORK|TT_OFF_BY_DEFAULT, NULL, NULL }, + { "search", dns_search_test, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + { "search_lower", dns_search_lower_test, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + { "search_cancel", dns_search_cancel_test, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + { "retry", dns_retry_test, TT_FORK|TT_NEED_BASE|TT_NO_LOGS, &basic_setup, NULL }, + { "retry_disable_when_inactive", dns_retry_disable_when_inactive_test, + TT_FORK|TT_NEED_BASE|TT_NO_LOGS, &basic_setup, NULL }, + { "reissue", dns_reissue_test, TT_FORK|TT_NEED_BASE|TT_NO_LOGS, &basic_setup, NULL }, + { "reissue_disable_when_inactive", dns_reissue_disable_when_inactive_test, + TT_FORK|TT_NEED_BASE|TT_NO_LOGS, &basic_setup, NULL }, + { "inflight", dns_inflight_test, TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + { "bufferevent_connect_hostname", test_bufferevent_connect_hostname, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + { "disable_when_inactive", dns_disable_when_inactive_test, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + { "disable_when_inactive_no_ns", dns_disable_when_inactive_no_ns_test, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + + { "getaddrinfo_async", test_getaddrinfo_async, + TT_FORK|TT_NEED_BASE, &basic_setup, (char*)"" }, + { "getaddrinfo_cancel_stress", test_getaddrinfo_async_cancel_stress, + TT_FORK, NULL, NULL }, + +#ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED + { "leak_shutdown", test_dbg_leak_shutdown, TT_FORK, &testleak_funcs, NULL }, + { "leak_cancel", test_dbg_leak_cancel, TT_FORK, &testleak_funcs, NULL }, + + { "leak_resume", test_dbg_leak_resume_, TT_FORK, &testleak_funcs, NULL }, + { "leak_cancel_and_resume", test_dbg_leak_cancel_and_resume_, + TT_FORK, &testleak_funcs, NULL }, + { "leak_resume_send_err", test_dbg_leak_resume_send_err_, + TT_FORK, &testleak_funcs, NULL }, + { "leak_cancel_and_resume_send_err", test_dbg_leak_cancel_and_resume_send_err_, + TT_FORK, &testleak_funcs, NULL }, +#endif + + { "client_fail_requests", dns_client_fail_requests_test, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + { "client_fail_requests_getaddrinfo", + dns_client_fail_requests_getaddrinfo_test, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + + END_OF_TESTCASES +}; + diff --git a/libs/libevent/docs/test/regress_et.c b/libs/libevent/docs/test/regress_et.c new file mode 100644 index 0000000000..229a78e2d4 --- /dev/null +++ b/libs/libevent/docs/test/regress_et.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "../util-internal.h" +#include "event2/event-config.h" + +#ifdef _WIN32 +#include <winsock2.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#ifndef _WIN32 +#include <sys/time.h> +#include <unistd.h> +#endif +#include <errno.h> + +#include "event2/event.h" +#include "event2/util.h" + +#include "regress.h" + +static int was_et = 0; + +static void +read_cb(evutil_socket_t fd, short event, void *arg) +{ + char buf; + int len; + + len = recv(fd, &buf, sizeof(buf), 0); + + called++; + if (event & EV_ET) + was_et = 1; + + if (!len) + event_del(arg); +} + +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + +#ifdef _WIN32 +#define LOCAL_SOCKETPAIR_AF AF_INET +#else +#define LOCAL_SOCKETPAIR_AF AF_UNIX +#endif + +static void +test_edgetriggered(void *et) +{ + struct event *ev = NULL; + struct event_base *base = NULL; + const char *test = "test string"; + evutil_socket_t pair[2] = {-1,-1}; + int supports_et; + + /* On Linux 3.2.1 (at least, as patched by Fedora and tested by Nick), + * doing a "recv" on an AF_UNIX socket resets the readability of the + * socket, even though there is no state change, so we don't actually + * get edge-triggered behavior. Yuck! Linux 3.1.9 didn't have this + * problem. + */ +#ifdef __linux__ + if (evutil_ersatz_socketpair_(AF_INET, SOCK_STREAM, 0, pair) == -1) { + tt_abort_perror("socketpair"); + } +#else + if (evutil_socketpair(LOCAL_SOCKETPAIR_AF, SOCK_STREAM, 0, pair) == -1) { + tt_abort_perror("socketpair"); + } +#endif + + called = was_et = 0; + + tt_int_op(send(pair[0], test, (int)strlen(test)+1, 0), >, 0); + shutdown(pair[0], SHUT_WR); + + /* Initalize the event library */ + base = event_base_new(); + + if (!strcmp(event_base_get_method(base), "epoll") || + !strcmp(event_base_get_method(base), "epoll (with changelist)") || + !strcmp(event_base_get_method(base), "kqueue")) + supports_et = 1; + else + supports_et = 0; + + TT_BLATHER(("Checking for edge-triggered events with %s, which should %s" + "support edge-triggering", event_base_get_method(base), + supports_et?"":"not ")); + + /* Initalize one event */ + ev = event_new(base, pair[1], EV_READ|EV_ET|EV_PERSIST, read_cb, &ev); + + event_add(ev, NULL); + + /* We're going to call the dispatch function twice. The first invocation + * will read a single byte from pair[1] in either case. If we're edge + * triggered, we'll only see the event once (since we only see transitions + * from no data to data), so the second invocation of event_base_loop will + * do nothing. If we're level triggered, the second invocation of + * event_base_loop will also activate the event (because there's still + * data to read). */ + event_base_loop(base,EVLOOP_NONBLOCK|EVLOOP_ONCE); + event_base_loop(base,EVLOOP_NONBLOCK|EVLOOP_ONCE); + + if (supports_et) { + tt_int_op(called, ==, 1); + tt_assert(was_et); + } else { + tt_int_op(called, ==, 2); + tt_assert(!was_et); + } + + end: + if (ev) { + event_del(ev); + event_free(ev); + } + if (base) + event_base_free(base); + evutil_closesocket(pair[0]); + evutil_closesocket(pair[1]); +} + +static void +test_edgetriggered_mix_error(void *data_) +{ + struct basic_test_data *data = data_; + struct event_base *base = NULL; + struct event *ev_et=NULL, *ev_lt=NULL; + +#ifdef EVENT__DISABLE_DEBUG_MODE + if (1) + tt_skip(); +#endif + + if (!libevent_tests_running_in_debug_mode) + event_enable_debug_mode(); + + base = event_base_new(); + + /* try mixing edge-triggered and level-triggered to make sure it fails*/ + ev_et = event_new(base, data->pair[0], EV_READ|EV_ET, read_cb, ev_et); + tt_assert(ev_et); + ev_lt = event_new(base, data->pair[0], EV_READ, read_cb, ev_lt); + tt_assert(ev_lt); + + /* Add edge-triggered, then level-triggered. Get an error. */ + tt_int_op(0, ==, event_add(ev_et, NULL)); + tt_int_op(-1, ==, event_add(ev_lt, NULL)); + tt_int_op(EV_READ, ==, event_pending(ev_et, EV_READ, NULL)); + tt_int_op(0, ==, event_pending(ev_lt, EV_READ, NULL)); + + tt_int_op(0, ==, event_del(ev_et)); + /* Add level-triggered, then edge-triggered. Get an error. */ + tt_int_op(0, ==, event_add(ev_lt, NULL)); + tt_int_op(-1, ==, event_add(ev_et, NULL)); + tt_int_op(EV_READ, ==, event_pending(ev_lt, EV_READ, NULL)); + tt_int_op(0, ==, event_pending(ev_et, EV_READ, NULL)); + +end: + if (ev_et) + event_free(ev_et); + if (ev_lt) + event_free(ev_lt); + if (base) + event_base_free(base); +} + +struct testcase_t edgetriggered_testcases[] = { + { "et", test_edgetriggered, TT_FORK, NULL, NULL }, + { "et_mix_error", test_edgetriggered_mix_error, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NO_LOGS, &basic_setup, NULL }, + END_OF_TESTCASES +}; diff --git a/libs/libevent/docs/test/regress_finalize.c b/libs/libevent/docs/test/regress_finalize.c new file mode 100644 index 0000000000..552210fe9d --- /dev/null +++ b/libs/libevent/docs/test/regress_finalize.c @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2013 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ + +#include "event2/event-config.h" +#include "evconfig-private.h" +#include "tinytest.h" +#include "tinytest_macros.h" +#include <stdlib.h> + +#include "event2/event.h" +#include "event2/util.h" +#include "event-internal.h" +#include "defer-internal.h" + +#include "regress.h" +#include "regress_thread.h" + +static void +timer_callback(evutil_socket_t fd, short what, void *arg) +{ + int *int_arg = arg; + *int_arg += 1; + (void)fd; + (void)what; +} +static void +simple_callback(struct event_callback *evcb, void *arg) +{ + int *int_arg = arg; + *int_arg += 1; + (void)evcb; +} +static void +event_finalize_callback_1(struct event *ev, void *arg) +{ + int *int_arg = arg; + *int_arg += 100; + (void)ev; +} +static void +callback_finalize_callback_1(struct event_callback *evcb, void *arg) +{ + int *int_arg = arg; + *int_arg += 100; + (void)evcb; +} + + +static void +test_fin_cb_invoked(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + + struct event *ev; + struct event ev2; + struct event_callback evcb; + int cb_called = 0; + int ev_called = 0; + + const struct timeval ten_sec = {10,0}; + + event_deferred_cb_init_(&evcb, 0, simple_callback, &cb_called); + ev = evtimer_new(base, timer_callback, &ev_called); + /* Just finalize them; don't bother adding. */ + event_free_finalize(0, ev, event_finalize_callback_1); + event_callback_finalize_(base, 0, &evcb, callback_finalize_callback_1); + + event_base_dispatch(base); + + tt_int_op(cb_called, ==, 100); + tt_int_op(ev_called, ==, 100); + + ev_called = cb_called = 0; + event_base_assert_ok_(base); + + /* Now try it when they're active. (actually, don't finalize: make + * sure activation can happen! */ + ev = evtimer_new(base, timer_callback, &ev_called); + event_deferred_cb_init_(&evcb, 0, simple_callback, &cb_called); + + event_active(ev, EV_TIMEOUT, 1); + event_callback_activate_(base, &evcb); + + event_base_dispatch(base); + tt_int_op(cb_called, ==, 1); + tt_int_op(ev_called, ==, 1); + + ev_called = cb_called = 0; + event_base_assert_ok_(base); + + /* Great, it worked. Now activate and finalize and make sure only + * finalizing happens. */ + event_active(ev, EV_TIMEOUT, 1); + event_callback_activate_(base, &evcb); + event_free_finalize(0, ev, event_finalize_callback_1); + event_callback_finalize_(base, 0, &evcb, callback_finalize_callback_1); + + event_base_dispatch(base); + tt_int_op(cb_called, ==, 100); + tt_int_op(ev_called, ==, 100); + + ev_called = 0; + + event_base_assert_ok_(base); + + /* Okay, now add but don't have it become active, and make sure *that* + * works. */ + ev = evtimer_new(base, timer_callback, &ev_called); + event_add(ev, &ten_sec); + event_free_finalize(0, ev, event_finalize_callback_1); + + event_base_dispatch(base); + tt_int_op(ev_called, ==, 100); + + ev_called = 0; + event_base_assert_ok_(base); + + /* Now try adding and deleting after finalizing. */ + ev = evtimer_new(base, timer_callback, &ev_called); + evtimer_assign(&ev2, base, timer_callback, &ev_called); + event_add(ev, &ten_sec); + event_free_finalize(0, ev, event_finalize_callback_1); + event_finalize(0, &ev2, event_finalize_callback_1); + + event_add(&ev2, &ten_sec); + event_del(ev); + event_active(&ev2, EV_TIMEOUT, 1); + + event_base_dispatch(base); + tt_int_op(ev_called, ==, 200); + + event_base_assert_ok_(base); + +end: + ; +} + +#ifndef EVENT__DISABLE_MM_REPLACEMENT +static void * +tfff_malloc(size_t n) +{ + return malloc(n); +} +static void *tfff_p1=NULL, *tfff_p2=NULL; +static int tfff_p1_freed=0, tfff_p2_freed=0; +static void +tfff_free(void *p) +{ + if (! p) + return; + if (p == tfff_p1) + ++tfff_p1_freed; + if (p == tfff_p2) + ++tfff_p2_freed; + free(p); +} +static void * +tfff_realloc(void *p, size_t sz) +{ + return realloc(p,sz); +} +#endif + +static void +test_fin_free_finalize(void *arg) +{ +#ifdef EVENT__DISABLE_MM_REPLACEMENT + tinytest_set_test_skipped_(); +#else + struct event_base *base = NULL; + struct event *ev, *ev2; + int ev_called = 0; + int ev2_called = 0; + + (void)arg; + + event_set_mem_functions(tfff_malloc, tfff_realloc, tfff_free); + + base = event_base_new(); + tt_assert(base); + + ev = evtimer_new(base, timer_callback, &ev_called); + ev2 = evtimer_new(base, timer_callback, &ev2_called); + tfff_p1 = ev; + tfff_p2 = ev2; + event_free_finalize(0, ev, event_finalize_callback_1); + event_finalize(0, ev2, event_finalize_callback_1); + + event_base_dispatch(base); + + tt_int_op(ev_called, ==, 100); + tt_int_op(ev2_called, ==, 100); + + event_base_assert_ok_(base); + tt_int_op(tfff_p1_freed, ==, 1); + tt_int_op(tfff_p2_freed, ==, 0); + + event_free(ev2); + +end: + if (base) + event_base_free(base); +#endif +} + +/* For test_fin_within_cb */ +struct event_and_count { + struct event *ev; + struct event *ev2; + int count; +}; +static void +event_finalize_callback_2(struct event *ev, void *arg) +{ + struct event_and_count *evc = arg; + evc->count += 100; + event_free(ev); +} +static void +timer_callback_2(evutil_socket_t fd, short what, void *arg) +{ + struct event_and_count *evc = arg; + event_finalize(0, evc->ev, event_finalize_callback_2); + event_finalize(0, evc->ev2, event_finalize_callback_2); + ++ evc->count; + (void)fd; + (void)what; +} + +static void +test_fin_within_cb(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + + struct event_and_count evc1, evc2; + evc1.count = evc2.count = 0; + evc2.ev2 = evc1.ev = evtimer_new(base, timer_callback_2, &evc1); + evc1.ev2 = evc2.ev = evtimer_new(base, timer_callback_2, &evc2); + + /* Activate both. The first one will have its callback run, which + * will finalize both of them, preventing the second one's callback + * from running. */ + event_active(evc1.ev, EV_TIMEOUT, 1); + event_active(evc2.ev, EV_TIMEOUT, 1); + + event_base_dispatch(base); + tt_int_op(evc1.count, ==, 101); + tt_int_op(evc2.count, ==, 100); + + event_base_assert_ok_(base); + /* Now try with EV_PERSIST events. */ + evc1.count = evc2.count = 0; + evc2.ev2 = evc1.ev = event_new(base, -1, EV_PERSIST, timer_callback_2, &evc1); + evc1.ev2 = evc2.ev = event_new(base, -1, EV_PERSIST, timer_callback_2, &evc2); + + event_active(evc1.ev, EV_TIMEOUT, 1); + event_active(evc2.ev, EV_TIMEOUT, 1); + + event_base_dispatch(base); + tt_int_op(evc1.count, ==, 101); + tt_int_op(evc2.count, ==, 100); + + event_base_assert_ok_(base); +end: + ; +} + +#if 0 +static void +timer_callback_3(evutil_socket_t *fd, short what, void *arg) +{ + (void)fd; + (void)what; + +} +static void +test_fin_many(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + + struct event *ev1, *ev2; + struct event_callback evcb1, evcb2; + int ev1_count = 0, ev2_count = 0; + int evcb1_count = 0, evcb2_count = 0; + struct event_callback *array[4]; + + int n; + + /* First attempt: call finalize_many with no events running */ + ev1 = evtimer_new(base, timer_callback, &ev1_count); + ev1 = evtimer_new(base, timer_callback, &ev2_count); + event_deferred_cb_init_(&evcb1, 0, simple_callback, &evcb1_called); + event_deferred_cb_init_(&evcb2, 0, simple_callback, &evcb2_called); + array[0] = &ev1->ev_evcallback; + array[1] = &ev2->ev_evcallback; + array[2] = &evcb1; + array[3] = &evcb2; + + + + n = event_callback_finalize_many(base, 4, array, + callback_finalize_callback_1); + +} +#endif + + +#define TEST(name, flags) \ + { #name, test_fin_##name, (flags), &basic_setup, NULL } + +struct testcase_t finalize_testcases[] = { + + TEST(cb_invoked, TT_FORK|TT_NEED_BASE), + TEST(free_finalize, TT_FORK), + TEST(within_cb, TT_FORK|TT_NEED_BASE), +// TEST(many, TT_FORK|TT_NEED_BASE), + + + END_OF_TESTCASES +}; + diff --git a/libs/libevent/docs/test/regress_http.c b/libs/libevent/docs/test/regress_http.c new file mode 100644 index 0000000000..cbe7aea34c --- /dev/null +++ b/libs/libevent/docs/test/regress_http.c @@ -0,0 +1,4335 @@ +/* + * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "util-internal.h" + +#ifdef _WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#include <windows.h> +#endif + +#include "event2/event-config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/queue.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <signal.h> +#include <unistd.h> +#include <netdb.h> +#endif +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "event2/dns.h" + +#include "event2/event.h" +#include "event2/http.h" +#include "event2/buffer.h" +#include "event2/bufferevent.h" +#include "event2/bufferevent_ssl.h" +#include "event2/util.h" +#include "event2/listener.h" +#include "log-internal.h" +#include "http-internal.h" +#include "regress.h" +#include "regress_testutils.h" + +static struct evhttp *http; +/* set if a test needs to call loopexit on a base */ +static struct event_base *exit_base; + +static char const BASIC_REQUEST_BODY[] = "This is funny"; + +#define IMPL_HTTP_REQUEST_ERROR_CB(name, expecting_error) \ + static void \ + http_request_error_cb_with_##name##_(enum evhttp_request_error error, \ + void *arg) \ + { \ + if (error != expecting_error) { \ + fprintf(stderr, "FAILED\n"); \ + exit(1); \ + } \ + test_ok = 1; \ + } +IMPL_HTTP_REQUEST_ERROR_CB(cancel, EVREQ_HTTP_REQUEST_CANCEL) + +static void http_basic_cb(struct evhttp_request *req, void *arg); +static void http_large_cb(struct evhttp_request *req, void *arg); +static void http_chunked_cb(struct evhttp_request *req, void *arg); +static void http_post_cb(struct evhttp_request *req, void *arg); +static void http_put_cb(struct evhttp_request *req, void *arg); +static void http_delete_cb(struct evhttp_request *req, void *arg); +static void http_delay_cb(struct evhttp_request *req, void *arg); +static void http_large_delay_cb(struct evhttp_request *req, void *arg); +static void http_badreq_cb(struct evhttp_request *req, void *arg); +static void http_dispatcher_cb(struct evhttp_request *req, void *arg); +static void http_on_complete_cb(struct evhttp_request *req, void *arg); + +#define HTTP_BIND_IPV6 1 +#define HTTP_BIND_SSL 2 +static int +http_bind(struct evhttp *myhttp, ev_uint16_t *pport, int mask) +{ + int port; + struct evhttp_bound_socket *sock; + int ipv6 = mask & HTTP_BIND_IPV6; + + if (ipv6) + sock = evhttp_bind_socket_with_handle(myhttp, "::1", *pport); + else + sock = evhttp_bind_socket_with_handle(myhttp, "127.0.0.1", *pport); + + if (sock == NULL) { + if (ipv6) + return -1; + else + event_errx(1, "Could not start web server"); + } + + port = regress_get_socket_port(evhttp_bound_socket_get_fd(sock)); + if (port < 0) + return -1; + *pport = (ev_uint16_t) port; + + return 0; +} + +#ifdef EVENT__HAVE_OPENSSL +static struct bufferevent * +https_bev(struct event_base *base, void *arg) +{ + SSL *ssl = SSL_new(get_ssl_ctx()); + + SSL_use_certificate(ssl, ssl_getcert()); + SSL_use_PrivateKey(ssl, ssl_getkey()); + + return bufferevent_openssl_socket_new( + base, -1, ssl, BUFFEREVENT_SSL_ACCEPTING, + BEV_OPT_CLOSE_ON_FREE); +} +#endif +static struct evhttp * +http_setup(ev_uint16_t *pport, struct event_base *base, int mask) +{ + struct evhttp *myhttp; + + /* Try a few different ports */ + myhttp = evhttp_new(base); + + if (http_bind(myhttp, pport, mask) < 0) + return NULL; +#ifdef EVENT__HAVE_OPENSSL + if (mask & HTTP_BIND_SSL) { + init_ssl(); + evhttp_set_bevcb(myhttp, https_bev, NULL); + } +#endif + + /* Register a callback for certain types of requests */ + evhttp_set_cb(myhttp, "/test", http_basic_cb, base); + evhttp_set_cb(myhttp, "/large", http_large_cb, base); + evhttp_set_cb(myhttp, "/chunked", http_chunked_cb, base); + evhttp_set_cb(myhttp, "/streamed", http_chunked_cb, base); + evhttp_set_cb(myhttp, "/postit", http_post_cb, base); + evhttp_set_cb(myhttp, "/putit", http_put_cb, base); + evhttp_set_cb(myhttp, "/deleteit", http_delete_cb, base); + evhttp_set_cb(myhttp, "/delay", http_delay_cb, base); + evhttp_set_cb(myhttp, "/largedelay", http_large_delay_cb, base); + evhttp_set_cb(myhttp, "/badrequest", http_badreq_cb, base); + evhttp_set_cb(myhttp, "/oncomplete", http_on_complete_cb, base); + evhttp_set_cb(myhttp, "/", http_dispatcher_cb, base); + return (myhttp); +} + +#ifndef NI_MAXSERV +#define NI_MAXSERV 1024 +#endif + +static evutil_socket_t +http_connect(const char *address, unsigned short port) +{ + /* Stupid code for connecting */ + struct evutil_addrinfo ai, *aitop; + char strport[NI_MAXSERV]; + + struct sockaddr *sa; + int slen; + evutil_socket_t fd; + + memset(&ai, 0, sizeof(ai)); + ai.ai_family = AF_INET; + ai.ai_socktype = SOCK_STREAM; + evutil_snprintf(strport, sizeof(strport), "%d", port); + if (evutil_getaddrinfo(address, strport, &ai, &aitop) != 0) { + event_warn("getaddrinfo"); + return (-1); + } + sa = aitop->ai_addr; + slen = aitop->ai_addrlen; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) + event_err(1, "socket failed"); + + evutil_make_socket_nonblocking(fd); + if (connect(fd, sa, slen) == -1) { +#ifdef _WIN32 + int tmp_err = WSAGetLastError(); + if (tmp_err != WSAEINPROGRESS && tmp_err != WSAEINVAL && + tmp_err != WSAEWOULDBLOCK) + event_err(1, "connect failed"); +#else + if (errno != EINPROGRESS) + event_err(1, "connect failed"); +#endif + } + + evutil_freeaddrinfo(aitop); + + return (fd); +} + +/* Helper: do a strcmp on the contents of buf and the string s. */ +static int +evbuffer_datacmp(struct evbuffer *buf, const char *s) +{ + size_t b_sz = evbuffer_get_length(buf); + size_t s_sz = strlen(s); + unsigned char *d; + int r; + + if (b_sz < s_sz) + return -1; + + d = evbuffer_pullup(buf, s_sz); + if ((r = memcmp(d, s, s_sz))) + return r; + + if (b_sz > s_sz) + return 1; + else + return 0; +} + +/* Helper: Return true iff buf contains s */ +static int +evbuffer_contains(struct evbuffer *buf, const char *s) +{ + struct evbuffer_ptr ptr; + ptr = evbuffer_search(buf, s, strlen(s), NULL); + return ptr.pos != -1; +} + +static void +http_readcb(struct bufferevent *bev, void *arg) +{ + const char *what = BASIC_REQUEST_BODY; + struct event_base *my_base = arg; + + if (evbuffer_contains(bufferevent_get_input(bev), what)) { + struct evhttp_request *req = evhttp_request_new(NULL, NULL); + enum message_read_status done; + + /* req->kind = EVHTTP_RESPONSE; */ + done = evhttp_parse_firstline_(req, bufferevent_get_input(bev)); + if (done != ALL_DATA_READ) + goto out; + + done = evhttp_parse_headers_(req, bufferevent_get_input(bev)); + if (done != ALL_DATA_READ) + goto out; + + if (done == 1 && + evhttp_find_header(evhttp_request_get_input_headers(req), + "Content-Type") != NULL) + test_ok++; + + out: + evhttp_request_free(req); + bufferevent_disable(bev, EV_READ); + if (exit_base) + event_base_loopexit(exit_base, NULL); + else if (my_base) + event_base_loopexit(my_base, NULL); + else { + fprintf(stderr, "No way to exit loop!\n"); + exit(1); + } + } +} + +static void +http_writecb(struct bufferevent *bev, void *arg) +{ + if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) { + /* enable reading of the reply */ + bufferevent_enable(bev, EV_READ); + test_ok++; + } +} + +static void +http_errorcb(struct bufferevent *bev, short what, void *arg) +{ + /** For ssl */ + if (what & BEV_EVENT_CONNECTED) + return; + test_ok = -2; + event_base_loopexit(arg, NULL); +} + +static int found_multi = 0; +static int found_multi2 = 0; + +static void +http_basic_cb(struct evhttp_request *req, void *arg) +{ + struct evbuffer *evb = evbuffer_new(); + struct evhttp_connection *evcon; + int empty = evhttp_find_header(evhttp_request_get_input_headers(req), "Empty") != NULL; + event_debug(("%s: called\n", __func__)); + evbuffer_add_printf(evb, BASIC_REQUEST_BODY); + + evcon = evhttp_request_get_connection(req); + tt_assert(evhttp_connection_get_server(evcon) == http); + + /* For multi-line headers test */ + { + const char *multi = + evhttp_find_header(evhttp_request_get_input_headers(req),"X-Multi"); + if (multi) { + found_multi = !strcmp(multi,"aaaaaaaa a END"); + if (strcmp("END", multi + strlen(multi) - 3) == 0) + test_ok++; + if (evhttp_find_header(evhttp_request_get_input_headers(req), "X-Last")) + test_ok++; + } + } + { + const char *multi2 = + evhttp_find_header(evhttp_request_get_input_headers(req),"X-Multi-Extra-WS"); + if (multi2) { + found_multi2 = !strcmp(multi2,"libevent 2.1"); + } + } + + + /* injecting a bad content-length */ + if (evhttp_find_header(evhttp_request_get_input_headers(req), "X-Negative")) + evhttp_add_header(evhttp_request_get_output_headers(req), + "Content-Length", "-100"); + + /* allow sending of an empty reply */ + evhttp_send_reply(req, HTTP_OK, "Everything is fine", + !empty ? evb : NULL); + +end: + evbuffer_free(evb); +} + +static void +http_large_cb(struct evhttp_request *req, void *arg) +{ + struct evbuffer *evb = evbuffer_new(); + int i; + + for (i = 0; i < 1<<20; ++i) { + evbuffer_add_printf(evb, BASIC_REQUEST_BODY); + } + evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); + evbuffer_free(evb); +} + +static char const* const CHUNKS[] = { + "This is funny", + "but not hilarious.", + "bwv 1052" +}; + +struct chunk_req_state { + struct event_base *base; + struct evhttp_request *req; + int i; +}; + +static void +http_chunked_trickle_cb(evutil_socket_t fd, short events, void *arg) +{ + struct evbuffer *evb = evbuffer_new(); + struct chunk_req_state *state = arg; + struct timeval when = { 0, 0 }; + + evbuffer_add_printf(evb, "%s", CHUNKS[state->i]); + evhttp_send_reply_chunk(state->req, evb); + evbuffer_free(evb); + + if (++state->i < (int) (sizeof(CHUNKS)/sizeof(CHUNKS[0]))) { + event_base_once(state->base, -1, EV_TIMEOUT, + http_chunked_trickle_cb, state, &when); + } else { + evhttp_send_reply_end(state->req); + free(state); + } +} + +static void +http_chunked_cb(struct evhttp_request *req, void *arg) +{ + struct timeval when = { 0, 0 }; + struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state)); + event_debug(("%s: called\n", __func__)); + + memset(state, 0, sizeof(struct chunk_req_state)); + state->req = req; + state->base = arg; + + if (strcmp(evhttp_request_get_uri(req), "/streamed") == 0) { + evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Length", "39"); + } + + /* generate a chunked/streamed reply */ + evhttp_send_reply_start(req, HTTP_OK, "Everything is fine"); + + /* but trickle it across several iterations to ensure we're not + * assuming it comes all at once */ + event_base_once(arg, -1, EV_TIMEOUT, http_chunked_trickle_cb, state, &when); +} + +static void +http_complete_write(evutil_socket_t fd, short what, void *arg) +{ + struct bufferevent *bev = arg; + const char *http_request = "host\r\n" + "Connection: close\r\n" + "\r\n"; + bufferevent_write(bev, http_request, strlen(http_request)); +} + +static struct bufferevent * +create_bev(struct event_base *base, int fd, int ssl) +{ + int flags = BEV_OPT_DEFER_CALLBACKS; + struct bufferevent *bev = NULL; + + if (!ssl) { + bev = bufferevent_socket_new(base, fd, flags); + } else { +#ifdef EVENT__HAVE_OPENSSL + SSL *ssl = SSL_new(get_ssl_ctx()); + bev = bufferevent_openssl_socket_new( + base, fd, ssl, BUFFEREVENT_SSL_CONNECTING, flags); + bufferevent_openssl_set_allow_dirty_shutdown(bev, 1); +#endif + } + + return bev; +} + +static void +http_basic_test_impl(void *arg, int ssl) +{ + struct basic_test_data *data = arg; + struct timeval tv; + struct bufferevent *bev = NULL; + evutil_socket_t fd; + const char *http_request; + ev_uint16_t port = 0, port2 = 0; + int server_flags = ssl ? HTTP_BIND_SSL : 0; + + test_ok = 0; + + http = http_setup(&port, data->base, server_flags); + + /* bind to a second socket */ + if (http_bind(http, &port2, server_flags) == -1) { + fprintf(stdout, "FAILED (bind)\n"); + exit(1); + } + + fd = http_connect("127.0.0.1", port); + + /* Stupid thing to send a request */ + bev = create_bev(data->base, fd, ssl); + bufferevent_setcb(bev, http_readcb, http_writecb, + http_errorcb, data->base); + + /* first half of the http request */ + http_request = + "GET /test HTTP/1.1\r\n" + "Host: some"; + + bufferevent_write(bev, http_request, strlen(http_request)); + evutil_timerclear(&tv); + tv.tv_usec = 10000; + event_base_once(data->base, + -1, EV_TIMEOUT, http_complete_write, bev, &tv); + + event_base_dispatch(data->base); + + tt_assert(test_ok == 3); + + /* connect to the second port */ + bufferevent_free(bev); + evutil_closesocket(fd); + + fd = http_connect("127.0.0.1", port2); + + /* Stupid thing to send a request */ + bev = create_bev(data->base, fd, ssl); + bufferevent_setcb(bev, http_readcb, http_writecb, + http_errorcb, data->base); + + http_request = + "GET /test HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + event_base_dispatch(data->base); + + tt_assert(test_ok == 5); + + /* Connect to the second port again. This time, send an absolute uri. */ + bufferevent_free(bev); + evutil_closesocket(fd); + + fd = http_connect("127.0.0.1", port2); + + /* Stupid thing to send a request */ + bev = create_bev(data->base, fd, ssl); + bufferevent_setcb(bev, http_readcb, http_writecb, + http_errorcb, data->base); + + http_request = + "GET http://somehost.net/test HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + event_base_dispatch(data->base); + + tt_assert(test_ok == 7); + + evhttp_free(http); + end: + if (bev) + bufferevent_free(bev); +} +static void http_basic_test(void *arg) +{ return http_basic_test_impl(arg, 0); } + + +static void +http_delay_reply(evutil_socket_t fd, short what, void *arg) +{ + struct evhttp_request *req = arg; + + evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL); + + ++test_ok; +} + +static void +http_delay_cb(struct evhttp_request *req, void *arg) +{ + struct timeval tv; + evutil_timerclear(&tv); + tv.tv_sec = 0; + tv.tv_usec = 200 * 1000; + + event_base_once(arg, -1, EV_TIMEOUT, http_delay_reply, req, &tv); +} + +static void +http_badreq_cb(struct evhttp_request *req, void *arg) +{ + struct evbuffer *buf = evbuffer_new(); + + evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/xml; charset=UTF-8"); + evbuffer_add_printf(buf, "Hello, %s!", "127.0.0.1"); + + evhttp_send_reply(req, HTTP_OK, "OK", buf); + evbuffer_free(buf); +} + +static void +http_badreq_errorcb(struct bufferevent *bev, short what, void *arg) +{ + event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); + /* ignore */ +} + +#ifndef SHUT_WR +#ifdef _WIN32 +#define SHUT_WR SD_SEND +#else +#define SHUT_WR 1 +#endif +#endif + +static void +http_badreq_readcb(struct bufferevent *bev, void *arg) +{ + const char *what = "Hello, 127.0.0.1"; + const char *bad_request = "400 Bad Request"; + + if (evbuffer_contains(bufferevent_get_input(bev), bad_request)) { + TT_FAIL(("%s:bad request detected", __func__)); + bufferevent_disable(bev, EV_READ); + event_base_loopexit(arg, NULL); + return; + } + + if (evbuffer_contains(bufferevent_get_input(bev), what)) { + struct evhttp_request *req = evhttp_request_new(NULL, NULL); + enum message_read_status done; + + /* req->kind = EVHTTP_RESPONSE; */ + done = evhttp_parse_firstline_(req, bufferevent_get_input(bev)); + if (done != ALL_DATA_READ) + goto out; + + done = evhttp_parse_headers_(req, bufferevent_get_input(bev)); + if (done != ALL_DATA_READ) + goto out; + + if (done == 1 && + evhttp_find_header(evhttp_request_get_input_headers(req), + "Content-Type") != NULL) + test_ok++; + + out: + evhttp_request_free(req); + evbuffer_drain(bufferevent_get_input(bev), evbuffer_get_length(bufferevent_get_input(bev))); + } + + shutdown(bufferevent_getfd(bev), SHUT_WR); +} + +static void +http_badreq_successcb(evutil_socket_t fd, short what, void *arg) +{ + event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); + event_base_loopexit(exit_base, NULL); +} + +static void +http_bad_request_test(void *arg) +{ + struct basic_test_data *data = arg; + struct timeval tv; + struct bufferevent *bev = NULL; + evutil_socket_t fd = -1; + const char *http_request; + ev_uint16_t port=0, port2=0; + + test_ok = 0; + exit_base = data->base; + + http = http_setup(&port, data->base, 0); + + /* bind to a second socket */ + if (http_bind(http, &port2, 0) == -1) + TT_DIE(("Bind socket failed")); + + /* NULL request test */ + fd = http_connect("127.0.0.1", port); + tt_int_op(fd, >=, 0); + + /* Stupid thing to send a request */ + bev = bufferevent_socket_new(data->base, fd, 0); + bufferevent_setcb(bev, http_badreq_readcb, http_writecb, + http_badreq_errorcb, data->base); + bufferevent_enable(bev, EV_READ); + + /* real NULL request */ + http_request = ""; + + bufferevent_write(bev, http_request, strlen(http_request)); + + shutdown(fd, SHUT_WR); + timerclear(&tv); + tv.tv_usec = 10000; + event_base_once(data->base, -1, EV_TIMEOUT, http_badreq_successcb, bev, &tv); + + event_base_dispatch(data->base); + + bufferevent_free(bev); + evutil_closesocket(fd); + + if (test_ok != 0) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* Second answer (BAD REQUEST) on connection close */ + + /* connect to the second port */ + fd = http_connect("127.0.0.1", port2); + + /* Stupid thing to send a request */ + bev = bufferevent_socket_new(data->base, fd, 0); + bufferevent_setcb(bev, http_badreq_readcb, http_writecb, + http_badreq_errorcb, data->base); + bufferevent_enable(bev, EV_READ); + + /* first half of the http request */ + http_request = + "GET /badrequest HTTP/1.0\r\n" \ + "Connection: Keep-Alive\r\n" \ + "\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + timerclear(&tv); + tv.tv_usec = 10000; + event_base_once(data->base, -1, EV_TIMEOUT, http_badreq_successcb, bev, &tv); + + event_base_dispatch(data->base); + + tt_int_op(test_ok, ==, 2); + +end: + evhttp_free(http); + if (bev) + bufferevent_free(bev); + if (fd >= 0) + evutil_closesocket(fd); +} + +static struct evhttp_connection *delayed_client; + +static void +http_large_delay_cb(struct evhttp_request *req, void *arg) +{ + struct timeval tv; + evutil_timerclear(&tv); + tv.tv_usec = 500000; + + event_base_once(arg, -1, EV_TIMEOUT, http_delay_reply, req, &tv); + evhttp_connection_fail_(delayed_client, EVREQ_HTTP_EOF); +} + +/* + * HTTP DELETE test, just piggyback on the basic test + */ + +static void +http_delete_cb(struct evhttp_request *req, void *arg) +{ + struct evbuffer *evb = evbuffer_new(); + int empty = evhttp_find_header(evhttp_request_get_input_headers(req), "Empty") != NULL; + + /* Expecting a DELETE request */ + if (evhttp_request_get_command(req) != EVHTTP_REQ_DELETE) { + fprintf(stdout, "FAILED (delete type)\n"); + exit(1); + } + + event_debug(("%s: called\n", __func__)); + evbuffer_add_printf(evb, BASIC_REQUEST_BODY); + + /* allow sending of an empty reply */ + evhttp_send_reply(req, HTTP_OK, "Everything is fine", + !empty ? evb : NULL); + + evbuffer_free(evb); +} + +static void +http_delete_test(void *arg) +{ + struct basic_test_data *data = arg; + struct bufferevent *bev; + evutil_socket_t fd = -1; + const char *http_request; + ev_uint16_t port = 0; + + test_ok = 0; + + http = http_setup(&port, data->base, 0); + + tt_assert(http); + fd = http_connect("127.0.0.1", port); + tt_int_op(fd, >=, 0); + + /* Stupid thing to send a request */ + bev = bufferevent_socket_new(data->base, fd, 0); + bufferevent_setcb(bev, http_readcb, http_writecb, + http_errorcb, data->base); + + http_request = + "DELETE /deleteit HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + event_base_dispatch(data->base); + + bufferevent_free(bev); + evutil_closesocket(fd); + fd = -1; + + evhttp_free(http); + + tt_int_op(test_ok, ==, 2); + end: + if (fd >= 0) + evutil_closesocket(fd); +} + +static void +http_sent_cb(struct evhttp_request *req, void *arg) +{ + ev_uintptr_t val = (ev_uintptr_t)arg; + struct evbuffer *b; + + if (val != 0xDEADBEEF) { + fprintf(stdout, "FAILED on_complete_cb argument\n"); + exit(1); + } + + b = evhttp_request_get_output_buffer(req); + if (evbuffer_get_length(b) != 0) { + fprintf(stdout, "FAILED on_complete_cb output buffer not written\n"); + exit(1); + } + + event_debug(("%s: called\n", __func__)); + + ++test_ok; +} + +static void +http_on_complete_cb(struct evhttp_request *req, void *arg) +{ + struct evbuffer *evb = evbuffer_new(); + + evhttp_request_set_on_complete_cb(req, http_sent_cb, (void *)0xDEADBEEF); + + event_debug(("%s: called\n", __func__)); + evbuffer_add_printf(evb, BASIC_REQUEST_BODY); + + /* allow sending of an empty reply */ + evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); + + evbuffer_free(evb); + + ++test_ok; +} + +static void +http_on_complete_test(void *arg) +{ + struct basic_test_data *data = arg; + struct bufferevent *bev; + evutil_socket_t fd = -1; + const char *http_request; + ev_uint16_t port = 0; + + test_ok = 0; + + http = http_setup(&port, data->base, 0); + + fd = http_connect("127.0.0.1", port); + tt_int_op(fd, >=, 0); + + /* Stupid thing to send a request */ + bev = bufferevent_socket_new(data->base, fd, 0); + bufferevent_setcb(bev, http_readcb, http_writecb, + http_errorcb, data->base); + + http_request = + "GET /oncomplete HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + event_base_dispatch(data->base); + + bufferevent_free(bev); + + evhttp_free(http); + + tt_int_op(test_ok, ==, 4); + end: + if (fd >= 0) + evutil_closesocket(fd); +} + +static void +http_allowed_methods_eventcb(struct bufferevent *bev, short what, void *arg) +{ + char **output = arg; + if ((what & (BEV_EVENT_ERROR|BEV_EVENT_EOF))) { + char buf[4096]; + int n; + n = evbuffer_remove(bufferevent_get_input(bev), buf, + sizeof(buf)-1); + if (n >= 0) { + buf[n]='\0'; + if (*output) + free(*output); + *output = strdup(buf); + } + event_base_loopexit(exit_base, NULL); + } +} + +static void +http_allowed_methods_test(void *arg) +{ + struct basic_test_data *data = arg; + struct bufferevent *bev1, *bev2, *bev3; + evutil_socket_t fd1=-1, fd2=-1, fd3=-1; + const char *http_request; + char *result1=NULL, *result2=NULL, *result3=NULL; + ev_uint16_t port = 0; + + exit_base = data->base; + test_ok = 0; + + http = http_setup(&port, data->base, 0); + + fd1 = http_connect("127.0.0.1", port); + tt_int_op(fd1, >=, 0); + + /* GET is out; PATCH is in. */ + evhttp_set_allowed_methods(http, EVHTTP_REQ_PATCH); + + /* Stupid thing to send a request */ + bev1 = bufferevent_socket_new(data->base, fd1, 0); + bufferevent_enable(bev1, EV_READ|EV_WRITE); + bufferevent_setcb(bev1, NULL, NULL, + http_allowed_methods_eventcb, &result1); + + http_request = + "GET /index.html HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "\r\n"; + + bufferevent_write(bev1, http_request, strlen(http_request)); + + event_base_dispatch(data->base); + + fd2 = http_connect("127.0.0.1", port); + tt_int_op(fd2, >=, 0); + + bev2 = bufferevent_socket_new(data->base, fd2, 0); + bufferevent_enable(bev2, EV_READ|EV_WRITE); + bufferevent_setcb(bev2, NULL, NULL, + http_allowed_methods_eventcb, &result2); + + http_request = + "PATCH /test HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "\r\n"; + + bufferevent_write(bev2, http_request, strlen(http_request)); + + event_base_dispatch(data->base); + + fd3 = http_connect("127.0.0.1", port); + tt_int_op(fd3, >=, 0); + + bev3 = bufferevent_socket_new(data->base, fd3, 0); + bufferevent_enable(bev3, EV_READ|EV_WRITE); + bufferevent_setcb(bev3, NULL, NULL, + http_allowed_methods_eventcb, &result3); + + http_request = + "FLOOP /test HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "\r\n"; + + bufferevent_write(bev3, http_request, strlen(http_request)); + + event_base_dispatch(data->base); + + bufferevent_free(bev1); + bufferevent_free(bev2); + bufferevent_free(bev3); + + evhttp_free(http); + + /* Method known but disallowed */ + tt_assert(result1); + tt_assert(!strncmp(result1, "HTTP/1.1 501 ", strlen("HTTP/1.1 501 "))); + + /* Method known and allowed */ + tt_assert(result2); + tt_assert(!strncmp(result2, "HTTP/1.1 200 ", strlen("HTTP/1.1 200 "))); + + /* Method unknown */ + tt_assert(result3); + tt_assert(!strncmp(result3, "HTTP/1.1 501 ", strlen("HTTP/1.1 501 "))); + + end: + if (result1) + free(result1); + if (result2) + free(result2); + if (result3) + free(result3); + if (fd1 >= 0) + evutil_closesocket(fd1); + if (fd2 >= 0) + evutil_closesocket(fd2); + if (fd3 >= 0) + evutil_closesocket(fd3); +} + +static void http_request_no_action_done(struct evhttp_request *, void *); +static void http_request_done(struct evhttp_request *, void *); +static void http_request_empty_done(struct evhttp_request *, void *); + +static void +http_connection_test_(struct basic_test_data *data, int persistent, + const char *address, struct evdns_base *dnsbase, int ipv6, int family) +{ + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + test_ok = 0; + + http = http_setup(&port, data->base, ipv6); + if (!http && ipv6) { + tt_skip(); + } + tt_assert(http); + + evcon = evhttp_connection_base_new(data->base, dnsbase, address, port); + tt_assert(evcon); + evhttp_connection_set_family(evcon, family); + + tt_assert(evhttp_connection_get_base(evcon) == data->base); + + exit_base = data->base; + + tt_assert(evhttp_connection_get_server(evcon) == NULL); + + /* + * At this point, we want to schedule a request to the HTTP + * server using our make request method. + */ + req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_base_dispatch(data->base); + + tt_assert(test_ok); + + /* try to make another request over the same connection */ + test_ok = 0; + + req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + /* + * if our connections are not supposed to be persistent; request + * a close from the server. + */ + if (!persistent) + evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("couldn't make request"); + } + + event_base_dispatch(data->base); + + /* make another request: request empty reply */ + test_ok = 0; + + req = evhttp_request_new(http_request_empty_done, data->base); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); +} + +static void +http_connection_test(void *arg) +{ + http_connection_test_(arg, 0, "127.0.0.1", NULL, 0, AF_UNSPEC); +} +static void +http_persist_connection_test(void *arg) +{ + http_connection_test_(arg, 1, "127.0.0.1", NULL, 0, AF_UNSPEC); +} + +static struct regress_dns_server_table search_table[] = { + { "localhost", "A", "127.0.0.1", 0, 0 }, + { NULL, NULL, NULL, 0, 0 } +}; + +static void +http_connection_async_test(void *arg) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + struct evdns_base *dns_base = NULL; + ev_uint16_t portnum = 0; + char address[64]; + + exit_base = data->base; + tt_assert(regress_dnsserver(data->base, &portnum, search_table)); + + dns_base = evdns_base_new(data->base, 0/* init name servers */); + tt_assert(dns_base); + + /* Add ourself as the only nameserver, and make sure we really are + * the only nameserver. */ + evutil_snprintf(address, sizeof(address), "127.0.0.1:%d", portnum); + evdns_base_nameserver_ip_add(dns_base, address); + + test_ok = 0; + + http = http_setup(&port, data->base, 0); + + evcon = evhttp_connection_base_new(data->base, dns_base, "127.0.0.1", port); + tt_assert(evcon); + + /* + * At this point, we want to schedule a request to the HTTP + * server using our make request method. + */ + + req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_base_dispatch(data->base); + + tt_assert(test_ok); + + /* try to make another request over the same connection */ + test_ok = 0; + + req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + /* + * if our connections are not supposed to be persistent; request + * a close from the server. + */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("couldn't make request"); + } + + event_base_dispatch(data->base); + + /* make another request: request empty reply */ + test_ok = 0; + + req = evhttp_request_new(http_request_empty_done, data->base); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); + if (dns_base) + evdns_base_free(dns_base, 0); + regress_clean_dnsserver(); +} + +static void +http_autofree_connection_test(void *arg) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req[2] = { NULL }; + + test_ok = 0; + http = http_setup(&port, data->base, 0); + + evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + tt_assert(evcon); + + /* + * At this point, we want to schedule two request to the HTTP + * server using our make request method. + */ + req[0] = evhttp_request_new(http_request_empty_done, data->base); + req[1] = evhttp_request_new(http_request_empty_done, data->base); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Host", "somehost"); + evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Connection", "close"); + evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Empty", "itis"); + evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Host", "somehost"); + evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Connection", "close"); + evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Empty", "itis"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req[0], EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("couldn't make request"); + } + if (evhttp_make_request(evcon, req[1], EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("couldn't make request"); + } + + /* + * Tell libevent to free the connection when the request completes + * We then set the evcon pointer to NULL since we don't want to free it + * when this function ends. + */ + evhttp_connection_free_on_completion(evcon); + evcon = NULL; + + event_base_dispatch(data->base); + + /* at this point, the http server should have no connection */ + tt_assert(TAILQ_FIRST(&http->connections) == NULL); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); +} + +static void +http_request_never_call(struct evhttp_request *req, void *arg) +{ + fprintf(stdout, "FAILED\n"); + exit(1); +} + +static void +http_do_cancel(evutil_socket_t fd, short what, void *arg) +{ + struct evhttp_request *req = arg; + struct timeval tv; + struct event_base *base; + evutil_timerclear(&tv); + tv.tv_sec = 0; + tv.tv_usec = 500 * 1000; + + base = evhttp_connection_get_base(evhttp_request_get_connection(req)); + evhttp_cancel_request(req); + + event_base_loopexit(base, &tv); + + ++test_ok; +} + +static void +http_cancel_test(void *arg) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + struct timeval tv; + + exit_base = data->base; + + test_ok = 0; + + http = http_setup(&port, data->base, 0); + + evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + tt_assert(evcon); + + /* + * At this point, we want to schedule a request to the HTTP + * server using our make request method. + */ + + req = evhttp_request_new(http_request_never_call, NULL); + evhttp_request_set_error_cb(req, http_request_error_cb_with_cancel_); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + /* We give ownership of the request to the connection */ + tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/delay"), + !=, -1); + + evutil_timerclear(&tv); + tv.tv_sec = 0; + tv.tv_usec = 100 * 1000; + + event_base_once(data->base, -1, EV_TIMEOUT, http_do_cancel, req, &tv); + + event_base_dispatch(data->base); + + tt_int_op(test_ok, ==, 3); + + /* try to make another request over the same connection */ + test_ok = 0; + + req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + /* We give ownership of the request to the connection */ + tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test"), + !=, -1); + + event_base_dispatch(data->base); + + /* make another request: request empty reply */ + test_ok = 0; + + req = evhttp_request_new(http_request_empty_done, data->base); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis"); + + /* We give ownership of the request to the connection */ + tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test"), + !=, -1); + + event_base_dispatch(data->base); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); +} + +static void +http_request_no_action_done(struct evhttp_request *req, void *arg) +{ + EVUTIL_ASSERT(exit_base); + event_base_loopexit(exit_base, NULL); +} + +static void +http_request_done(struct evhttp_request *req, void *arg) +{ + const char *what = arg; + + if (evhttp_request_get_response_code(req) != HTTP_OK) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + test_ok = 1; + EVUTIL_ASSERT(exit_base); + event_base_loopexit(exit_base, NULL); +} + +static void +http_request_expect_error(struct evhttp_request *req, void *arg) +{ + if (evhttp_request_get_response_code(req) == HTTP_OK) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + test_ok = 1; + EVUTIL_ASSERT(arg); + event_base_loopexit(arg, NULL); +} + +/* test virtual hosts */ +static void +http_virtual_host_test(void *arg) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + struct evhttp *second = NULL, *third = NULL; + evutil_socket_t fd; + struct bufferevent *bev; + const char *http_request; + + exit_base = data->base; + + http = http_setup(&port, data->base, 0); + + /* virtual host */ + second = evhttp_new(NULL); + evhttp_set_cb(second, "/funnybunny", http_basic_cb, NULL); + third = evhttp_new(NULL); + evhttp_set_cb(third, "/blackcoffee", http_basic_cb, NULL); + + if (evhttp_add_virtual_host(http, "foo.com", second) == -1) { + tt_abort_msg("Couldn't add vhost"); + } + + if (evhttp_add_virtual_host(http, "bar.*.foo.com", third) == -1) { + tt_abort_msg("Couldn't add wildcarded vhost"); + } + + /* add some aliases to the vhosts */ + tt_assert(evhttp_add_server_alias(second, "manolito.info") == 0); + tt_assert(evhttp_add_server_alias(third, "bonkers.org") == 0); + + evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + tt_assert(evcon); + + /* make a request with a different host and expect an error */ + req = evhttp_request_new(http_request_expect_error, data->base); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, + "/funnybunny") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + tt_assert(test_ok == 1); + + test_ok = 0; + + /* make a request with the right host and expect a response */ + req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "foo.com"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, + "/funnybunny") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_base_dispatch(data->base); + + tt_assert(test_ok == 1); + + test_ok = 0; + + /* make a request with the right host and expect a response */ + req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "bar.magic.foo.com"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, + "/blackcoffee") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + tt_assert(test_ok == 1) + + test_ok = 0; + + /* make a request with the right host and expect a response */ + req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "manolito.info"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, + "/funnybunny") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + tt_assert(test_ok == 1) + + test_ok = 0; + + /* make a request with the right host and expect a response */ + req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); + + /* Add the Host header. This time with the optional port. */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "bonkers.org:8000"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, + "/blackcoffee") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + tt_assert(test_ok == 1) + + test_ok = 0; + + /* Now make a raw request with an absolute URI. */ + fd = http_connect("127.0.0.1", port); + + /* Stupid thing to send a request */ + bev = bufferevent_socket_new(data->base, fd, 0); + bufferevent_setcb(bev, http_readcb, http_writecb, + http_errorcb, NULL); + + /* The host in the URI should override the Host: header */ + http_request = + "GET http://manolito.info/funnybunny HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + event_base_dispatch(data->base); + + tt_int_op(test_ok, ==, 2); + + bufferevent_free(bev); + evutil_closesocket(fd); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); +} + + +/* test date header and content length */ + +static void +http_request_empty_done(struct evhttp_request *req, void *arg) +{ + if (evhttp_request_get_response_code(req) != HTTP_OK) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evhttp_find_header(evhttp_request_get_input_headers(req), "Date") == NULL) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + + if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Length") == NULL) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (strcmp(evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Length"), + "0")) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != 0) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + test_ok = 1; + EVUTIL_ASSERT(arg); + event_base_loopexit(arg, NULL); +} + +/* + * HTTP DISPATCHER test + */ + +void +http_dispatcher_cb(struct evhttp_request *req, void *arg) +{ + + struct evbuffer *evb = evbuffer_new(); + event_debug(("%s: called\n", __func__)); + evbuffer_add_printf(evb, "DISPATCHER_TEST"); + + evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); + + evbuffer_free(evb); +} + +static void +http_dispatcher_test_done(struct evhttp_request *req, void *arg) +{ + struct event_base *base = arg; + const char *what = "DISPATCHER_TEST"; + + if (!req) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evhttp_request_get_response_code(req) != HTTP_OK) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) { + fprintf(stderr, "FAILED (content type)\n"); + exit(1); + } + + if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) { + fprintf(stderr, "FAILED (length %lu vs %lu)\n", + (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(what)); + exit(1); + } + + if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) { + fprintf(stderr, "FAILED (data)\n"); + exit(1); + } + + test_ok = 1; + event_base_loopexit(base, NULL); +} + +static void +http_dispatcher_test(void *arg) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + test_ok = 0; + + http = http_setup(&port, data->base, 0); + + evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + tt_assert(evcon); + + /* also bind to local host */ + evhttp_connection_set_local_address(evcon, "127.0.0.1"); + + /* + * At this point, we want to schedule an HTTP GET request + * server using our make request method. + */ + + req = evhttp_request_new(http_dispatcher_test_done, data->base); + tt_assert(req); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); +} + +/* + * HTTP POST test. + */ + +void http_postrequest_done(struct evhttp_request *, void *); + +#define POST_DATA "Okay. Not really printf" + +static void +http_post_test(void *arg) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + test_ok = 0; + + http = http_setup(&port, data->base, 0); + + evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + tt_assert(evcon); + + /* + * At this point, we want to schedule an HTTP POST request + * server using our make request method. + */ + + req = evhttp_request_new(http_postrequest_done, data->base); + tt_assert(req); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + evbuffer_add_printf(evhttp_request_get_output_buffer(req), POST_DATA); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + tt_int_op(test_ok, ==, 1); + + test_ok = 0; + + req = evhttp_request_new(http_postrequest_done, data->base); + tt_assert(req); + + /* Now try with 100-continue. */ + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + evhttp_add_header(evhttp_request_get_output_headers(req), "Expect", "100-continue"); + evbuffer_add_printf(evhttp_request_get_output_buffer(req), POST_DATA); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + tt_int_op(test_ok, ==, 1); + + evhttp_connection_free(evcon); + evhttp_free(http); + + end: + ; +} + +void +http_post_cb(struct evhttp_request *req, void *arg) +{ + struct evbuffer *evb; + event_debug(("%s: called\n", __func__)); + + /* Yes, we are expecting a post request */ + if (evhttp_request_get_command(req) != EVHTTP_REQ_POST) { + fprintf(stdout, "FAILED (post type)\n"); + exit(1); + } + + if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(POST_DATA)) { + fprintf(stdout, "FAILED (length: %lu vs %lu)\n", + (unsigned long) evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long) strlen(POST_DATA)); + exit(1); + } + + if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), POST_DATA) != 0) { + fprintf(stdout, "FAILED (data)\n"); + fprintf(stdout, "Got :%s\n", evbuffer_pullup(evhttp_request_get_input_buffer(req),-1)); + fprintf(stdout, "Want:%s\n", POST_DATA); + exit(1); + } + + evb = evbuffer_new(); + evbuffer_add_printf(evb, BASIC_REQUEST_BODY); + + evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); + + evbuffer_free(evb); +} + +void +http_postrequest_done(struct evhttp_request *req, void *arg) +{ + const char *what = BASIC_REQUEST_BODY; + struct event_base *base = arg; + + if (req == NULL) { + fprintf(stderr, "FAILED (timeout)\n"); + exit(1); + } + + if (evhttp_request_get_response_code(req) != HTTP_OK) { + + fprintf(stderr, "FAILED (response code)\n"); + exit(1); + } + + if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) { + fprintf(stderr, "FAILED (content type)\n"); + exit(1); + } + + if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) { + fprintf(stderr, "FAILED (length %lu vs %lu)\n", + (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(what)); + exit(1); + } + + if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) { + fprintf(stderr, "FAILED (data)\n"); + exit(1); + } + + test_ok = 1; + event_base_loopexit(base, NULL); +} + +/* + * HTTP PUT test, basically just like POST, but ... + */ + +void http_putrequest_done(struct evhttp_request *, void *); + +#define PUT_DATA "Hi, I'm some PUT data" + +static void +http_put_test(void *arg) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + test_ok = 0; + + http = http_setup(&port, data->base, 0); + + evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + tt_assert(evcon); + + /* + * Schedule the HTTP PUT request + */ + + req = evhttp_request_new(http_putrequest_done, data->base); + tt_assert(req); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "someotherhost"); + evbuffer_add_printf(evhttp_request_get_output_buffer(req), PUT_DATA); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_PUT, "/putit") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + evhttp_connection_free(evcon); + evhttp_free(http); + + tt_int_op(test_ok, ==, 1); + end: + ; +} + +void +http_put_cb(struct evhttp_request *req, void *arg) +{ + struct evbuffer *evb; + event_debug(("%s: called\n", __func__)); + + /* Expecting a PUT request */ + if (evhttp_request_get_command(req) != EVHTTP_REQ_PUT) { + fprintf(stdout, "FAILED (put type)\n"); + exit(1); + } + + if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(PUT_DATA)) { + fprintf(stdout, "FAILED (length: %lu vs %lu)\n", + (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(PUT_DATA)); + exit(1); + } + + if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), PUT_DATA) != 0) { + fprintf(stdout, "FAILED (data)\n"); + fprintf(stdout, "Got :%s\n", evbuffer_pullup(evhttp_request_get_input_buffer(req),-1)); + fprintf(stdout, "Want:%s\n", PUT_DATA); + exit(1); + } + + evb = evbuffer_new(); + evbuffer_add_printf(evb, "That ain't funny"); + + evhttp_send_reply(req, HTTP_OK, "Everything is great", evb); + + evbuffer_free(evb); +} + +void +http_putrequest_done(struct evhttp_request *req, void *arg) +{ + struct event_base *base = arg; + const char *what = "That ain't funny"; + + if (req == NULL) { + fprintf(stderr, "FAILED (timeout)\n"); + exit(1); + } + + if (evhttp_request_get_response_code(req) != HTTP_OK) { + + fprintf(stderr, "FAILED (response code)\n"); + exit(1); + } + + if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) { + fprintf(stderr, "FAILED (content type)\n"); + exit(1); + } + + if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) { + fprintf(stderr, "FAILED (length %lu vs %lu)\n", + (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(what)); + exit(1); + } + + + if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) { + fprintf(stderr, "FAILED (data)\n"); + exit(1); + } + + test_ok = 1; + event_base_loopexit(base, NULL); +} + +static void +http_failure_readcb(struct bufferevent *bev, void *arg) +{ + const char *what = "400 Bad Request"; + if (evbuffer_contains(bufferevent_get_input(bev), what)) { + test_ok = 2; + bufferevent_disable(bev, EV_READ); + event_base_loopexit(arg, NULL); + } +} + +/* + * Testing that the HTTP server can deal with a malformed request. + */ +static void +http_failure_test(void *arg) +{ + struct basic_test_data *data = arg; + struct bufferevent *bev; + evutil_socket_t fd = -1; + const char *http_request; + ev_uint16_t port = 0; + + test_ok = 0; + + http = http_setup(&port, data->base, 0); + + fd = http_connect("127.0.0.1", port); + tt_int_op(fd, >=, 0); + + /* Stupid thing to send a request */ + bev = bufferevent_socket_new(data->base, fd, 0); + bufferevent_setcb(bev, http_failure_readcb, http_writecb, + http_errorcb, data->base); + + http_request = "illegal request\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + event_base_dispatch(data->base); + + bufferevent_free(bev); + + evhttp_free(http); + + tt_int_op(test_ok, ==, 2); + end: + if (fd >= 0) + evutil_closesocket(fd); +} + +static void +close_detect_done(struct evhttp_request *req, void *arg) +{ + struct timeval tv; + tt_assert(req); + tt_assert(evhttp_request_get_response_code(req) == HTTP_OK); + + test_ok = 1; + + end: + evutil_timerclear(&tv); + tv.tv_usec = 150000; + event_base_loopexit(arg, &tv); +} + +static void +close_detect_launch(evutil_socket_t fd, short what, void *arg) +{ + struct evhttp_connection *evcon = arg; + struct event_base *base = evhttp_connection_get_base(evcon); + struct evhttp_request *req; + + req = evhttp_request_new(close_detect_done, base); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + tt_fail_msg("Couldn't make request"); + } +} + +static void +close_detect_cb(struct evhttp_request *req, void *arg) +{ + struct evhttp_connection *evcon = arg; + struct event_base *base = evhttp_connection_get_base(evcon); + struct timeval tv; + + if (req != NULL && evhttp_request_get_response_code(req) != HTTP_OK) { + tt_abort_msg("Failed"); + } + + evutil_timerclear(&tv); + tv.tv_sec = 0; /* longer than the http time out */ + tv.tv_usec = 600000; /* longer than the http time out */ + + /* launch a new request on the persistent connection in .3 seconds */ + event_base_once(base, -1, EV_TIMEOUT, close_detect_launch, evcon, &tv); + end: + ; +} + + +static void +http_close_detection_(struct basic_test_data *data, int with_delay) +{ + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + const struct timeval sec_tenth = { 0, 100000 }; + + test_ok = 0; + http = http_setup(&port, data->base, 0); + + /* .1 second timeout */ + evhttp_set_timeout_tv(http, &sec_tenth); + + evcon = evhttp_connection_base_new(data->base, NULL, + "127.0.0.1", port); + tt_assert(evcon); + evhttp_connection_set_timeout_tv(evcon, &sec_tenth); + + + tt_assert(evcon); + delayed_client = evcon; + + /* + * At this point, we want to schedule a request to the HTTP + * server using our make request method. + */ + + req = evhttp_request_new(close_detect_cb, evcon); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, + req, EVHTTP_REQ_GET, with_delay ? "/largedelay" : "/test") == -1) { + tt_abort_msg("couldn't make request"); + } + + event_base_dispatch(data->base); + + /* at this point, the http server should have no connection */ + tt_assert(TAILQ_FIRST(&http->connections) == NULL); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); +} +static void +http_close_detection_test(void *arg) +{ + http_close_detection_(arg, 0); +} +static void +http_close_detection_delay_test(void *arg) +{ + http_close_detection_(arg, 1); +} + +static void +http_highport_test(void *arg) +{ + struct basic_test_data *data = arg; + int i = -1; + struct evhttp *myhttp = NULL; + + /* Try a few different ports */ + for (i = 0; i < 50; ++i) { + myhttp = evhttp_new(data->base); + if (evhttp_bind_socket(myhttp, "127.0.0.1", 65535 - i) == 0) { + test_ok = 1; + evhttp_free(myhttp); + return; + } + evhttp_free(myhttp); + } + + tt_fail_msg("Couldn't get a high port"); +} + +static void +http_bad_header_test(void *ptr) +{ + struct evkeyvalq headers; + + TAILQ_INIT(&headers); + + tt_want(evhttp_add_header(&headers, "One", "Two") == 0); + tt_want(evhttp_add_header(&headers, "One", "Two\r\n Three") == 0); + tt_want(evhttp_add_header(&headers, "One\r", "Two") == -1); + tt_want(evhttp_add_header(&headers, "One\n", "Two") == -1); + tt_want(evhttp_add_header(&headers, "One", "Two\r") == -1); + tt_want(evhttp_add_header(&headers, "One", "Two\n") == -1); + + evhttp_clear_headers(&headers); +} + +static int validate_header( + const struct evkeyvalq* headers, + const char *key, const char *value) +{ + const char *real_val = evhttp_find_header(headers, key); + tt_assert(real_val != NULL); + tt_want(strcmp(real_val, value) == 0); +end: + return (0); +} + +static void +http_parse_query_test(void *ptr) +{ + struct evkeyvalq headers; + int r; + + TAILQ_INIT(&headers); + + r = evhttp_parse_query("http://www.test.com/?q=test", &headers); + tt_want(validate_header(&headers, "q", "test") == 0); + tt_int_op(r, ==, 0); + evhttp_clear_headers(&headers); + + r = evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers); + tt_want(validate_header(&headers, "q", "test") == 0); + tt_want(validate_header(&headers, "foo", "bar") == 0); + tt_int_op(r, ==, 0); + evhttp_clear_headers(&headers); + + r = evhttp_parse_query("http://www.test.com/?q=test+foo", &headers); + tt_want(validate_header(&headers, "q", "test foo") == 0); + tt_int_op(r, ==, 0); + evhttp_clear_headers(&headers); + + r = evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers); + tt_want(validate_header(&headers, "q", "test\nfoo") == 0); + tt_int_op(r, ==, 0); + evhttp_clear_headers(&headers); + + r = evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers); + tt_want(validate_header(&headers, "q", "test\rfoo") == 0); + tt_int_op(r, ==, 0); + evhttp_clear_headers(&headers); + + r = evhttp_parse_query("http://www.test.com/?q=test&&q2", &headers); + tt_int_op(r, ==, -1); + evhttp_clear_headers(&headers); + + r = evhttp_parse_query("http://www.test.com/?q=test+this", &headers); + tt_want(validate_header(&headers, "q", "test this") == 0); + tt_int_op(r, ==, 0); + evhttp_clear_headers(&headers); + + r = evhttp_parse_query("http://www.test.com/?q=test&q2=foo", &headers); + tt_int_op(r, ==, 0); + tt_want(validate_header(&headers, "q", "test") == 0); + tt_want(validate_header(&headers, "q2", "foo") == 0); + evhttp_clear_headers(&headers); + + r = evhttp_parse_query("http://www.test.com/?q&q2=foo", &headers); + tt_int_op(r, ==, -1); + evhttp_clear_headers(&headers); + + r = evhttp_parse_query("http://www.test.com/?q=foo&q2", &headers); + tt_int_op(r, ==, -1); + evhttp_clear_headers(&headers); + + r = evhttp_parse_query("http://www.test.com/?q=foo&q2&q3=x", &headers); + tt_int_op(r, ==, -1); + evhttp_clear_headers(&headers); + + r = evhttp_parse_query("http://www.test.com/?q=&q2=&q3=", &headers); + tt_int_op(r, ==, 0); + tt_want(validate_header(&headers, "q", "") == 0); + tt_want(validate_header(&headers, "q2", "") == 0); + tt_want(validate_header(&headers, "q3", "") == 0); + evhttp_clear_headers(&headers); + +end: + evhttp_clear_headers(&headers); +} + +static void +http_parse_uri_test(void *ptr) +{ + const int nonconform = (ptr != NULL); + const unsigned parse_flags = + nonconform ? EVHTTP_URI_NONCONFORMANT : 0; + struct evhttp_uri *uri = NULL; + char url_tmp[4096]; +#define URI_PARSE(uri) \ + evhttp_uri_parse_with_flags((uri), parse_flags) + +#define TT_URI(want) do { \ + char *ret = evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)); \ + tt_want(ret != NULL); \ + tt_want(ret == url_tmp); \ + if (strcmp(ret,want) != 0) \ + TT_FAIL(("\"%s\" != \"%s\"",ret,want)); \ + } while(0) + + tt_want(evhttp_uri_join(NULL, 0, 0) == NULL); + tt_want(evhttp_uri_join(NULL, url_tmp, 0) == NULL); + tt_want(evhttp_uri_join(NULL, url_tmp, sizeof(url_tmp)) == NULL); + + /* bad URIs: parsing */ +#define BAD(s) do { \ + if (URI_PARSE(s) != NULL) \ + TT_FAIL(("Expected error parsing \"%s\"",s)); \ + } while(0) + /* Nonconformant URIs we can parse: parsing */ +#define NCF(s) do { \ + uri = URI_PARSE(s); \ + if (uri != NULL && !nonconform) { \ + TT_FAIL(("Expected error parsing \"%s\"",s)); \ + } else if (uri == NULL && nonconform) { \ + TT_FAIL(("Couldn't parse nonconformant URI \"%s\"", \ + s)); \ + } \ + if (uri) { \ + tt_want(evhttp_uri_join(uri, url_tmp, \ + sizeof(url_tmp))); \ + evhttp_uri_free(uri); \ + } \ + } while(0) + + NCF("http://www.test.com/ why hello"); + NCF("http://www.test.com/why-hello\x01"); + NCF("http://www.test.com/why-hello?\x01"); + NCF("http://www.test.com/why-hello#\x01"); + BAD("http://www.\x01.test.com/why-hello"); + BAD("http://www.%7test.com/why-hello"); + NCF("http://www.test.com/why-hell%7o"); + BAD("h%3ttp://www.test.com/why-hello"); + NCF("http://www.test.com/why-hello%7"); + NCF("http://www.test.com/why-hell%7o"); + NCF("http://www.test.com/foo?ba%r"); + NCF("http://www.test.com/foo#ba%r"); + BAD("99:99/foo"); + BAD("http://www.test.com:999x/"); + BAD("http://www.test.com:x/"); + BAD("http://[hello-there]/"); + BAD("http://[::1]]/"); + BAD("http://[::1/"); + BAD("http://[foob/"); + BAD("http://[/"); + BAD("http://[ffff:ffff:ffff:ffff:Ffff:ffff:ffff:" + "ffff:ffff:ffff:ffff:ffff:ffff:ffff]/"); + BAD("http://[vX.foo]/"); + BAD("http://[vX.foo]/"); + BAD("http://[v.foo]/"); + BAD("http://[v5.fo%o]/"); + BAD("http://[v5X]/"); + BAD("http://[v5]/"); + BAD("http://[]/"); + BAD("http://f\x01red@www.example.com/"); + BAD("http://f%0red@www.example.com/"); + BAD("http://www.example.com:9999999999999999999999999999999999999/"); + BAD("http://www.example.com:hihi/"); + BAD("://www.example.com/"); + + /* bad URIs: joining */ + uri = evhttp_uri_new(); + tt_want(0==evhttp_uri_set_host(uri, "www.example.com")); + tt_want(evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)) != NULL); + /* not enough space: */ + tt_want(evhttp_uri_join(uri, url_tmp, 3) == NULL); + /* host is set, but path doesn't start with "/": */ + tt_want(0==evhttp_uri_set_path(uri, "hi_mom")); + tt_want(evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)) == NULL); + tt_want(evhttp_uri_join(uri, NULL, sizeof(url_tmp))==NULL); + tt_want(evhttp_uri_join(uri, url_tmp, 0)==NULL); + evhttp_uri_free(uri); + uri = URI_PARSE("mailto:foo@bar"); + tt_want(uri != NULL); + tt_want(evhttp_uri_get_host(uri) == NULL); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(!strcmp(evhttp_uri_get_scheme(uri), "mailto")); + tt_want(!strcmp(evhttp_uri_get_path(uri), "foo@bar")); + tt_want(evhttp_uri_get_query(uri) == NULL); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("mailto:foo@bar"); + evhttp_uri_free(uri); + + uri = evhttp_uri_new(); + /* Bad URI usage: setting invalid values */ + tt_want(-1 == evhttp_uri_set_scheme(uri,"")); + tt_want(-1 == evhttp_uri_set_scheme(uri,"33")); + tt_want(-1 == evhttp_uri_set_scheme(uri,"hi!")); + tt_want(-1 == evhttp_uri_set_userinfo(uri,"hello@")); + tt_want(-1 == evhttp_uri_set_host(uri,"[1.2.3.4]")); + tt_want(-1 == evhttp_uri_set_host(uri,"[")); + tt_want(-1 == evhttp_uri_set_host(uri,"www.[foo].com")); + tt_want(-1 == evhttp_uri_set_port(uri,-3)); + tt_want(-1 == evhttp_uri_set_path(uri,"hello?world")); + tt_want(-1 == evhttp_uri_set_query(uri,"hello#world")); + tt_want(-1 == evhttp_uri_set_fragment(uri,"hello#world")); + /* Valid URI usage: setting valid values */ + tt_want(0 == evhttp_uri_set_scheme(uri,"http")); + tt_want(0 == evhttp_uri_set_scheme(uri,NULL)); + tt_want(0 == evhttp_uri_set_userinfo(uri,"username:pass")); + tt_want(0 == evhttp_uri_set_userinfo(uri,NULL)); + tt_want(0 == evhttp_uri_set_host(uri,"www.example.com")); + tt_want(0 == evhttp_uri_set_host(uri,"1.2.3.4")); + tt_want(0 == evhttp_uri_set_host(uri,"[1:2:3:4::]")); + tt_want(0 == evhttp_uri_set_host(uri,"[v7.wobblewobble]")); + tt_want(0 == evhttp_uri_set_host(uri,NULL)); + tt_want(0 == evhttp_uri_set_host(uri,"")); + tt_want(0 == evhttp_uri_set_port(uri, -1)); + tt_want(0 == evhttp_uri_set_port(uri, 80)); + tt_want(0 == evhttp_uri_set_port(uri, 65535)); + tt_want(0 == evhttp_uri_set_path(uri, "")); + tt_want(0 == evhttp_uri_set_path(uri, "/documents/public/index.html")); + tt_want(0 == evhttp_uri_set_path(uri, NULL)); + tt_want(0 == evhttp_uri_set_query(uri, "key=val&key2=val2")); + tt_want(0 == evhttp_uri_set_query(uri, "keyvalblarg")); + tt_want(0 == evhttp_uri_set_query(uri, "")); + tt_want(0 == evhttp_uri_set_query(uri, NULL)); + tt_want(0 == evhttp_uri_set_fragment(uri, "")); + tt_want(0 == evhttp_uri_set_fragment(uri, "here?i?am")); + tt_want(0 == evhttp_uri_set_fragment(uri, NULL)); + evhttp_uri_free(uri); + + /* Valid parsing */ + uri = URI_PARSE("http://www.test.com/?q=t%33est"); + tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); + tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); + tt_want(strcmp(evhttp_uri_get_query(uri), "q=t%33est") == 0); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("http://www.test.com/?q=t%33est"); + evhttp_uri_free(uri); + + uri = URI_PARSE("http://%77ww.test.com"); + tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "%77ww.test.com") == 0); + tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0); + tt_want(evhttp_uri_get_query(uri) == NULL); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("http://%77ww.test.com"); + evhttp_uri_free(uri); + + uri = URI_PARSE("http://www.test.com?q=test"); + tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); + tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0); + tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("http://www.test.com?q=test"); + evhttp_uri_free(uri); + + uri = URI_PARSE("http://www.test.com#fragment"); + tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); + tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0); + tt_want(evhttp_uri_get_query(uri) == NULL); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want_str_op(evhttp_uri_get_fragment(uri), ==, "fragment"); + TT_URI("http://www.test.com#fragment"); + evhttp_uri_free(uri); + + uri = URI_PARSE("http://8000/"); + tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "8000") == 0); + tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); + tt_want(evhttp_uri_get_query(uri) == NULL); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("http://8000/"); + evhttp_uri_free(uri); + + uri = URI_PARSE("http://:8000/"); + tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0); + tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); + tt_want(evhttp_uri_get_query(uri) == NULL); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == 8000); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("http://:8000/"); + evhttp_uri_free(uri); + + uri = URI_PARSE("http://www.test.com:/"); /* empty port */ + tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); + tt_want_str_op(evhttp_uri_get_path(uri), ==, "/"); + tt_want(evhttp_uri_get_query(uri) == NULL); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("http://www.test.com/"); + evhttp_uri_free(uri); + + uri = URI_PARSE("http://www.test.com:"); /* empty port 2 */ + tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); + tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0); + tt_want(evhttp_uri_get_query(uri) == NULL); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("http://www.test.com"); + evhttp_uri_free(uri); + + uri = URI_PARSE("ftp://www.test.com/?q=test"); + tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); + tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); + tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("ftp://www.test.com/?q=test"); + evhttp_uri_free(uri); + + uri = URI_PARSE("ftp://[::1]:999/?q=test"); + tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "[::1]") == 0); + tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); + tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == 999); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("ftp://[::1]:999/?q=test"); + evhttp_uri_free(uri); + + uri = URI_PARSE("ftp://[ff00::127.0.0.1]/?q=test"); + tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "[ff00::127.0.0.1]") == 0); + tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); + tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("ftp://[ff00::127.0.0.1]/?q=test"); + evhttp_uri_free(uri); + + uri = URI_PARSE("ftp://[v99.not_(any:time)_soon]/?q=test"); + tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "[v99.not_(any:time)_soon]") == 0); + tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); + tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("ftp://[v99.not_(any:time)_soon]/?q=test"); + evhttp_uri_free(uri); + + uri = URI_PARSE("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment"); + tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0); + tt_want(strcmp(evhttp_uri_get_userinfo(uri), "user:pass") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0); + tt_want(evhttp_uri_get_port(uri) == 42); + tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); + tt_want(strcmp(evhttp_uri_get_query(uri), "q=test&s=some+thing") == 0); + tt_want(strcmp(evhttp_uri_get_fragment(uri), "fragment") == 0); + TT_URI("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment"); + evhttp_uri_free(uri); + + uri = URI_PARSE("scheme://user@foo.com/#fragment"); + tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0); + tt_want(strcmp(evhttp_uri_get_userinfo(uri), "user") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); + tt_want(evhttp_uri_get_query(uri) == NULL); + tt_want(strcmp(evhttp_uri_get_fragment(uri), "fragment") == 0); + TT_URI("scheme://user@foo.com/#fragment"); + evhttp_uri_free(uri); + + uri = URI_PARSE("scheme://%75ser@foo.com/#frag@ment"); + tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0); + tt_want(strcmp(evhttp_uri_get_userinfo(uri), "%75ser") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); + tt_want(evhttp_uri_get_query(uri) == NULL); + tt_want(strcmp(evhttp_uri_get_fragment(uri), "frag@ment") == 0); + TT_URI("scheme://%75ser@foo.com/#frag@ment"); + evhttp_uri_free(uri); + + uri = URI_PARSE("file:///some/path/to/the/file"); + tt_want(strcmp(evhttp_uri_get_scheme(uri), "file") == 0); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(strcmp(evhttp_uri_get_path(uri), "/some/path/to/the/file") == 0); + tt_want(evhttp_uri_get_query(uri) == NULL); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("file:///some/path/to/the/file"); + evhttp_uri_free(uri); + + uri = URI_PARSE("///some/path/to/the-file"); + tt_want(uri != NULL); + tt_want(evhttp_uri_get_scheme(uri) == NULL); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(strcmp(evhttp_uri_get_path(uri), "/some/path/to/the-file") == 0); + tt_want(evhttp_uri_get_query(uri) == NULL); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("///some/path/to/the-file"); + evhttp_uri_free(uri); + + uri = URI_PARSE("/s:ome/path/to/the-file?q=99#fred"); + tt_want(uri != NULL); + tt_want(evhttp_uri_get_scheme(uri) == NULL); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_host(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(strcmp(evhttp_uri_get_path(uri), "/s:ome/path/to/the-file") == 0); + tt_want(strcmp(evhttp_uri_get_query(uri), "q=99") == 0); + tt_want(strcmp(evhttp_uri_get_fragment(uri), "fred") == 0); + TT_URI("/s:ome/path/to/the-file?q=99#fred"); + evhttp_uri_free(uri); + + uri = URI_PARSE("relative/path/with/co:lon"); + tt_want(uri != NULL); + tt_want(evhttp_uri_get_scheme(uri) == NULL); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_host(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(strcmp(evhttp_uri_get_path(uri), "relative/path/with/co:lon") == 0); + tt_want(evhttp_uri_get_query(uri) == NULL); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("relative/path/with/co:lon"); + evhttp_uri_free(uri); + + uri = URI_PARSE("bob?q=99&q2=q?33#fr?ed"); + tt_want(uri != NULL); + tt_want(evhttp_uri_get_scheme(uri) == NULL); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_host(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(strcmp(evhttp_uri_get_path(uri), "bob") == 0); + tt_want(strcmp(evhttp_uri_get_query(uri), "q=99&q2=q?33") == 0); + tt_want(strcmp(evhttp_uri_get_fragment(uri), "fr?ed") == 0); + TT_URI("bob?q=99&q2=q?33#fr?ed"); + evhttp_uri_free(uri); + + uri = URI_PARSE("#fr?ed"); + tt_want(uri != NULL); + tt_want(evhttp_uri_get_scheme(uri) == NULL); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_host(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0); + tt_want(evhttp_uri_get_query(uri) == NULL); + tt_want(strcmp(evhttp_uri_get_fragment(uri), "fr?ed") == 0); + TT_URI("#fr?ed"); + evhttp_uri_free(uri); +#undef URI_PARSE +#undef TT_URI +#undef BAD +} + +static void +http_uriencode_test(void *ptr) +{ + char *s=NULL, *s2=NULL; + size_t sz; + int bytes_decoded; + +#define ENC(from,want,plus) do { \ + s = evhttp_uriencode((from), -1, (plus)); \ + tt_assert(s); \ + tt_str_op(s,==,(want)); \ + sz = -1; \ + s2 = evhttp_uridecode((s), (plus), &sz); \ + tt_assert(s2); \ + tt_str_op(s2,==,(from)); \ + tt_int_op(sz,==,strlen(from)); \ + free(s); \ + free(s2); \ + s = s2 = NULL; \ + } while (0) + +#define DEC(from,want,dp) do { \ + s = evhttp_uridecode((from),(dp),&sz); \ + tt_assert(s); \ + tt_str_op(s,==,(want)); \ + tt_int_op(sz,==,strlen(want)); \ + free(s); \ + s = NULL; \ + } while (0) + +#define OLD_DEC(from,want) do { \ + s = evhttp_decode_uri((from)); \ + tt_assert(s); \ + tt_str_op(s,==,(want)); \ + free(s); \ + s = NULL; \ + } while (0) + + + ENC("Hello", "Hello",0); + ENC("99", "99",0); + ENC("", "",0); + ENC( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789-.~_", + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789-.~_",0); + ENC(" ", "%20",0); + ENC(" ", "+",1); + ENC("\xff\xf0\xe0", "%FF%F0%E0",0); + ENC("\x01\x19", "%01%19",1); + ENC("http://www.ietf.org/rfc/rfc3986.txt", + "http%3A%2F%2Fwww.ietf.org%2Frfc%2Frfc3986.txt",1); + + ENC("1+2=3", "1%2B2%3D3",1); + ENC("1+2=3", "1%2B2%3D3",0); + + /* Now try encoding with internal NULs. */ + s = evhttp_uriencode("hello\0world", 11, 0); + tt_assert(s); + tt_str_op(s,==,"hello%00world"); + free(s); + s = NULL; + + /* Now try decoding just part of string. */ + s = malloc(6 + 1 /* NUL byte */); + bytes_decoded = evhttp_decode_uri_internal("hello%20%20", 6, s, 0); + tt_assert(s); + tt_int_op(bytes_decoded,==,6); + tt_str_op(s,==,"hello%"); + free(s); + s = NULL; + + /* Now try out some decoding cases that we don't generate with + * encode_uri: Make sure that malformed stuff doesn't crash... */ + DEC("%%xhello th+ere \xff", + "%%xhello th+ere \xff", 0); + /* Make sure plus decoding works */ + DEC("plus+should%20work+", "plus should work ",1); + /* Try some lowercase hex */ + DEC("%f0%a0%b0", "\xf0\xa0\xb0",1); + + /* Try an internal NUL. */ + sz = 0; + s = evhttp_uridecode("%00%00x%00%00", 1, &sz); + tt_int_op(sz,==,5); + tt_assert(!memcmp(s, "\0\0x\0\0", 5)); + free(s); + s = NULL; + + /* Try with size == NULL */ + sz = 0; + s = evhttp_uridecode("%00%00x%00%00", 1, NULL); + tt_assert(!memcmp(s, "\0\0x\0\0", 5)); + free(s); + s = NULL; + + /* Test out the crazy old behavior of the deprecated + * evhttp_decode_uri */ + OLD_DEC("http://example.com/normal+path/?key=val+with+spaces", + "http://example.com/normal+path/?key=val with spaces"); + +end: + if (s) + free(s); + if (s2) + free(s2); +#undef ENC +#undef DEC +#undef OLD_DEC +} + +static void +http_base_test(void *ptr) +{ + struct event_base *base = NULL; + struct bufferevent *bev; + evutil_socket_t fd; + const char *http_request; + ev_uint16_t port = 0; + + test_ok = 0; + base = event_base_new(); + tt_assert(base); + http = http_setup(&port, base, 0); + + fd = http_connect("127.0.0.1", port); + tt_int_op(fd, >=, 0); + + /* Stupid thing to send a request */ + bev = bufferevent_socket_new(base, fd, 0); + bufferevent_setcb(bev, http_readcb, http_writecb, + http_errorcb, base); + bufferevent_base_set(base, bev); + + http_request = + "GET /test HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + event_base_dispatch(base); + + bufferevent_free(bev); + evutil_closesocket(fd); + + evhttp_free(http); + + tt_int_op(test_ok, ==, 2); + +end: + if (base) + event_base_free(base); +} + +/* + * the server is just going to close the connection if it times out during + * reading the headers. + */ + +static void +http_incomplete_readcb(struct bufferevent *bev, void *arg) +{ + test_ok = -1; + event_base_loopexit(exit_base,NULL); +} + +static void +http_incomplete_errorcb(struct bufferevent *bev, short what, void *arg) +{ + /** For ssl */ + if (what & BEV_EVENT_CONNECTED) + return; + + if (what == (BEV_EVENT_READING|BEV_EVENT_EOF)) + test_ok++; + else + test_ok = -2; + event_base_loopexit(exit_base,NULL); +} + +static void +http_incomplete_writecb(struct bufferevent *bev, void *arg) +{ + if (arg != NULL) { + evutil_socket_t fd = *(evutil_socket_t *)arg; + /* terminate the write side to simulate EOF */ + shutdown(fd, SHUT_WR); + } + if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) { + /* enable reading of the reply */ + bufferevent_enable(bev, EV_READ); + test_ok++; + } +} + +static void +http_incomplete_test_(struct basic_test_data *data, int use_timeout, int ssl) +{ + struct bufferevent *bev; + evutil_socket_t fd; + const char *http_request; + ev_uint16_t port = 0; + struct timeval tv_start, tv_end; + + exit_base = data->base; + + test_ok = 0; + + http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); + evhttp_set_timeout(http, 1); + + fd = http_connect("127.0.0.1", port); + tt_int_op(fd, >=, 0); + + /* Stupid thing to send a request */ + bev = create_bev(data->base, fd, ssl); + bufferevent_setcb(bev, + http_incomplete_readcb, http_incomplete_writecb, + http_incomplete_errorcb, use_timeout ? NULL : &fd); + + http_request = + "GET /test HTTP/1.1\r\n" + "Host: somehost\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + evutil_gettimeofday(&tv_start, NULL); + + event_base_dispatch(data->base); + + evutil_gettimeofday(&tv_end, NULL); + evutil_timersub(&tv_end, &tv_start, &tv_end); + + bufferevent_free(bev); + if (use_timeout) { + evutil_closesocket(fd); + fd = -1; + } + + evhttp_free(http); + + if (use_timeout && tv_end.tv_sec >= 3) { + tt_abort_msg("time"); + } else if (!use_timeout && tv_end.tv_sec >= 1) { + /* we should be done immediately */ + tt_abort_msg("time"); + } + + tt_int_op(test_ok, ==, 2); + end: + if (fd >= 0) + evutil_closesocket(fd); +} +static void http_incomplete_test(void *arg) +{ http_incomplete_test_(arg, 0, 0); } +static void http_incomplete_timeout_test(void *arg) +{ http_incomplete_test_(arg, 1, 0); } + + +/* + * the server is going to reply with chunked data. + */ + +static void +http_chunked_readcb(struct bufferevent *bev, void *arg) +{ + /* nothing here */ +} + +static void +http_chunked_errorcb(struct bufferevent *bev, short what, void *arg) +{ + struct evhttp_request *req = NULL; + + /** SSL */ + if (what & BEV_EVENT_CONNECTED) + return; + + if (!test_ok) + goto out; + + test_ok = -1; + + if ((what & BEV_EVENT_EOF) != 0) { + const char *header; + enum message_read_status done; + req = evhttp_request_new(NULL, NULL); + + /* req->kind = EVHTTP_RESPONSE; */ + done = evhttp_parse_firstline_(req, bufferevent_get_input(bev)); + if (done != ALL_DATA_READ) + goto out; + + done = evhttp_parse_headers_(req, bufferevent_get_input(bev)); + if (done != ALL_DATA_READ) + goto out; + + header = evhttp_find_header(evhttp_request_get_input_headers(req), "Transfer-Encoding"); + if (header == NULL || strcmp(header, "chunked")) + goto out; + + header = evhttp_find_header(evhttp_request_get_input_headers(req), "Connection"); + if (header == NULL || strcmp(header, "close")) + goto out; + + header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF); + if (header == NULL) + goto out; + /* 13 chars */ + if (strcmp(header, "d")) { + free((void*)header); + goto out; + } + free((void*)header); + + if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev), 13), + "This is funny", 13)) + goto out; + + evbuffer_drain(bufferevent_get_input(bev), 13 + 2); + + header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF); + if (header == NULL) + goto out; + /* 18 chars */ + if (strcmp(header, "12")) + goto out; + free((char *)header); + + if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev), 18), + "but not hilarious.", 18)) + goto out; + + evbuffer_drain(bufferevent_get_input(bev), 18 + 2); + + header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF); + if (header == NULL) + goto out; + /* 8 chars */ + if (strcmp(header, "8")) { + free((void*)header); + goto out; + } + free((char *)header); + + if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev), 8), + "bwv 1052.", 8)) + goto out; + + evbuffer_drain(bufferevent_get_input(bev), 8 + 2); + + header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF); + if (header == NULL) + goto out; + /* 0 chars */ + if (strcmp(header, "0")) { + free((void*)header); + goto out; + } + free((void *)header); + + test_ok = 2; + } + +out: + if (req) + evhttp_request_free(req); + + event_base_loopexit(arg, NULL); +} + +static void +http_chunked_writecb(struct bufferevent *bev, void *arg) +{ + if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) { + /* enable reading of the reply */ + bufferevent_enable(bev, EV_READ); + test_ok++; + } +} + +static void +http_chunked_request_done(struct evhttp_request *req, void *arg) +{ + if (evhttp_request_get_response_code(req) != HTTP_OK) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evhttp_find_header(evhttp_request_get_input_headers(req), + "Transfer-Encoding") == NULL) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != 13 + 18 + 8) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (strncmp((char *)evbuffer_pullup(evhttp_request_get_input_buffer(req), 13 + 18 + 8), + "This is funnybut not hilarious.bwv 1052", + 13 + 18 + 8)) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + test_ok = 1; + event_base_loopexit(arg, NULL); +} + +static void +http_chunk_out_test_impl(void *arg, int ssl) +{ + struct basic_test_data *data = arg; + struct bufferevent *bev; + evutil_socket_t fd; + const char *http_request; + ev_uint16_t port = 0; + struct timeval tv_start, tv_end; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + int i; + + exit_base = data->base; + test_ok = 0; + + http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); + + fd = http_connect("127.0.0.1", port); + + /* Stupid thing to send a request */ + bev = create_bev(data->base, fd, ssl); + bufferevent_setcb(bev, + http_chunked_readcb, http_chunked_writecb, + http_chunked_errorcb, data->base); + + http_request = + "GET /chunked HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + evutil_gettimeofday(&tv_start, NULL); + + event_base_dispatch(data->base); + + bufferevent_free(bev); + + evutil_gettimeofday(&tv_end, NULL); + evutil_timersub(&tv_end, &tv_start, &tv_end); + + tt_int_op(tv_end.tv_sec, <, 1); + + tt_int_op(test_ok, ==, 2); + + /* now try again with the regular connection object */ + bev = create_bev(data->base, -1, ssl); + evcon = evhttp_connection_base_bufferevent_new( + data->base, NULL, bev, "127.0.0.1", port); + tt_assert(evcon); + + /* make two requests to check the keepalive behavior */ + for (i = 0; i < 2; i++) { + test_ok = 0; + req = evhttp_request_new(http_chunked_request_done,data->base); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, + EVHTTP_REQ_GET, "/chunked") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + tt_assert(test_ok == 1); + } + + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); +} +static void http_chunk_out_test(void *arg) +{ return http_chunk_out_test_impl(arg, 0); } + +static void +http_stream_out_test_impl(void *arg, int ssl) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + struct bufferevent *bev; + + test_ok = 0; + exit_base = data->base; + + http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); + + bev = create_bev(data->base, -1, ssl); + evcon = evhttp_connection_base_bufferevent_new( + data->base, NULL, bev, "127.0.0.1", port); + tt_assert(evcon); + + /* + * At this point, we want to schedule a request to the HTTP + * server using our make request method. + */ + + req = evhttp_request_new(http_request_done, + (void *)"This is funnybut not hilarious.bwv 1052"); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/streamed") + == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); +} +static void http_stream_out_test(void *arg) +{ return http_stream_out_test_impl(arg, 0); } + +static void +http_stream_in_chunk(struct evhttp_request *req, void *arg) +{ + struct evbuffer *reply = arg; + + if (evhttp_request_get_response_code(req) != HTTP_OK) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + evbuffer_add_buffer(reply, evhttp_request_get_input_buffer(req)); +} + +static void +http_stream_in_done(struct evhttp_request *req, void *arg) +{ + if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != 0) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + event_base_loopexit(exit_base, NULL); +} + +/** + * Makes a request and reads the response in chunks. + */ +static void +http_stream_in_test_(struct basic_test_data *data, char const *url, + size_t expected_len, char const *expected) +{ + struct evhttp_connection *evcon; + struct evbuffer *reply = evbuffer_new(); + struct evhttp_request *req = NULL; + ev_uint16_t port = 0; + + exit_base = data->base; + http = http_setup(&port, data->base, 0); + + evcon = evhttp_connection_base_new(data->base, NULL,"127.0.0.1", port); + tt_assert(evcon); + + req = evhttp_request_new(http_stream_in_done, reply); + evhttp_request_set_chunked_cb(req, http_stream_in_chunk); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, url) == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + if (evbuffer_get_length(reply) != expected_len) { + TT_DIE(("reply length %lu; expected %lu; FAILED (%s)\n", + (unsigned long)evbuffer_get_length(reply), + (unsigned long)expected_len, + (char*)evbuffer_pullup(reply, -1))); + } + + if (memcmp(evbuffer_pullup(reply, -1), expected, expected_len) != 0) { + tt_abort_msg("Memory mismatch"); + } + + test_ok = 1; + end: + if (reply) + evbuffer_free(reply); + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); +} + +static void +http_stream_in_test(void *arg) +{ + http_stream_in_test_(arg, "/chunked", 13 + 18 + 8, + "This is funnybut not hilarious.bwv 1052"); + + http_stream_in_test_(arg, "/test", strlen(BASIC_REQUEST_BODY), + BASIC_REQUEST_BODY); +} + +static void +http_stream_in_cancel_chunk(struct evhttp_request *req, void *arg) +{ + tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_OK); + + end: + evhttp_cancel_request(req); + event_base_loopexit(arg, NULL); +} + +static void +http_stream_in_cancel_done(struct evhttp_request *req, void *arg) +{ + /* should never be called */ + tt_fail_msg("In cancel done"); +} + +static void +http_stream_in_cancel_test(void *arg) +{ + struct basic_test_data *data = arg; + struct evhttp_connection *evcon; + struct evhttp_request *req = NULL; + ev_uint16_t port = 0; + + http = http_setup(&port, data->base, 0); + + evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + tt_assert(evcon); + + req = evhttp_request_new(http_stream_in_cancel_done, data->base); + evhttp_request_set_chunked_cb(req, http_stream_in_cancel_chunk); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/chunked") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + test_ok = 1; + end: + evhttp_connection_free(evcon); + evhttp_free(http); + +} + +static void +http_connection_fail_done(struct evhttp_request *req, void *arg) +{ + struct evhttp_connection *evcon = arg; + struct event_base *base = evhttp_connection_get_base(evcon); + + /* An ENETUNREACH error results in an unrecoverable + * evhttp_connection error (see evhttp_connection_fail_()). The + * connection will be reset, and the user will be notified with a NULL + * req parameter. */ + tt_assert(!req); + + evhttp_connection_free(evcon); + + test_ok = 1; + + end: + event_base_loopexit(base, NULL); +} + +/* Test unrecoverable evhttp_connection errors by generating an ENETUNREACH + * error on connection. */ +static void +http_connection_fail_test_impl(void *arg, int ssl) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + struct bufferevent *bev; + + exit_base = data->base; + test_ok = 0; + + /* auto detect a port */ + http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); + evhttp_free(http); + http = NULL; + + bev = create_bev(data->base, -1, ssl); + /* Pick an unroutable address. This administratively scoped multicast + * address should do when working with TCP. */ + evcon = evhttp_connection_base_bufferevent_new( + data->base, NULL, bev, "239.10.20.30", 80); + tt_assert(evcon); + + /* + * At this point, we want to schedule an HTTP GET request + * server using our make request method. + */ + + req = evhttp_request_new(http_connection_fail_done, evcon); + tt_assert(req); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + tt_int_op(test_ok, ==, 1); + + end: + ; +} +static void http_connection_fail_test(void *arg) +{ return http_connection_fail_test_impl(arg, 0); } + +static void +http_connection_retry_done(struct evhttp_request *req, void *arg) +{ + tt_assert(req); + tt_int_op(evhttp_request_get_response_code(req), !=, HTTP_OK); + if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") != NULL) { + tt_abort_msg("(content type)\n"); + } + + tt_uint_op(evbuffer_get_length(evhttp_request_get_input_buffer(req)), ==, 0); + + test_ok = 1; + end: + event_base_loopexit(arg,NULL); +} + +struct http_server +{ + ev_uint16_t port; + int ssl; +}; +static struct event_base *http_make_web_server_base=NULL; +static void +http_make_web_server(evutil_socket_t fd, short what, void *arg) +{ + struct http_server *hs = (struct http_server *)arg; + http = http_setup(&hs->port, http_make_web_server_base, hs->ssl ? HTTP_BIND_SSL : 0); +} + +static void +http_simple_test_impl(void *arg, int ssl, int dirty) +{ + struct basic_test_data *data = arg; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + struct bufferevent *bev; + struct http_server hs = { .port = 0, .ssl = ssl, }; + + exit_base = data->base; + test_ok = 0; + + http = http_setup(&hs.port, data->base, ssl ? HTTP_BIND_SSL : 0); + + bev = create_bev(data->base, -1, ssl); + evcon = evhttp_connection_base_bufferevent_new( + data->base, NULL, bev, "127.0.0.1", hs.port); + tt_assert(evcon); + evhttp_connection_set_local_address(evcon, "127.0.0.1"); + + req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); + tt_assert(req); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + tt_int_op(test_ok, ==, 1); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); +} +static void http_simple_test(void *arg) +{ return http_simple_test_impl(arg, 0, 0); } + +static void +http_connection_retry_test_basic(void *arg, const char *addr, struct evdns_base *dns_base, int ssl) +{ + struct basic_test_data *data = arg; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + struct timeval tv, tv_start, tv_end; + struct bufferevent *bev; + struct http_server hs = { .port = 0, .ssl = ssl, }; + + exit_base = data->base; + test_ok = 0; + + /* auto detect a port */ + http = http_setup(&hs.port, data->base, ssl ? HTTP_BIND_SSL : 0); + evhttp_free(http); + http = NULL; + + bev = create_bev(data->base, -1, ssl); + evcon = evhttp_connection_base_bufferevent_new(data->base, dns_base, bev, addr, hs.port); + tt_assert(evcon); + if (dns_base) + tt_assert(!evhttp_connection_set_flags(evcon, EVHTTP_CON_REUSE_CONNECTED_ADDR)); + + evhttp_connection_set_timeout(evcon, 1); + /* also bind to local host */ + evhttp_connection_set_local_address(evcon, "127.0.0.1"); + + /* + * At this point, we want to schedule an HTTP GET request + * server using our make request method. + */ + + req = evhttp_request_new(http_connection_retry_done, data->base); + tt_assert(req); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, + "/?arg=val") == -1) { + tt_abort_msg("Couldn't make request"); + } + + evutil_gettimeofday(&tv_start, NULL); + event_base_dispatch(data->base); + evutil_gettimeofday(&tv_end, NULL); + evutil_timersub(&tv_end, &tv_start, &tv_end); + tt_int_op(tv_end.tv_sec, <, 1); + + tt_int_op(test_ok, ==, 1); + + /* + * now test the same but with retries + */ + test_ok = 0; + /** Shutdown dns server, to test conn_address reusing */ + if (dns_base) + regress_clean_dnsserver(); + + { + const struct timeval tv_timeout = { 0, 500000 }; + const struct timeval tv_retry = { 0, 500000 }; + evhttp_connection_set_timeout_tv(evcon, &tv_timeout); + evhttp_connection_set_initial_retry_tv(evcon, &tv_retry); + } + evhttp_connection_set_retries(evcon, 1); + + req = evhttp_request_new(http_connection_retry_done, data->base); + tt_assert(req); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, + "/?arg=val") == -1) { + tt_abort_msg("Couldn't make request"); + } + + evutil_gettimeofday(&tv_start, NULL); + event_base_dispatch(data->base); + evutil_gettimeofday(&tv_end, NULL); + + /* fails fast, .5 sec to wait to retry, fails fast again. */ + test_timeval_diff_leq(&tv_start, &tv_end, 500, 200); + + tt_assert(test_ok == 1); + + /* + * now test the same but with retries and give it a web server + * at the end + */ + test_ok = 0; + + evhttp_connection_set_timeout(evcon, 1); + evhttp_connection_set_retries(evcon, 3); + + req = evhttp_request_new(http_dispatcher_test_done, data->base); + tt_assert(req); + + /* Add the information that we care about */ + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, + "/?arg=val") == -1) { + tt_abort_msg("Couldn't make request"); + } + + /* start up a web server .2 seconds after the connection tried + * to send a request + */ + evutil_timerclear(&tv); + tv.tv_usec = 200000; + http_make_web_server_base = data->base; + event_base_once(data->base, -1, EV_TIMEOUT, http_make_web_server, &hs, &tv); + + evutil_gettimeofday(&tv_start, NULL); + event_base_dispatch(data->base); + evutil_gettimeofday(&tv_end, NULL); + /* We'll wait twice as long as we did last time. */ + test_timeval_diff_leq(&tv_start, &tv_end, 1000, 400); + + tt_int_op(test_ok, ==, 1); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); +} + +static void +http_connection_retry_conn_address_test_impl(void *arg, int ssl) +{ + struct basic_test_data *data = arg; + ev_uint16_t portnum = 0; + struct evdns_base *dns_base = NULL; + char address[64]; + + tt_assert(regress_dnsserver(data->base, &portnum, search_table)); + dns_base = evdns_base_new(data->base, 0/* init name servers */); + tt_assert(dns_base); + + /* Add ourself as the only nameserver, and make sure we really are + * the only nameserver. */ + evutil_snprintf(address, sizeof(address), "127.0.0.1:%d", portnum); + evdns_base_nameserver_ip_add(dns_base, address); + + http_connection_retry_test_basic(arg, "localhost", dns_base, ssl); + + end: + if (dns_base) + evdns_base_free(dns_base, 0); + /** dnsserver will be cleaned in http_connection_retry_test_basic() */ +} +static void http_connection_retry_conn_address_test(void *arg) +{ return http_connection_retry_conn_address_test_impl(arg, 0); } + +static void +http_connection_retry_test_impl(void *arg, int ssl) +{ + return http_connection_retry_test_basic(arg, "127.0.0.1", NULL, ssl); +} +static void +http_connection_retry_test(void *arg) +{ return http_connection_retry_test_impl(arg, 0); } + +static void +http_primitives(void *ptr) +{ + char *escaped = NULL; + struct evhttp *http = NULL; + + escaped = evhttp_htmlescape("<script>"); + tt_assert(escaped); + tt_str_op(escaped, ==, "<script>"); + free(escaped); + + escaped = evhttp_htmlescape("\"\'&"); + tt_assert(escaped); + tt_str_op(escaped, ==, ""'&"); + + http = evhttp_new(NULL); + tt_assert(http); + tt_int_op(evhttp_set_cb(http, "/test", http_basic_cb, NULL), ==, 0); + tt_int_op(evhttp_set_cb(http, "/test", http_basic_cb, NULL), ==, -1); + tt_int_op(evhttp_del_cb(http, "/test"), ==, 0); + tt_int_op(evhttp_del_cb(http, "/test"), ==, -1); + tt_int_op(evhttp_set_cb(http, "/test", http_basic_cb, NULL), ==, 0); + + end: + if (escaped) + free(escaped); + if (http) + evhttp_free(http); +} + +static void +http_multi_line_header_test(void *arg) +{ + struct basic_test_data *data = arg; + struct bufferevent *bev= NULL; + evutil_socket_t fd = -1; + const char *http_start_request; + ev_uint16_t port = 0; + + test_ok = 0; + + http = http_setup(&port, data->base, 0); + + tt_ptr_op(http, !=, NULL); + + fd = http_connect("127.0.0.1", port); + + tt_int_op(fd, !=, -1); + + /* Stupid thing to send a request */ + bev = bufferevent_socket_new(data->base, fd, 0); + tt_ptr_op(bev, !=, NULL); + bufferevent_setcb(bev, http_readcb, http_writecb, + http_errorcb, data->base); + + http_start_request = + "GET /test HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "X-Multi-Extra-WS: libevent \r\n" + "\t\t\t2.1 \r\n" + "X-Multi: aaaaaaaa\r\n" + " a\r\n" + "\tEND\r\n" + "X-Last: last\r\n" + "\r\n"; + + bufferevent_write(bev, http_start_request, strlen(http_start_request)); + found_multi = found_multi2 = 0; + + event_base_dispatch(data->base); + + tt_int_op(found_multi, ==, 1); + tt_int_op(found_multi2, ==, 1); + tt_int_op(test_ok, ==, 4); + end: + if (bev) + bufferevent_free(bev); + if (fd >= 0) + evutil_closesocket(fd); + if (http) + evhttp_free(http); +} + +static void +http_request_bad(struct evhttp_request *req, void *arg) +{ + if (req != NULL) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + test_ok = 1; + event_base_loopexit(arg, NULL); +} + +static void +http_negative_content_length_test(void *arg) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + test_ok = 0; + + http = http_setup(&port, data->base, 0); + + evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + tt_assert(evcon); + + /* + * At this point, we want to schedule a request to the HTTP + * server using our make request method. + */ + + req = evhttp_request_new(http_request_bad, data->base); + + /* Cause the response to have a negative content-length */ + evhttp_add_header(evhttp_request_get_output_headers(req), "X-Negative", "makeitso"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); +} + + +static void +http_data_length_constraints_test_done(struct evhttp_request *req, void *arg) +{ + tt_assert(req); + tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_BADREQUEST); +end: + event_base_loopexit(arg, NULL); +} + +static void +http_large_entity_test_done(struct evhttp_request *req, void *arg) +{ + tt_assert(req); + tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_ENTITYTOOLARGE); +end: + event_base_loopexit(arg, NULL); +} + +static void +http_data_length_constraints_test(void *arg) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + char long_str[8192]; + + test_ok = 0; + + http = http_setup(&port, data->base, 0); + + evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + tt_assert(evcon); + + /* also bind to local host */ + evhttp_connection_set_local_address(evcon, "127.0.0.1"); + + /* + * At this point, we want to schedule an HTTP GET request + * server using our make request method. + */ + + req = evhttp_request_new(http_data_length_constraints_test_done, data->base); + tt_assert(req); + + memset(long_str, 'a', 8192); + long_str[8191] = '\0'; + /* Add the information that we care about */ + evhttp_set_max_headers_size(http, 8191); + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + evhttp_add_header(evhttp_request_get_output_headers(req), "Longheader", long_str); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) { + tt_abort_msg("Couldn't make request"); + } + event_base_dispatch(data->base); + + req = evhttp_request_new(http_data_length_constraints_test_done, data->base); + tt_assert(req); + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + + /* GET /?arg=verylongvalue HTTP/1.1 */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, long_str) == -1) { + tt_abort_msg("Couldn't make request"); + } + event_base_dispatch(data->base); + + evhttp_set_max_body_size(http, 8190); + req = evhttp_request_new(http_data_length_constraints_test_done, data->base); + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str); + if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) { + tt_abort_msg("Couldn't make request"); + } + event_base_dispatch(data->base); + + req = evhttp_request_new(http_large_entity_test_done, data->base); + evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); + evhttp_add_header(evhttp_request_get_output_headers(req), "Expect", "100-continue"); + evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str); + if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) { + tt_abort_msg("Couldn't make request"); + } + event_base_dispatch(data->base); + + test_ok = 1; + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); +} + +/* + * Testing client reset of server chunked connections + */ + +struct terminate_state { + struct event_base *base; + struct evhttp_request *req; + struct bufferevent *bev; + evutil_socket_t fd; + int gotclosecb: 1; + int oneshot: 1; +}; + +static void +terminate_chunked_trickle_cb(evutil_socket_t fd, short events, void *arg) +{ + struct terminate_state *state = arg; + struct evbuffer *evb; + + if (!state->req) { + return; + } + + if (evhttp_request_get_connection(state->req) == NULL) { + test_ok = 1; + evhttp_request_free(state->req); + event_base_loopexit(state->base,NULL); + return; + } + + evb = evbuffer_new(); + evbuffer_add_printf(evb, "%p", evb); + evhttp_send_reply_chunk(state->req, evb); + evbuffer_free(evb); + + if (!state->oneshot) { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 3000; + EVUTIL_ASSERT(state); + EVUTIL_ASSERT(state->base); + event_base_once(state->base, -1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv); + } +} + +static void +terminate_chunked_close_cb(struct evhttp_connection *evcon, void *arg) +{ + struct terminate_state *state = arg; + state->gotclosecb = 1; + + /** TODO: though we can do this unconditionally */ + if (state->oneshot) { + evhttp_request_free(state->req); + state->req = NULL; + event_base_loopexit(state->base,NULL); + } +} + +static void +terminate_chunked_cb(struct evhttp_request *req, void *arg) +{ + struct terminate_state *state = arg; + struct timeval tv; + + /* we want to know if this connection closes on us */ + evhttp_connection_set_closecb( + evhttp_request_get_connection(req), + terminate_chunked_close_cb, arg); + + state->req = req; + + evhttp_send_reply_start(req, HTTP_OK, "OK"); + + tv.tv_sec = 0; + tv.tv_usec = 3000; + event_base_once(state->base, -1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv); +} + +static void +terminate_chunked_client(evutil_socket_t fd, short event, void *arg) +{ + struct terminate_state *state = arg; + bufferevent_free(state->bev); + evutil_closesocket(state->fd); +} + +static void +terminate_readcb(struct bufferevent *bev, void *arg) +{ + /* just drop the data */ + evbuffer_drain(bufferevent_get_input(bev), -1); +} + + +static void +http_terminate_chunked_test_impl(void *arg, int oneshot) +{ + struct basic_test_data *data = arg; + struct bufferevent *bev = NULL; + struct timeval tv; + const char *http_request; + ev_uint16_t port = 0; + evutil_socket_t fd = -1; + struct terminate_state terminate_state; + + test_ok = 0; + + http = http_setup(&port, data->base, 0); + evhttp_del_cb(http, "/test"); + tt_assert(evhttp_set_cb(http, "/test", + terminate_chunked_cb, &terminate_state) == 0); + + fd = http_connect("127.0.0.1", port); + + /* Stupid thing to send a request */ + bev = bufferevent_socket_new(data->base, fd, 0); + bufferevent_setcb(bev, terminate_readcb, http_writecb, + http_errorcb, data->base); + + memset(&terminate_state, 0, sizeof(terminate_state)); + terminate_state.base = data->base; + terminate_state.fd = fd; + terminate_state.bev = bev; + terminate_state.gotclosecb = 0; + terminate_state.oneshot = oneshot; + + /* first half of the http request */ + http_request = + "GET /test HTTP/1.1\r\n" + "Host: some\r\n\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + evutil_timerclear(&tv); + tv.tv_usec = 10000; + event_base_once(data->base, -1, EV_TIMEOUT, terminate_chunked_client, &terminate_state, + &tv); + + event_base_dispatch(data->base); + + if (terminate_state.gotclosecb == 0) + test_ok = 0; + + end: + if (fd >= 0) + evutil_closesocket(fd); + if (http) + evhttp_free(http); +} +static void +http_terminate_chunked_test(void *arg) +{ + http_terminate_chunked_test_impl(arg, 0); +} +static void +http_terminate_chunked_oneshot_test(void *arg) +{ + http_terminate_chunked_test_impl(arg, 1); +} + +static struct regress_dns_server_table ipv6_search_table[] = { + { "localhost", "AAAA", "::1", 0, 0 }, + { NULL, NULL, NULL, 0, 0 } +}; + +static void +http_ipv6_for_domain_test_impl(void *arg, int family) +{ + struct basic_test_data *data = arg; + struct evdns_base *dns_base = NULL; + ev_uint16_t portnum = 0; + char address[64]; + + tt_assert(regress_dnsserver(data->base, &portnum, ipv6_search_table)); + + dns_base = evdns_base_new(data->base, 0/* init name servers */); + tt_assert(dns_base); + + /* Add ourself as the only nameserver, and make sure we really are + * the only nameserver. */ + evutil_snprintf(address, sizeof(address), "127.0.0.1:%d", portnum); + evdns_base_nameserver_ip_add(dns_base, address); + + http_connection_test_(arg, 0 /* not persistent */, "localhost", dns_base, + 1 /* ipv6 */, family); + + end: + if (dns_base) + evdns_base_free(dns_base, 0); + regress_clean_dnsserver(); +} +static void +http_ipv6_for_domain_test(void *arg) +{ + http_ipv6_for_domain_test_impl(arg, AF_UNSPEC); +} + +static void +http_request_get_addr_on_close(struct evhttp_connection *evcon, void *arg) +{ + const struct sockaddr *storage; + char addrbuf[128]; + char local[] = "127.0.0.1:"; + + test_ok = 0; + tt_assert(evcon); + + storage = evhttp_connection_get_addr(evcon); + tt_assert(storage); + + evutil_format_sockaddr_port_((struct sockaddr *)storage, addrbuf, sizeof(addrbuf)); + tt_assert(!strncmp(addrbuf, local, sizeof(local) - 1)); + + test_ok = 1; + return; + +end: + test_ok = 0; +} + +static void +http_get_addr_test(void *arg) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + test_ok = 0; + exit_base = data->base; + + http = http_setup(&port, data->base, 0); + + evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + tt_assert(evcon); + evhttp_connection_set_closecb(evcon, http_request_get_addr_on_close, arg); + + /* + * At this point, we want to schedule a request to the HTTP + * server using our make request method. + */ + + req = evhttp_request_new(http_request_done, (void *)BASIC_REQUEST_BODY); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("Couldn't make request"); + } + + event_base_dispatch(data->base); + + http_request_get_addr_on_close(evcon, NULL); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (http) + evhttp_free(http); +} + +static void +http_set_family_test(void *arg) +{ + http_connection_test_(arg, 0, "127.0.0.1", NULL, 0, AF_UNSPEC); +} +static void +http_set_family_ipv4_test(void *arg) +{ + http_connection_test_(arg, 0, "127.0.0.1", NULL, 0, AF_INET); +} +static void +http_set_family_ipv6_test(void *arg) +{ + http_ipv6_for_domain_test_impl(arg, AF_INET6); +} + +static void +http_write_during_read(evutil_socket_t fd, short what, void *arg) +{ + struct bufferevent *bev = arg; + struct timeval tv; + + bufferevent_write(bev, "foobar", strlen("foobar")); + + evutil_timerclear(&tv); + tv.tv_sec = 1; + event_base_loopexit(exit_base, &tv); +} +static void +http_write_during_read_test_impl(void *arg, int ssl) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct bufferevent *bev = NULL; + struct timeval tv; + int fd; + const char *http_request; + + test_ok = 0; + exit_base = data->base; + + http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); + + fd = http_connect("127.0.0.1", port); + bev = create_bev(data->base, fd, 0); + bufferevent_setcb(bev, NULL, NULL, NULL, data->base); + bufferevent_disable(bev, EV_READ); + + http_request = + "GET /large HTTP/1.1\r\n" + "Host: somehost\r\n" + "\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + evutil_timerclear(&tv); + tv.tv_usec = 10000; + event_base_once(data->base, -1, EV_TIMEOUT, http_write_during_read, bev, &tv); + + event_base_dispatch(data->base); + + if (bev) + bufferevent_free(bev); + if (http) + evhttp_free(http); +} +static void http_write_during_read_test(void *arg) +{ return http_write_during_read_test_impl(arg, 0); } + +static void +http_request_own_test(void *arg) +{ + struct basic_test_data *data = arg; + ev_uint16_t port = 0; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + test_ok = 0; + exit_base = data->base; + + http = http_setup(&port, data->base, 0); + evhttp_free(http); + + evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); + tt_assert(evcon); + + req = evhttp_request_new(http_request_no_action_done, NULL); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + tt_abort_msg("Couldn't make request"); + } + evhttp_request_own(req); + + event_base_dispatch(data->base); + + end: + if (evcon) + evhttp_connection_free(evcon); + if (req) + evhttp_request_free(req); + + test_ok = 1; +} + +#define HTTP_LEGACY(name) \ + { #name, run_legacy_test_fn, TT_ISOLATED|TT_LEGACY, &legacy_setup, \ + http_##name##_test } + +#define HTTP(name) \ + { #name, http_##name##_test, TT_ISOLATED, &basic_setup, NULL } +#define HTTPS(name) \ + { "https_" #name, https_##name##_test, TT_ISOLATED, &basic_setup, NULL } + +#ifdef EVENT__HAVE_OPENSSL +static void https_basic_test(void *arg) +{ return http_basic_test_impl(arg, 1); } +static void https_incomplete_test(void *arg) +{ http_incomplete_test_(arg, 0, 1); } +static void https_incomplete_timeout_test(void *arg) +{ http_incomplete_test_(arg, 1, 1); } +static void https_simple_test(void *arg) +{ return http_simple_test_impl(arg, 1, 0); } +static void https_simple_dirty_test(void *arg) +{ return http_simple_test_impl(arg, 1, 1); } +static void https_connection_retry_conn_address_test(void *arg) +{ return http_connection_retry_conn_address_test_impl(arg, 1); } +static void https_connection_retry_test(void *arg) +{ return http_connection_retry_test_impl(arg, 1); } +static void https_chunk_out_test(void *arg) +{ return http_chunk_out_test_impl(arg, 1); } +static void https_stream_out_test(void *arg) +{ return http_stream_out_test_impl(arg, 1); } +static void https_connection_fail_test(void *arg) +{ return http_connection_fail_test_impl(arg, 1); } +static void https_write_during_read_test(void *arg) +{ return http_write_during_read_test_impl(arg, 1); } +#endif + +struct testcase_t http_testcases[] = { + { "primitives", http_primitives, 0, NULL, NULL }, + { "base", http_base_test, TT_FORK, NULL, NULL }, + { "bad_headers", http_bad_header_test, 0, NULL, NULL }, + { "parse_query", http_parse_query_test, 0, NULL, NULL }, + { "parse_uri", http_parse_uri_test, 0, NULL, NULL }, + { "parse_uri_nc", http_parse_uri_test, 0, &basic_setup, (void*)"nc" }, + { "uriencode", http_uriencode_test, 0, NULL, NULL }, + HTTP(basic), + HTTP(simple), + HTTP(cancel), + HTTP(virtual_host), + HTTP(post), + HTTP(put), + HTTP(delete), + HTTP(allowed_methods), + HTTP(failure), + HTTP(connection), + HTTP(persist_connection), + HTTP(autofree_connection), + HTTP(connection_async), + HTTP(close_detection), + HTTP(close_detection_delay), + HTTP(bad_request), + HTTP(incomplete), + HTTP(incomplete_timeout), + HTTP(terminate_chunked), + HTTP(terminate_chunked_oneshot), + HTTP(on_complete), + + HTTP(highport), + HTTP(dispatcher), + HTTP(multi_line_header), + HTTP(negative_content_length), + HTTP(chunk_out), + HTTP(stream_out), + + HTTP(stream_in), + HTTP(stream_in_cancel), + + HTTP(connection_fail), + { "connection_retry", http_connection_retry_test, TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, NULL }, + { "connection_retry_conn_address", http_connection_retry_conn_address_test, + TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, NULL }, + + HTTP(data_length_constraints), + + HTTP(ipv6_for_domain), + HTTP(get_addr), + + HTTP(set_family), + HTTP(set_family_ipv4), + HTTP(set_family_ipv6), + + HTTP(write_during_read), + HTTP(request_own), + +#ifdef EVENT__HAVE_OPENSSL + HTTPS(basic), + HTTPS(simple), + HTTPS(simple_dirty), + HTTPS(incomplete), + HTTPS(incomplete_timeout), + { "https_connection_retry", https_connection_retry_test, TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, NULL }, + { "https_connection_retry_conn_address", https_connection_retry_conn_address_test, + TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, NULL }, + HTTPS(chunk_out), + HTTPS(stream_out), + HTTPS(connection_fail), + HTTPS(write_during_read), +#endif + + END_OF_TESTCASES +}; + diff --git a/libs/libevent/docs/test/regress_iocp.c b/libs/libevent/docs/test/regress_iocp.c new file mode 100644 index 0000000000..17b385241f --- /dev/null +++ b/libs/libevent/docs/test/regress_iocp.c @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ + +#include <stdlib.h> +#include <string.h> +#include "event2/event.h" +#include "event2/thread.h" +#include "event2/buffer.h" +#include "event2/buffer_compat.h" +#include "event2/bufferevent.h" + +#include <winsock2.h> +#include <ws2tcpip.h> + +#include "regress.h" +#include "tinytest.h" +#include "tinytest_macros.h" + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <winsock2.h> +#undef WIN32_LEAN_AND_MEAN + +#include "iocp-internal.h" +#include "evbuffer-internal.h" +#include "evthread-internal.h" + +/* FIXME remove these ones */ +#include <sys/queue.h> +#include "event2/event_struct.h" +#include "event-internal.h" + +#define MAX_CALLS 16 + +static void *count_lock = NULL, *count_cond = NULL; +static int count = 0; + +static void +count_init(void) +{ + EVTHREAD_ALLOC_LOCK(count_lock, 0); + EVTHREAD_ALLOC_COND(count_cond); + + tt_assert(count_lock); + tt_assert(count_cond); + +end: + ; +} + +static void +count_free(void) +{ + EVTHREAD_FREE_LOCK(count_lock, 0); + EVTHREAD_FREE_COND(count_cond); +} + +static void +count_incr(void) +{ + EVLOCK_LOCK(count_lock, 0); + count++; + EVTHREAD_COND_BROADCAST(count_cond); + EVLOCK_UNLOCK(count_lock, 0); +} + +static int +count_wait_for(int i, int ms) +{ + struct timeval tv; + DWORD elapsed; + int rv = -1; + + EVLOCK_LOCK(count_lock, 0); + while (ms > 0 && count != i) { + tv.tv_sec = 0; + tv.tv_usec = ms * 1000; + elapsed = GetTickCount(); + EVTHREAD_COND_WAIT_TIMED(count_cond, count_lock, &tv); + elapsed = GetTickCount() - elapsed; + ms -= elapsed; + } + if (count == i) + rv = 0; + EVLOCK_UNLOCK(count_lock, 0); + + return rv; +} + +struct dummy_overlapped { + struct event_overlapped eo; + void *lock; + int call_count; + uintptr_t keys[MAX_CALLS]; + ev_ssize_t sizes[MAX_CALLS]; +}; + +static void +dummy_cb(struct event_overlapped *o, uintptr_t key, ev_ssize_t n, int ok) +{ + struct dummy_overlapped *d_o = + EVUTIL_UPCAST(o, struct dummy_overlapped, eo); + + EVLOCK_LOCK(d_o->lock, 0); + if (d_o->call_count < MAX_CALLS) { + d_o->keys[d_o->call_count] = key; + d_o->sizes[d_o->call_count] = n; + } + d_o->call_count++; + EVLOCK_UNLOCK(d_o->lock, 0); + + count_incr(); +} + +static int +pair_is_in(struct dummy_overlapped *o, uintptr_t key, ev_ssize_t n) +{ + int i; + int result = 0; + EVLOCK_LOCK(o->lock, 0); + for (i=0; i < o->call_count; ++i) { + if (o->keys[i] == key && o->sizes[i] == n) { + result = 1; + break; + } + } + EVLOCK_UNLOCK(o->lock, 0); + return result; +} + +static void +test_iocp_port(void *ptr) +{ + struct event_iocp_port *port = NULL; + struct dummy_overlapped o1, o2; + + memset(&o1, 0, sizeof(o1)); + memset(&o2, 0, sizeof(o2)); + + count_init(); + EVTHREAD_ALLOC_LOCK(o1.lock, EVTHREAD_LOCKTYPE_RECURSIVE); + EVTHREAD_ALLOC_LOCK(o2.lock, EVTHREAD_LOCKTYPE_RECURSIVE); + + tt_assert(o1.lock); + tt_assert(o2.lock); + + event_overlapped_init_(&o1.eo, dummy_cb); + event_overlapped_init_(&o2.eo, dummy_cb); + + port = event_iocp_port_launch_(0); + tt_assert(port); + + tt_assert(!event_iocp_activate_overlapped_(port, &o1.eo, 10, 100)); + tt_assert(!event_iocp_activate_overlapped_(port, &o2.eo, 20, 200)); + + tt_assert(!event_iocp_activate_overlapped_(port, &o1.eo, 11, 101)); + tt_assert(!event_iocp_activate_overlapped_(port, &o2.eo, 21, 201)); + + tt_assert(!event_iocp_activate_overlapped_(port, &o1.eo, 12, 102)); + tt_assert(!event_iocp_activate_overlapped_(port, &o2.eo, 22, 202)); + + tt_assert(!event_iocp_activate_overlapped_(port, &o1.eo, 13, 103)); + tt_assert(!event_iocp_activate_overlapped_(port, &o2.eo, 23, 203)); + + tt_int_op(count_wait_for(8, 2000), ==, 0); + + tt_want(!event_iocp_shutdown_(port, 2000)); + + tt_int_op(o1.call_count, ==, 4); + tt_int_op(o2.call_count, ==, 4); + + tt_want(pair_is_in(&o1, 10, 100)); + tt_want(pair_is_in(&o1, 11, 101)); + tt_want(pair_is_in(&o1, 12, 102)); + tt_want(pair_is_in(&o1, 13, 103)); + + tt_want(pair_is_in(&o2, 20, 200)); + tt_want(pair_is_in(&o2, 21, 201)); + tt_want(pair_is_in(&o2, 22, 202)); + tt_want(pair_is_in(&o2, 23, 203)); + +end: + EVTHREAD_FREE_LOCK(o1.lock, EVTHREAD_LOCKTYPE_RECURSIVE); + EVTHREAD_FREE_LOCK(o2.lock, EVTHREAD_LOCKTYPE_RECURSIVE); + count_free(); +} + +static struct evbuffer *rbuf = NULL, *wbuf = NULL; + +static void +read_complete(struct event_overlapped *eo, uintptr_t key, + ev_ssize_t nbytes, int ok) +{ + tt_assert(ok); + evbuffer_commit_read_(rbuf, nbytes); + count_incr(); +end: + ; +} + +static void +write_complete(struct event_overlapped *eo, uintptr_t key, + ev_ssize_t nbytes, int ok) +{ + tt_assert(ok); + evbuffer_commit_write_(wbuf, nbytes); + count_incr(); +end: + ; +} + +static void +test_iocp_evbuffer(void *ptr) +{ + struct event_overlapped rol, wol; + struct basic_test_data *data = ptr; + struct event_iocp_port *port = NULL; + struct evbuffer *buf=NULL; + struct evbuffer_chain *chain; + char junk[1024]; + int i; + + count_init(); + event_overlapped_init_(&rol, read_complete); + event_overlapped_init_(&wol, write_complete); + + for (i = 0; i < (int)sizeof(junk); ++i) + junk[i] = (char)(i); + + rbuf = evbuffer_overlapped_new_(data->pair[0]); + wbuf = evbuffer_overlapped_new_(data->pair[1]); + evbuffer_enable_locking(rbuf, NULL); + evbuffer_enable_locking(wbuf, NULL); + + port = event_iocp_port_launch_(0); + tt_assert(port); + tt_assert(rbuf); + tt_assert(wbuf); + + tt_assert(!event_iocp_port_associate_(port, data->pair[0], 100)); + tt_assert(!event_iocp_port_associate_(port, data->pair[1], 100)); + + for (i=0;i<10;++i) + evbuffer_add(wbuf, junk, sizeof(junk)); + + buf = evbuffer_new(); + tt_assert(buf != NULL); + evbuffer_add(rbuf, junk, sizeof(junk)); + tt_assert(!evbuffer_launch_read_(rbuf, 2048, &rol)); + evbuffer_add_buffer(buf, rbuf); + tt_int_op(evbuffer_get_length(buf), ==, sizeof(junk)); + for (chain = buf->first; chain; chain = chain->next) + tt_int_op(chain->flags & EVBUFFER_MEM_PINNED_ANY, ==, 0); + tt_assert(!evbuffer_get_length(rbuf)); + tt_assert(!evbuffer_launch_write_(wbuf, 512, &wol)); + + tt_int_op(count_wait_for(2, 2000), ==, 0); + + tt_int_op(evbuffer_get_length(rbuf),==,512); + + /* FIXME Actually test some stuff here. */ + + tt_want(!event_iocp_shutdown_(port, 2000)); +end: + count_free(); + evbuffer_free(rbuf); + evbuffer_free(wbuf); + if (buf) evbuffer_free(buf); +} + +static int got_readcb = 0; + +static void +async_readcb(struct bufferevent *bev, void *arg) +{ + /* Disabling read should cause the loop to quit */ + bufferevent_disable(bev, EV_READ); + got_readcb++; +} + +static void +test_iocp_bufferevent_async(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event_iocp_port *port = NULL; + struct bufferevent *bea1=NULL, *bea2=NULL; + char buf[128]; + size_t n; + + event_base_start_iocp_(data->base, 0); + port = event_base_get_iocp_(data->base); + tt_assert(port); + + bea1 = bufferevent_async_new_(data->base, data->pair[0], + BEV_OPT_DEFER_CALLBACKS); + bea2 = bufferevent_async_new_(data->base, data->pair[1], + BEV_OPT_DEFER_CALLBACKS); + tt_assert(bea1); + tt_assert(bea2); + + bufferevent_setcb(bea2, async_readcb, NULL, NULL, NULL); + bufferevent_enable(bea1, EV_WRITE); + bufferevent_enable(bea2, EV_READ); + + bufferevent_write(bea1, "Hello world", strlen("Hello world")+1); + + event_base_dispatch(data->base); + + tt_int_op(got_readcb, ==, 1); + n = bufferevent_read(bea2, buf, sizeof(buf)-1); + buf[n]='\0'; + tt_str_op(buf, ==, "Hello world"); + +end: + bufferevent_free(bea1); + bufferevent_free(bea2); +} + + +struct testcase_t iocp_testcases[] = { + { "port", test_iocp_port, TT_FORK|TT_NEED_THREADS, &basic_setup, NULL }, + { "evbuffer", test_iocp_evbuffer, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_THREADS, + &basic_setup, NULL }, + { "bufferevent_async", test_iocp_bufferevent_async, + TT_FORK|TT_NEED_SOCKETPAIR|TT_NEED_THREADS|TT_NEED_BASE, + &basic_setup, NULL }, + END_OF_TESTCASES +}; diff --git a/libs/libevent/docs/test/regress_listener.c b/libs/libevent/docs/test/regress_listener.c new file mode 100644 index 0000000000..4db102df68 --- /dev/null +++ b/libs/libevent/docs/test/regress_listener.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "util-internal.h" + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#endif + +#include <sys/types.h> + +#ifndef _WIN32 +#include <sys/socket.h> +#include <netinet/in.h> +# ifdef _XOPEN_SOURCE_EXTENDED +# include <arpa/inet.h> +# endif +#include <unistd.h> +#endif + +#include <string.h> + +#include "event2/listener.h" +#include "event2/event.h" +#include "event2/util.h" + +#include "regress.h" +#include "tinytest.h" +#include "tinytest_macros.h" + +static void +acceptcb(struct evconnlistener *listener, evutil_socket_t fd, + struct sockaddr *addr, int socklen, void *arg) +{ + int *ptr = arg; + --*ptr; + TT_BLATHER(("Got one for %p", ptr)); + evutil_closesocket(fd); + + if (! *ptr) + evconnlistener_disable(listener); +} + +static void +regress_pick_a_port(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evconnlistener *listener1 = NULL, *listener2 = NULL; + struct sockaddr_in sin; + int count1 = 2, count2 = 1; + struct sockaddr_storage ss1, ss2; + struct sockaddr_in *sin1, *sin2; + ev_socklen_t slen1 = sizeof(ss1), slen2 = sizeof(ss2); + unsigned int flags = + LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_EXEC; + + evutil_socket_t fd1 = -1, fd2 = -1, fd3 = -1; + + if (data->setup_data && strstr((char*)data->setup_data, "ts")) { + flags |= LEV_OPT_THREADSAFE; + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ + sin.sin_port = 0; /* "You pick!" */ + + listener1 = evconnlistener_new_bind(base, acceptcb, &count1, + flags, -1, (struct sockaddr *)&sin, sizeof(sin)); + tt_assert(listener1); + listener2 = evconnlistener_new_bind(base, acceptcb, &count2, + flags, -1, (struct sockaddr *)&sin, sizeof(sin)); + tt_assert(listener2); + + tt_int_op(evconnlistener_get_fd(listener1), >=, 0); + tt_int_op(evconnlistener_get_fd(listener2), >=, 0); + tt_assert(getsockname(evconnlistener_get_fd(listener1), + (struct sockaddr*)&ss1, &slen1) == 0); + tt_assert(getsockname(evconnlistener_get_fd(listener2), + (struct sockaddr*)&ss2, &slen2) == 0); + tt_int_op(ss1.ss_family, ==, AF_INET); + tt_int_op(ss2.ss_family, ==, AF_INET); + + sin1 = (struct sockaddr_in*)&ss1; + sin2 = (struct sockaddr_in*)&ss2; + tt_int_op(ntohl(sin1->sin_addr.s_addr), ==, 0x7f000001); + tt_int_op(ntohl(sin2->sin_addr.s_addr), ==, 0x7f000001); + tt_int_op(sin1->sin_port, !=, sin2->sin_port); + + tt_ptr_op(evconnlistener_get_base(listener1), ==, base); + tt_ptr_op(evconnlistener_get_base(listener2), ==, base); + + fd1 = fd2 = fd3 = -1; + evutil_socket_connect_(&fd1, (struct sockaddr*)&ss1, slen1); + evutil_socket_connect_(&fd2, (struct sockaddr*)&ss1, slen1); + evutil_socket_connect_(&fd3, (struct sockaddr*)&ss2, slen2); + +#ifdef _WIN32 + Sleep(100); /* XXXX this is a stupid stopgap. */ +#endif + event_base_dispatch(base); + + tt_int_op(count1, ==, 0); + tt_int_op(count2, ==, 0); + +end: + if (fd1>=0) + EVUTIL_CLOSESOCKET(fd1); + if (fd2>=0) + EVUTIL_CLOSESOCKET(fd2); + if (fd3>=0) + EVUTIL_CLOSESOCKET(fd3); + if (listener1) + evconnlistener_free(listener1); + if (listener2) + evconnlistener_free(listener2); +} + +static void +errorcb(struct evconnlistener *lis, void *data_) +{ + int *data = data_; + *data = 1000; + evconnlistener_disable(lis); +} + +static void +regress_listener_error(void *arg) +{ + struct basic_test_data *data = arg; + struct event_base *base = data->base; + struct evconnlistener *listener = NULL; + int count = 1; + unsigned int flags = LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE; + + if (data->setup_data && strstr((char*)data->setup_data, "ts")) { + flags |= LEV_OPT_THREADSAFE; + } + + /* send, so that pair[0] will look 'readable'*/ + tt_int_op(send(data->pair[1], "hello", 5, 0), >, 0); + + /* Start a listener with a bogus socket. */ + listener = evconnlistener_new(base, acceptcb, &count, + flags, 0, + data->pair[0]); + tt_assert(listener); + + evconnlistener_set_error_cb(listener, errorcb); + + tt_assert(listener); + + event_base_dispatch(base); + tt_int_op(count,==,1000); /* set by error cb */ + +end: + if (listener) + evconnlistener_free(listener); +} + +struct testcase_t listener_testcases[] = { + + { "randport", regress_pick_a_port, TT_FORK|TT_NEED_BASE, + &basic_setup, NULL}, + + { "randport_ts", regress_pick_a_port, TT_FORK|TT_NEED_BASE, + &basic_setup, (char*)"ts"}, + + { "error", regress_listener_error, + TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR, + &basic_setup, NULL}, + + { "error_ts", regress_listener_error, + TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR, + &basic_setup, (char*)"ts"}, + + END_OF_TESTCASES, +}; + +struct testcase_t listener_iocp_testcases[] = { + { "randport", regress_pick_a_port, + TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, + &basic_setup, NULL}, + + { "error", regress_listener_error, + TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR|TT_ENABLE_IOCP, + &basic_setup, NULL}, + + END_OF_TESTCASES, +}; diff --git a/libs/libevent/docs/test/regress_main.c b/libs/libevent/docs/test/regress_main.c new file mode 100644 index 0000000000..6d045bb833 --- /dev/null +++ b/libs/libevent/docs/test/regress_main.c @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "util-internal.h" + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#include <io.h> +#include <fcntl.h> +#endif + +#if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) +#if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070) +#define FORK_BREAKS_GCOV +#include <vproc.h> +#endif +#endif + +#include "event2/event-config.h" + +#ifdef EVENT____func__ +#define __func__ EVENT____func__ +#endif + +#if 0 +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/queue.h> +#include <signal.h> +#include <errno.h> +#endif + +#include <sys/types.h> +#ifdef EVENT__HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#ifndef _WIN32 +#include <sys/socket.h> +#include <sys/wait.h> +#include <signal.h> +#include <unistd.h> +#include <netdb.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#include "event2/util.h" +#include "event2/event.h" +#include "event2/event_compat.h" +#include "event2/dns.h" +#include "event2/dns_compat.h" +#include "event2/thread.h" + +#include "event2/event-config.h" +#include "regress.h" +#include "tinytest.h" +#include "tinytest_macros.h" +#include "../iocp-internal.h" +#include "../event-internal.h" + +struct evutil_weakrand_state test_weakrand_state; + +long +timeval_msec_diff(const struct timeval *start, const struct timeval *end) +{ + long ms = end->tv_sec - start->tv_sec; + ms *= 1000; + ms += ((end->tv_usec - start->tv_usec)+500) / 1000; + return ms; +} + +/* ============================================================ */ +/* Code to wrap up old legacy test cases that used setup() and cleanup(). + * + * Not all of the tests designated "legacy" are ones that used setup() and + * cleanup(), of course. A test is legacy it it uses setup()/cleanup(), OR + * if it wants to find its event base/socketpair in global variables (ugh), + * OR if it wants to communicate success/failure through test_ok. + */ + +/* This is set to true if we're inside a legacy test wrapper. It lets the + setup() and cleanup() functions in regress.c know they're not needed. + */ +int in_legacy_test_wrapper = 0; + +static void dnslogcb(int w, const char *m) +{ + TT_BLATHER(("%s", m)); +} + +/* creates a temporary file with the data in it. If *filename_out gets set, + * the caller should try to unlink it. */ +int +regress_make_tmpfile(const void *data, size_t datalen, char **filename_out) +{ +#ifndef _WIN32 + char tmpfilename[32]; + int fd; + *filename_out = NULL; + strcpy(tmpfilename, "/tmp/eventtmp.XXXXXX"); +#ifdef EVENT__HAVE_UMASK + umask(0077); +#endif + fd = mkstemp(tmpfilename); + if (fd == -1) + return (-1); + if (write(fd, data, datalen) != (int)datalen) { + close(fd); + return (-1); + } + lseek(fd, 0, SEEK_SET); + /* remove it from the file system */ + unlink(tmpfilename); + return (fd); +#else + /* XXXX actually delete the file later */ + char tmpfilepath[MAX_PATH]; + char tmpfilename[MAX_PATH]; + DWORD r, written; + int tries = 16; + HANDLE h; + r = GetTempPathA(MAX_PATH, tmpfilepath); + if (r > MAX_PATH || r == 0) + return (-1); + for (; tries > 0; --tries) { + r = GetTempFileNameA(tmpfilepath, "LIBEVENT", 0, tmpfilename); + if (r == 0) + return (-1); + h = CreateFileA(tmpfilename, GENERIC_READ|GENERIC_WRITE, + 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (h != INVALID_HANDLE_VALUE) + break; + } + if (tries == 0) + return (-1); + written = 0; + *filename_out = strdup(tmpfilename); + WriteFile(h, data, (DWORD)datalen, &written, NULL); + /* Closing the fd returned by this function will indeed close h. */ + return _open_osfhandle((intptr_t)h,_O_RDONLY); +#endif +} + +#ifndef _WIN32 +pid_t +regress_fork(void) +{ + pid_t pid = fork(); +#ifdef FORK_BREAKS_GCOV + vproc_transaction_begin(0); +#endif + return pid; +} +#endif + +static void +ignore_log_cb(int s, const char *msg) +{ +} + +static void * +basic_test_setup(const struct testcase_t *testcase) +{ + struct event_base *base = NULL; + evutil_socket_t spair[2] = { -1, -1 }; + struct basic_test_data *data = NULL; + +#ifndef _WIN32 + if (testcase->flags & TT_ENABLE_IOCP_FLAG) + return (void*)TT_SKIP; +#endif + + if (testcase->flags & TT_NEED_THREADS) { + if (!(testcase->flags & TT_FORK)) + return NULL; +#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) + if (evthread_use_pthreads()) + exit(1); +#elif defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED) + if (evthread_use_windows_threads()) + exit(1); +#else + return (void*)TT_SKIP; +#endif + } + + if (testcase->flags & TT_NEED_SOCKETPAIR) { + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, spair) == -1) { + fprintf(stderr, "%s: socketpair\n", __func__); + exit(1); + } + + if (evutil_make_socket_nonblocking(spair[0]) == -1) { + fprintf(stderr, "fcntl(O_NONBLOCK)"); + exit(1); + } + + if (evutil_make_socket_nonblocking(spair[1]) == -1) { + fprintf(stderr, "fcntl(O_NONBLOCK)"); + exit(1); + } + } + if (testcase->flags & TT_NEED_BASE) { + if (testcase->flags & TT_LEGACY) + base = event_init(); + else + base = event_base_new(); + if (!base) + exit(1); + } + if (testcase->flags & TT_ENABLE_IOCP_FLAG) { + if (event_base_start_iocp_(base, 0)<0) { + event_base_free(base); + return (void*)TT_SKIP; + } + } + + if (testcase->flags & TT_NEED_DNS) { + evdns_set_log_fn(dnslogcb); + if (evdns_init()) + return NULL; /* fast failure */ /*XXX asserts. */ + } + + if (testcase->flags & TT_NO_LOGS) + event_set_log_callback(ignore_log_cb); + + data = calloc(1, sizeof(*data)); + if (!data) + exit(1); + data->base = base; + data->pair[0] = spair[0]; + data->pair[1] = spair[1]; + data->setup_data = testcase->setup_data; + return data; +} + +static int +basic_test_cleanup(const struct testcase_t *testcase, void *ptr) +{ + struct basic_test_data *data = ptr; + + if (testcase->flags & TT_NO_LOGS) + event_set_log_callback(NULL); + + if (testcase->flags & TT_NEED_SOCKETPAIR) { + if (data->pair[0] != -1) + evutil_closesocket(data->pair[0]); + if (data->pair[1] != -1) + evutil_closesocket(data->pair[1]); + } + + if (testcase->flags & TT_NEED_DNS) { + evdns_shutdown(0); + } + + if (testcase->flags & TT_NEED_BASE) { + if (data->base) { + event_base_assert_ok_(data->base); + event_base_free(data->base); + } + } + + if (testcase->flags & TT_FORK) + libevent_global_shutdown(); + + free(data); + + return 1; +} + +const struct testcase_setup_t basic_setup = { + basic_test_setup, basic_test_cleanup +}; + +/* The "data" for a legacy test is just a pointer to the void fn(void) + function implementing the test case. We need to set up some globals, + though, since that's where legacy tests expect to find a socketpair + (sometimes) and a global event_base (sometimes). + */ +static void * +legacy_test_setup(const struct testcase_t *testcase) +{ + struct basic_test_data *data = basic_test_setup(testcase); + if (data == (void*)TT_SKIP || data == NULL) + return data; + global_base = data->base; + pair[0] = data->pair[0]; + pair[1] = data->pair[1]; + data->legacy_test_fn = testcase->setup_data; + return data; +} + +/* This function is the implementation of every legacy test case. It + sets test_ok to 0, invokes the test function, and tells tinytest that + the test failed if the test didn't set test_ok to 1. + */ +void +run_legacy_test_fn(void *ptr) +{ + struct basic_test_data *data = ptr; + test_ok = called = 0; + + in_legacy_test_wrapper = 1; + data->legacy_test_fn(); /* This part actually calls the test */ + in_legacy_test_wrapper = 0; + + if (!test_ok) + tt_abort_msg("Legacy unit test failed"); + +end: + test_ok = 0; +} + +/* This function doesn't have to clean up ptr (which is just a pointer + to the test function), but it may need to close the socketpair or + free the event_base. + */ +static int +legacy_test_cleanup(const struct testcase_t *testcase, void *ptr) +{ + int r = basic_test_cleanup(testcase, ptr); + pair[0] = pair[1] = -1; + global_base = NULL; + return r; +} + +const struct testcase_setup_t legacy_setup = { + legacy_test_setup, legacy_test_cleanup +}; + +/* ============================================================ */ + +#if (!defined(EVENT__HAVE_PTHREADS) && !defined(_WIN32)) || defined(EVENT__DISABLE_THREAD_SUPPORT) +struct testcase_t thread_testcases[] = { + { "basic", NULL, TT_SKIP, NULL, NULL }, + END_OF_TESTCASES +}; +#endif + +struct testgroup_t testgroups[] = { + { "main/", main_testcases }, + { "heap/", minheap_testcases }, + { "et/", edgetriggered_testcases }, + { "finalize/", finalize_testcases }, + { "evbuffer/", evbuffer_testcases }, + { "signal/", signal_testcases }, + { "util/", util_testcases }, + { "bufferevent/", bufferevent_testcases }, + { "http/", http_testcases }, + { "dns/", dns_testcases }, + { "evtag/", evtag_testcases }, + { "rpc/", rpc_testcases }, + { "thread/", thread_testcases }, + { "listener/", listener_testcases }, +#ifdef _WIN32 + { "iocp/", iocp_testcases }, + { "iocp/bufferevent/", bufferevent_iocp_testcases }, + { "iocp/listener/", listener_iocp_testcases }, +#endif +#ifdef EVENT__HAVE_OPENSSL + { "ssl/", ssl_testcases }, +#endif + END_OF_GROUPS +}; + +const char *alltests[] = { "+..", NULL }; +const char *livenettests[] = { + "+util/getaddrinfo_live", + "+dns/gethostby..", + "+dns/resolve_reverse", + NULL +}; +const char *finetimetests[] = { + "+util/monotonic_res_precise", + "+util/monotonic_res_fallback", + "+thread/deferred_cb_skew", + "+http/connection_retry", + "+http/https_connection_retry", + NULL +}; +struct testlist_alias_t testaliases[] = { + { "all", alltests }, + { "live_net", livenettests }, + { "fine_timing", finetimetests }, + END_OF_ALIASES +}; + +int libevent_tests_running_in_debug_mode = 0; + +int +main(int argc, const char **argv) +{ +#ifdef _WIN32 + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 2); + + (void) WSAStartup(wVersionRequested, &wsaData); +#endif + +#ifndef _WIN32 + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + return 1; +#endif + +#ifdef _WIN32 + tinytest_skip(testgroups, "http/connection_retry"); + tinytest_skip(testgroups, "http/https_connection_retry"); +#endif + +#ifndef EVENT__DISABLE_THREAD_SUPPORT + if (!getenv("EVENT_NO_DEBUG_LOCKS")) + evthread_enable_lock_debugging(); +#endif + + if (getenv("EVENT_DEBUG_MODE")) { + event_enable_debug_mode(); + libevent_tests_running_in_debug_mode = 1; + } + if (getenv("EVENT_DEBUG_LOGGING_ALL")) { + event_enable_debug_logging(EVENT_DBG_ALL); + } + + tinytest_set_aliases(testaliases); + + evutil_weakrand_seed_(&test_weakrand_state, 0); + + if (tinytest_main(argc,argv,testgroups)) + return 1; + + libevent_global_shutdown(); + + return 0; +} + diff --git a/libs/libevent/docs/test/regress_minheap.c b/libs/libevent/docs/test/regress_minheap.c new file mode 100644 index 0000000000..05db32e26f --- /dev/null +++ b/libs/libevent/docs/test/regress_minheap.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "../minheap-internal.h" + +#include <stdlib.h> +#include "event2/event_struct.h" + +#include "tinytest.h" +#include "tinytest_macros.h" +#include "regress.h" + +static void +set_random_timeout(struct event *ev) +{ + ev->ev_timeout.tv_sec = test_weakrand(); + ev->ev_timeout.tv_usec = test_weakrand() & 0xfffff; + ev->ev_timeout_pos.min_heap_idx = -1; +} + +static void +check_heap(struct min_heap *heap) +{ + unsigned i; + for (i = 1; i < heap->n; ++i) { + unsigned parent_idx = (i-1)/2; + tt_want(evutil_timercmp(&heap->p[i]->ev_timeout, + &heap->p[parent_idx]->ev_timeout, >=)); + } +} + +static void +test_heap_randomized(void *ptr) +{ + struct min_heap heap; + struct event *inserted[1024]; + struct event *e, *last_e; + int i; + + min_heap_ctor_(&heap); + + for (i = 0; i < 1024; ++i) { + inserted[i] = malloc(sizeof(struct event)); + set_random_timeout(inserted[i]); + min_heap_push_(&heap, inserted[i]); + } + check_heap(&heap); + + tt_assert(min_heap_size_(&heap) == 1024); + + for (i = 0; i < 512; ++i) { + min_heap_erase_(&heap, inserted[i]); + if (0 == (i % 32)) + check_heap(&heap); + } + tt_assert(min_heap_size_(&heap) == 512); + + last_e = min_heap_pop_(&heap); + while (1) { + e = min_heap_pop_(&heap); + if (!e) + break; + tt_want(evutil_timercmp(&last_e->ev_timeout, + &e->ev_timeout, <=)); + } + tt_assert(min_heap_size_(&heap) == 0); +end: + for (i = 0; i < 1024; ++i) + free(inserted[i]); + + min_heap_dtor_(&heap); +} + +struct testcase_t minheap_testcases[] = { + { "randomized", test_heap_randomized, 0, NULL, NULL }, + END_OF_TESTCASES +}; diff --git a/libs/libevent/docs/test/regress_rpc.c b/libs/libevent/docs/test/regress_rpc.c new file mode 100644 index 0000000000..01a058cbb2 --- /dev/null +++ b/libs/libevent/docs/test/regress_rpc.c @@ -0,0 +1,905 @@ +/* + * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ + +/* The old tests here need assertions to work. */ +#undef NDEBUG + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#endif + +#include "event2/event-config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/queue.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <signal.h> +#include <unistd.h> +#include <netdb.h> +#endif +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "event2/buffer.h" +#include "event2/event.h" +#include "event2/event_compat.h" +#include "event2/http.h" +#include "event2/http_compat.h" +#include "event2/http_struct.h" +#include "event2/rpc.h" +#include "event2/rpc.h" +#include "event2/rpc_struct.h" +#include "event2/tag.h" +#include "log-internal.h" + +#include "regress.gen.h" + +#include "regress.h" +#include "regress_testutils.h" + +#ifndef NO_PYTHON_EXISTS + +static struct evhttp * +http_setup(ev_uint16_t *pport) +{ + struct evhttp *myhttp; + ev_uint16_t port; + struct evhttp_bound_socket *sock; + + myhttp = evhttp_new(NULL); + if (!myhttp) + event_errx(1, "Could not start web server"); + + /* Try a few different ports */ + sock = evhttp_bind_socket_with_handle(myhttp, "127.0.0.1", 0); + if (!sock) + event_errx(1, "Couldn't open web port"); + + port = regress_get_socket_port(evhttp_bound_socket_get_fd(sock)); + + *pport = port; + return (myhttp); +} + +EVRPC_HEADER(Message, msg, kill) +EVRPC_HEADER(NeverReply, msg, kill) + +EVRPC_GENERATE(Message, msg, kill) +EVRPC_GENERATE(NeverReply, msg, kill) + +static int need_input_hook = 0; +static int need_output_hook = 0; + +static void +MessageCb(EVRPC_STRUCT(Message)* rpc, void *arg) +{ + struct kill* kill_reply = rpc->reply; + + if (need_input_hook) { + struct evhttp_request* req = EVRPC_REQUEST_HTTP(rpc); + const char *header = evhttp_find_header( + req->input_headers, "X-Hook"); + assert(header); + assert(strcmp(header, "input") == 0); + } + + /* we just want to fill in some non-sense */ + EVTAG_ASSIGN(kill_reply, weapon, "dagger"); + EVTAG_ASSIGN(kill_reply, action, "wave around like an idiot"); + + /* no reply to the RPC */ + EVRPC_REQUEST_DONE(rpc); +} + +static EVRPC_STRUCT(NeverReply) *saved_rpc; + +static void +NeverReplyCb(EVRPC_STRUCT(NeverReply)* rpc, void *arg) +{ + test_ok += 1; + saved_rpc = rpc; +} + +static void +rpc_setup(struct evhttp **phttp, ev_uint16_t *pport, struct evrpc_base **pbase) +{ + ev_uint16_t port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + + http = http_setup(&port); + base = evrpc_init(http); + + EVRPC_REGISTER(base, Message, msg, kill, MessageCb, NULL); + EVRPC_REGISTER(base, NeverReply, msg, kill, NeverReplyCb, NULL); + + *phttp = http; + *pport = port; + *pbase = base; + + need_input_hook = 0; + need_output_hook = 0; +} + +static void +rpc_teardown(struct evrpc_base *base) +{ + assert(EVRPC_UNREGISTER(base, Message) == 0); + assert(EVRPC_UNREGISTER(base, NeverReply) == 0); + + evrpc_free(base); +} + +static void +rpc_postrequest_failure(struct evhttp_request *req, void *arg) +{ + if (req->response_code != HTTP_SERVUNAVAIL) { + + fprintf(stderr, "FAILED (response code)\n"); + exit(1); + } + + test_ok = 1; + event_loopexit(NULL); +} + +/* + * Test a malformed payload submitted as an RPC + */ + +static void +rpc_basic_test(void) +{ + ev_uint16_t port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + rpc_setup(&http, &port, &base); + + evcon = evhttp_connection_new("127.0.0.1", port); + tt_assert(evcon); + + /* + * At this point, we want to schedule an HTTP POST request + * server using our make request method. + */ + + req = evhttp_request_new(rpc_postrequest_failure, NULL); + tt_assert(req); + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + evbuffer_add_printf(req->output_buffer, "Some Nonsense"); + + if (evhttp_make_request(evcon, req, + EVHTTP_REQ_POST, + "/.rpc.Message") == -1) { + tt_abort(); + } + + test_ok = 0; + + event_dispatch(); + + evhttp_connection_free(evcon); + + rpc_teardown(base); + + tt_assert(test_ok == 1); + +end: + evhttp_free(http); +} + +static void +rpc_postrequest_done(struct evhttp_request *req, void *arg) +{ + struct kill* kill_reply = NULL; + + if (req->response_code != HTTP_OK) { + fprintf(stderr, "FAILED (response code)\n"); + exit(1); + } + + kill_reply = kill_new(); + + if ((kill_unmarshal(kill_reply, req->input_buffer)) == -1) { + fprintf(stderr, "FAILED (unmarshal)\n"); + exit(1); + } + + kill_free(kill_reply); + + test_ok = 1; + event_loopexit(NULL); +} + +static void +rpc_basic_message(void) +{ + ev_uint16_t port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + struct msg *msg; + + rpc_setup(&http, &port, &base); + + evcon = evhttp_connection_new("127.0.0.1", port); + tt_assert(evcon); + + /* + * At this point, we want to schedule an HTTP POST request + * server using our make request method. + */ + + req = evhttp_request_new(rpc_postrequest_done, NULL); + if (req == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + + /* set up the basic message */ + msg = msg_new(); + EVTAG_ASSIGN(msg, from_name, "niels"); + EVTAG_ASSIGN(msg, to_name, "tester"); + msg_marshal(req->output_buffer, msg); + msg_free(msg); + + if (evhttp_make_request(evcon, req, + EVHTTP_REQ_POST, + "/.rpc.Message") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + test_ok = 0; + + event_dispatch(); + + evhttp_connection_free(evcon); + + rpc_teardown(base); + +end: + evhttp_free(http); +} + +static struct evrpc_pool * +rpc_pool_with_connection(ev_uint16_t port) +{ + struct evhttp_connection *evcon; + struct evrpc_pool *pool; + + pool = evrpc_pool_new(NULL); + assert(pool != NULL); + + evcon = evhttp_connection_new("127.0.0.1", port); + assert(evcon != NULL); + + evrpc_pool_add_connection(pool, evcon); + + return (pool); +} + +static void +GotKillCb(struct evrpc_status *status, + struct msg *msg, struct kill *kill, void *arg) +{ + char *weapon; + char *action; + + if (need_output_hook) { + struct evhttp_request *req = status->http_req; + const char *header = evhttp_find_header( + req->input_headers, "X-Pool-Hook"); + assert(header); + assert(strcmp(header, "ran") == 0); + } + + if (status->error != EVRPC_STATUS_ERR_NONE) + goto done; + + if (EVTAG_GET(kill, weapon, &weapon) == -1) { + fprintf(stderr, "get weapon\n"); + goto done; + } + if (EVTAG_GET(kill, action, &action) == -1) { + fprintf(stderr, "get action\n"); + goto done; + } + + if (strcmp(weapon, "dagger")) + goto done; + + if (strcmp(action, "wave around like an idiot")) + goto done; + + test_ok += 1; + +done: + event_loopexit(NULL); +} + +static void +GotKillCbTwo(struct evrpc_status *status, + struct msg *msg, struct kill *kill, void *arg) +{ + char *weapon; + char *action; + + if (status->error != EVRPC_STATUS_ERR_NONE) + goto done; + + if (EVTAG_GET(kill, weapon, &weapon) == -1) { + fprintf(stderr, "get weapon\n"); + goto done; + } + if (EVTAG_GET(kill, action, &action) == -1) { + fprintf(stderr, "get action\n"); + goto done; + } + + if (strcmp(weapon, "dagger")) + goto done; + + if (strcmp(action, "wave around like an idiot")) + goto done; + + test_ok += 1; + +done: + if (test_ok == 2) + event_loopexit(NULL); +} + +static int +rpc_hook_add_header(void *ctx, struct evhttp_request *req, + struct evbuffer *evbuf, void *arg) +{ + const char *hook_type = arg; + if (strcmp("input", hook_type) == 0) + evhttp_add_header(req->input_headers, "X-Hook", hook_type); + else + evhttp_add_header(req->output_headers, "X-Hook", hook_type); + + assert(evrpc_hook_get_connection(ctx) != NULL); + + return (EVRPC_CONTINUE); +} + +static int +rpc_hook_add_meta(void *ctx, struct evhttp_request *req, + struct evbuffer *evbuf, void *arg) +{ + evrpc_hook_add_meta(ctx, "meta", "test", 5); + + assert(evrpc_hook_get_connection(ctx) != NULL); + + return (EVRPC_CONTINUE); +} + +static int +rpc_hook_remove_header(void *ctx, struct evhttp_request *req, + struct evbuffer *evbuf, void *arg) +{ + const char *header = evhttp_find_header(req->input_headers, "X-Hook"); + void *data = NULL; + size_t data_len = 0; + + assert(header != NULL); + assert(strcmp(header, arg) == 0); + + evhttp_remove_header(req->input_headers, "X-Hook"); + evhttp_add_header(req->input_headers, "X-Pool-Hook", "ran"); + + assert(evrpc_hook_find_meta(ctx, "meta", &data, &data_len) == 0); + assert(data != NULL); + assert(data_len == 5); + + assert(evrpc_hook_get_connection(ctx) != NULL); + + return (EVRPC_CONTINUE); +} + +static void +rpc_basic_client(void) +{ + ev_uint16_t port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + struct evrpc_pool *pool = NULL; + struct msg *msg = NULL; + struct kill *kill = NULL; + + rpc_setup(&http, &port, &base); + + need_input_hook = 1; + need_output_hook = 1; + + assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_add_header, (void*)"input") + != NULL); + assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_add_header, (void*)"output") + != NULL); + + pool = rpc_pool_with_connection(port); + tt_assert(pool); + + assert(evrpc_add_hook(pool, EVRPC_OUTPUT, rpc_hook_add_meta, NULL)); + assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_remove_header, (void*)"output")); + + /* set up the basic message */ + msg = msg_new(); + tt_assert(msg); + EVTAG_ASSIGN(msg, from_name, "niels"); + EVTAG_ASSIGN(msg, to_name, "tester"); + + kill = kill_new(); + + EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL); + + test_ok = 0; + + event_dispatch(); + + tt_assert(test_ok == 1); + + /* we do it twice to make sure that reuse works correctly */ + kill_clear(kill); + + EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL); + + event_dispatch(); + + tt_assert(test_ok == 2); + + /* we do it trice to make sure other stuff works, too */ + kill_clear(kill); + + { + struct evrpc_request_wrapper *ctx = + EVRPC_MAKE_CTX(Message, msg, kill, + pool, msg, kill, GotKillCb, NULL); + evrpc_make_request(ctx); + } + + event_dispatch(); + + rpc_teardown(base); + + tt_assert(test_ok == 3); + +end: + if (msg) + msg_free(msg); + if (kill) + kill_free(kill); + + if (pool) + evrpc_pool_free(pool); + if (http) + evhttp_free(http); + + need_input_hook = 0; + need_output_hook = 0; +} + +/* + * We are testing that the second requests gets send over the same + * connection after the first RPCs completes. + */ +static void +rpc_basic_queued_client(void) +{ + ev_uint16_t port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + struct evrpc_pool *pool = NULL; + struct msg *msg=NULL; + struct kill *kill_one=NULL, *kill_two=NULL; + + rpc_setup(&http, &port, &base); + + pool = rpc_pool_with_connection(port); + tt_assert(pool); + + /* set up the basic message */ + msg = msg_new(); + tt_assert(msg); + EVTAG_ASSIGN(msg, from_name, "niels"); + EVTAG_ASSIGN(msg, to_name, "tester"); + + kill_one = kill_new(); + kill_two = kill_new(); + + EVRPC_MAKE_REQUEST(Message, pool, msg, kill_one, GotKillCbTwo, NULL); + EVRPC_MAKE_REQUEST(Message, pool, msg, kill_two, GotKillCb, NULL); + + test_ok = 0; + + event_dispatch(); + + rpc_teardown(base); + + tt_assert(test_ok == 2); + +end: + if (msg) + msg_free(msg); + if (kill_one) + kill_free(kill_one); + if (kill_two) + kill_free(kill_two); + + if (pool) + evrpc_pool_free(pool); + if (http) + evhttp_free(http); +} + +static void +GotErrorCb(struct evrpc_status *status, + struct msg *msg, struct kill *kill, void *arg) +{ + if (status->error != EVRPC_STATUS_ERR_TIMEOUT) + goto done; + + /* should never be complete but just to check */ + if (kill_complete(kill) == 0) + goto done; + + test_ok += 1; + +done: + event_loopexit(NULL); +} + +/* we just pause the rpc and continue it in the next callback */ + +struct rpc_hook_ctx_ { + void *vbase; + void *ctx; +}; + +static int hook_pause_cb_called=0; + +static void +rpc_hook_pause_cb(evutil_socket_t fd, short what, void *arg) +{ + struct rpc_hook_ctx_ *ctx = arg; + ++hook_pause_cb_called; + evrpc_resume_request(ctx->vbase, ctx->ctx, EVRPC_CONTINUE); + free(arg); +} + +static int +rpc_hook_pause(void *ctx, struct evhttp_request *req, struct evbuffer *evbuf, + void *arg) +{ + struct rpc_hook_ctx_ *tmp = malloc(sizeof(*tmp)); + struct timeval tv; + + assert(tmp != NULL); + tmp->vbase = arg; + tmp->ctx = ctx; + + memset(&tv, 0, sizeof(tv)); + event_once(-1, EV_TIMEOUT, rpc_hook_pause_cb, tmp, &tv); + return EVRPC_PAUSE; +} + +static void +rpc_basic_client_with_pause(void) +{ + ev_uint16_t port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + struct evrpc_pool *pool = NULL; + struct msg *msg = NULL; + struct kill *kill= NULL; + + rpc_setup(&http, &port, &base); + + assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_pause, base)); + assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_pause, base)); + + pool = rpc_pool_with_connection(port); + tt_assert(pool); + assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_pause, pool)); + assert(evrpc_add_hook(pool, EVRPC_OUTPUT, rpc_hook_pause, pool)); + + /* set up the basic message */ + msg = msg_new(); + tt_assert(msg); + EVTAG_ASSIGN(msg, from_name, "niels"); + EVTAG_ASSIGN(msg, to_name, "tester"); + + kill = kill_new(); + + EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL); + + test_ok = 0; + + event_dispatch(); + + tt_int_op(test_ok, ==, 1); + tt_int_op(hook_pause_cb_called, ==, 4); + +end: + if (base) + rpc_teardown(base); + + if (msg) + msg_free(msg); + if (kill) + kill_free(kill); + + if (pool) + evrpc_pool_free(pool); + if (http) + evhttp_free(http); +} + +static void +rpc_client_timeout(void) +{ + ev_uint16_t port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + struct evrpc_pool *pool = NULL; + struct msg *msg = NULL; + struct kill *kill = NULL; + + rpc_setup(&http, &port, &base); + + pool = rpc_pool_with_connection(port); + tt_assert(pool); + + /* set the timeout to 1 second. */ + evrpc_pool_set_timeout(pool, 1); + + /* set up the basic message */ + msg = msg_new(); + tt_assert(msg); + EVTAG_ASSIGN(msg, from_name, "niels"); + EVTAG_ASSIGN(msg, to_name, "tester"); + + kill = kill_new(); + + EVRPC_MAKE_REQUEST(NeverReply, pool, msg, kill, GotErrorCb, NULL); + + test_ok = 0; + + event_dispatch(); + + /* free the saved RPC structure up */ + EVRPC_REQUEST_DONE(saved_rpc); + + rpc_teardown(base); + + tt_assert(test_ok == 2); + +end: + if (msg) + msg_free(msg); + if (kill) + kill_free(kill); + + if (pool) + evrpc_pool_free(pool); + if (http) + evhttp_free(http); +} + +static void +rpc_test(void) +{ + struct msg *msg = NULL, *msg2 = NULL; + struct kill *attack = NULL; + struct run *run = NULL; + struct evbuffer *tmp = evbuffer_new(); + struct timeval tv_start, tv_end; + ev_uint32_t tag; + int i; + + msg = msg_new(); + + tt_assert(msg); + + EVTAG_ASSIGN(msg, from_name, "niels"); + EVTAG_ASSIGN(msg, to_name, "phoenix"); + + if (EVTAG_GET(msg, attack, &attack) == -1) { + tt_abort_msg("Failed to set kill message."); + } + + EVTAG_ASSIGN(attack, weapon, "feather"); + EVTAG_ASSIGN(attack, action, "tickle"); + for (i = 0; i < 3; ++i) { + if (EVTAG_ARRAY_ADD_VALUE(attack, how_often, i) == NULL) { + tt_abort_msg("Failed to add how_often."); + } + } + + evutil_gettimeofday(&tv_start, NULL); + for (i = 0; i < 1000; ++i) { + run = EVTAG_ARRAY_ADD(msg, run); + if (run == NULL) { + tt_abort_msg("Failed to add run message."); + } + EVTAG_ASSIGN(run, how, "very fast but with some data in it"); + EVTAG_ASSIGN(run, fixed_bytes, + (ev_uint8_t*)"012345678901234567890123"); + + if (EVTAG_ARRAY_ADD_VALUE( + run, notes, "this is my note") == NULL) { + tt_abort_msg("Failed to add note."); + } + if (EVTAG_ARRAY_ADD_VALUE(run, notes, "pps") == NULL) { + tt_abort_msg("Failed to add note"); + } + + EVTAG_ASSIGN(run, large_number, 0xdead0a0bcafebeefLL); + EVTAG_ARRAY_ADD_VALUE(run, other_numbers, 0xdead0a0b); + EVTAG_ARRAY_ADD_VALUE(run, other_numbers, 0xbeefcafe); + } + + if (msg_complete(msg) == -1) + tt_abort_msg("Failed to make complete message."); + + evtag_marshal_msg(tmp, 0xdeaf, msg); + + if (evtag_peek(tmp, &tag) == -1) + tt_abort_msg("Failed to peak tag."); + + if (tag != 0xdeaf) + TT_DIE(("Got incorrect tag: %0x.", (unsigned)tag)); + + msg2 = msg_new(); + if (evtag_unmarshal_msg(tmp, 0xdeaf, msg2) == -1) + tt_abort_msg("Failed to unmarshal message."); + + evutil_gettimeofday(&tv_end, NULL); + evutil_timersub(&tv_end, &tv_start, &tv_end); + TT_BLATHER(("(%.1f us/add) ", + (float)tv_end.tv_sec/(float)i * 1000000.0 + + tv_end.tv_usec / (float)i)); + + if (!EVTAG_HAS(msg2, from_name) || + !EVTAG_HAS(msg2, to_name) || + !EVTAG_HAS(msg2, attack)) { + tt_abort_msg("Missing data structures."); + } + + if (EVTAG_GET(msg2, attack, &attack) == -1) { + tt_abort_msg("Could not get attack."); + } + + if (EVTAG_ARRAY_LEN(msg2, run) != i) { + tt_abort_msg("Wrong number of run messages."); + } + + /* get the very first run message */ + if (EVTAG_ARRAY_GET(msg2, run, 0, &run) == -1) { + tt_abort_msg("Failed to get run msg."); + } else { + /* verify the notes */ + char *note_one, *note_two; + ev_uint64_t large_number; + ev_uint32_t short_number; + + if (EVTAG_ARRAY_LEN(run, notes) != 2) { + tt_abort_msg("Wrong number of note strings."); + } + + if (EVTAG_ARRAY_GET(run, notes, 0, ¬e_one) == -1 || + EVTAG_ARRAY_GET(run, notes, 1, ¬e_two) == -1) { + tt_abort_msg("Could not get note strings."); + } + + if (strcmp(note_one, "this is my note") || + strcmp(note_two, "pps")) { + tt_abort_msg("Incorrect note strings encoded."); + } + + if (EVTAG_GET(run, large_number, &large_number) == -1 || + large_number != 0xdead0a0bcafebeefLL) { + tt_abort_msg("Incorrrect large_number."); + } + + if (EVTAG_ARRAY_LEN(run, other_numbers) != 2) { + tt_abort_msg("Wrong number of other_numbers."); + } + + if (EVTAG_ARRAY_GET( + run, other_numbers, 0, &short_number) == -1) { + tt_abort_msg("Could not get short number."); + } + tt_uint_op(short_number, ==, 0xdead0a0b); + + } + tt_int_op(EVTAG_ARRAY_LEN(attack, how_often), ==, 3); + + for (i = 0; i < 3; ++i) { + ev_uint32_t res; + if (EVTAG_ARRAY_GET(attack, how_often, i, &res) == -1) { + TT_DIE(("Cannot get %dth how_often msg.", i)); + } + if ((int)res != i) { + TT_DIE(("Wrong message encoded %d != %d", i, res)); + } + } + + test_ok = 1; +end: + if (msg) + msg_free(msg); + if (msg2) + msg_free(msg2); + if (tmp) + evbuffer_free(tmp); +} + +#define RPC_LEGACY(name) \ + { #name, run_legacy_test_fn, TT_FORK|TT_NEED_BASE|TT_LEGACY, \ + &legacy_setup, \ + rpc_##name } +#else +/* NO_PYTHON_EXISTS */ + +#define RPC_LEGACY(name) \ + { #name, NULL, TT_SKIP, NULL, NULL } + +#endif + +struct testcase_t rpc_testcases[] = { + RPC_LEGACY(basic_test), + RPC_LEGACY(basic_message), + RPC_LEGACY(basic_client), + RPC_LEGACY(basic_queued_client), + RPC_LEGACY(basic_client_with_pause), + RPC_LEGACY(client_timeout), + RPC_LEGACY(test), + + END_OF_TESTCASES, +}; diff --git a/libs/libevent/docs/test/regress_ssl.c b/libs/libevent/docs/test/regress_ssl.c new file mode 100644 index 0000000000..a415952a7d --- /dev/null +++ b/libs/libevent/docs/test/regress_ssl.c @@ -0,0 +1,781 @@ +/* + * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ + +// Get rid of OSX 10.7 and greater deprecation warnings. +#if defined(__APPLE__) && defined(__clang__) +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#endif + +#ifndef _WIN32 +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#endif + +#include "event2/util.h" +#include "event2/event.h" +#include "event2/bufferevent_ssl.h" +#include "event2/bufferevent_struct.h" +#include "event2/buffer.h" +#include "event2/listener.h" + +#include "regress.h" +#include "tinytest.h" +#include "tinytest_macros.h" + +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/pem.h> + +#include <string.h> +#ifdef _WIN32 +#include <io.h> +#define read _read +#define write _write +#else +#include <unistd.h> +#endif + +/* A short pre-generated key, to save the cost of doing an RSA key generation + * step during the unit tests. It's only 512 bits long, and it is published + * in this file, so you would have to be very foolish to consider using it in + * your own code. */ +static const char KEY[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIBOgIBAAJBAKibTEzXjj+sqpipePX1lEk5BNFuL/dDBbw8QCXgaJWikOiKHeJq\n" + "3FQ0OmCnmpkdsPFE4x3ojYmmdgE2i0dJwq0CAwEAAQJAZ08gpUS+qE1IClps/2gG\n" + "AAer6Bc31K2AaiIQvCSQcH440cp062QtWMC3V5sEoWmdLsbAHFH26/9ZHn5zAflp\n" + "gQIhANWOx/UYeR8HD0WREU5kcuSzgzNLwUErHLzxP7U6aojpAiEAyh2H35CjN/P7\n" + "NhcZ4QYw3PeUWpqgJnaE/4i80BSYkSUCIQDLHFhLYLJZ80HwHTADif/ISn9/Ow6b\n" + "p6BWh3DbMar/eQIgBPS6azH5vpp983KXkNv9AL4VZi9ac/b+BeINdzC6GP0CIDmB\n" + "U6GFEQTZ3IfuiVabG5pummdC4DNbcdI+WKrSFNmQ\n" + "-----END RSA PRIVATE KEY-----\n"; + +EVP_PKEY * +ssl_getkey(void) +{ + EVP_PKEY *key; + BIO *bio; + + /* new read-only BIO backed by KEY. */ + bio = BIO_new_mem_buf((char*)KEY, -1); + tt_assert(bio); + + key = PEM_read_bio_PrivateKey(bio,NULL,NULL,NULL); + BIO_free(bio); + tt_assert(key); + + return key; +end: + return NULL; +} + +X509 * +ssl_getcert(void) +{ + /* Dummy code to make a quick-and-dirty valid certificate with + OpenSSL. Don't copy this code into your own program! It does a + number of things in a stupid and insecure way. */ + X509 *x509 = NULL; + X509_NAME *name = NULL; + EVP_PKEY *key = ssl_getkey(); + int nid; + time_t now = time(NULL); + + tt_assert(key); + + x509 = X509_new(); + tt_assert(x509); + tt_assert(0 != X509_set_version(x509, 2)); + tt_assert(0 != ASN1_INTEGER_set(X509_get_serialNumber(x509), + (long)now)); + + name = X509_NAME_new(); + tt_assert(name); + nid = OBJ_txt2nid("commonName"); + tt_assert(NID_undef != nid); + tt_assert(0 != X509_NAME_add_entry_by_NID( + name, nid, MBSTRING_ASC, (unsigned char*)"example.com", + -1, -1, 0)); + + X509_set_subject_name(x509, name); + X509_set_issuer_name(x509, name); + + X509_time_adj(X509_get_notBefore(x509), 0, &now); + now += 3600; + X509_time_adj(X509_get_notAfter(x509), 0, &now); + X509_set_pubkey(x509, key); + tt_assert(0 != X509_sign(x509, key, EVP_sha1())); + + return x509; +end: + X509_free(x509); + return NULL; +} + +static int disable_tls_11_and_12 = 0; +static SSL_CTX *the_ssl_ctx = NULL; + +SSL_CTX * +get_ssl_ctx(void) +{ + if (the_ssl_ctx) + return the_ssl_ctx; + the_ssl_ctx = SSL_CTX_new(SSLv23_method()); + if (!the_ssl_ctx) + return NULL; + if (disable_tls_11_and_12) { +#ifdef SSL_OP_NO_TLSv1_2 + SSL_CTX_set_options(the_ssl_ctx, SSL_OP_NO_TLSv1_2); +#endif +#ifdef SSL_OP_NO_TLSv1_1 + SSL_CTX_set_options(the_ssl_ctx, SSL_OP_NO_TLSv1_1); +#endif + } + return the_ssl_ctx; +} + +void +init_ssl(void) +{ + SSL_library_init(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + if (SSLeay() != OPENSSL_VERSION_NUMBER) { + TT_DECLARE("WARN", ("Version mismatch for openssl: compiled with %lx but running with %lx", (unsigned long)OPENSSL_VERSION_NUMBER, (unsigned long) SSLeay())); + } +} + +/* ==================== + Here's a simple test: we read a number from the input, increment it, and + reply, until we get to 1001. +*/ + +static int test_is_done = 0; +static int n_connected = 0; +static int got_close = 0; +static int got_error = 0; +static int got_timeout = 0; +static int renegotiate_at = -1; +static int stop_when_connected = 0; +static int pending_connect_events = 0; +static struct event_base *exit_base = NULL; + +enum regress_openssl_type +{ + REGRESS_OPENSSL_SOCKETPAIR = 1, + REGRESS_OPENSSL_FILTER = 2, + REGRESS_OPENSSL_RENEGOTIATE = 4, + REGRESS_OPENSSL_OPEN = 8, + REGRESS_OPENSSL_DIRTY_SHUTDOWN = 16, + REGRESS_OPENSSL_FD = 32, + + REGRESS_OPENSSL_CLIENT = 64, + REGRESS_OPENSSL_SERVER = 128, + + REGRESS_OPENSSL_FREED = 256, + REGRESS_OPENSSL_TIMEOUT = 512, + REGRESS_OPENSSL_SLEEP = 1024, +}; + +static void +bufferevent_openssl_check_fd(struct bufferevent *bev, int filter) +{ + if (filter) { + tt_int_op(bufferevent_getfd(bev), ==, -1); + tt_int_op(bufferevent_setfd(bev, -1), ==, -1); + } else { + tt_int_op(bufferevent_getfd(bev), !=, -1); + tt_int_op(bufferevent_setfd(bev, -1), ==, 0); + } + tt_int_op(bufferevent_getfd(bev), ==, -1); + +end: + ; +} +static void +bufferevent_openssl_check_freed(struct bufferevent *bev) +{ + tt_int_op(event_pending(&bev->ev_read, EVLIST_ALL, NULL), ==, 0); + tt_int_op(event_pending(&bev->ev_write, EVLIST_ALL, NULL), ==, 0); + +end: + ; +} + +static void +respond_to_number(struct bufferevent *bev, void *ctx) +{ + struct evbuffer *b = bufferevent_get_input(bev); + char *line; + int n; + + enum regress_openssl_type type; + type = (enum regress_openssl_type)ctx; + + line = evbuffer_readln(b, NULL, EVBUFFER_EOL_LF); + if (! line) + return; + n = atoi(line); + if (n <= 0) + TT_FAIL(("Bad number: %s", line)); + free(line); + TT_BLATHER(("The number was %d", n)); + if (n == 1001) { + ++test_is_done; + bufferevent_free(bev); /* Should trigger close on other side. */ + return; + } + if ((type & REGRESS_OPENSSL_CLIENT) && n == renegotiate_at) { + SSL_renegotiate(bufferevent_openssl_get_ssl(bev)); + } + ++n; + evbuffer_add_printf(bufferevent_get_output(bev), + "%d\n", n); + TT_BLATHER(("Done reading; now writing.")); + bufferevent_enable(bev, EV_WRITE); + bufferevent_disable(bev, EV_READ); +} + +static void +done_writing_cb(struct bufferevent *bev, void *ctx) +{ + struct evbuffer *b = bufferevent_get_output(bev); + if (evbuffer_get_length(b)) + return; + TT_BLATHER(("Done writing.")); + bufferevent_disable(bev, EV_WRITE); + bufferevent_enable(bev, EV_READ); +} + +static void +eventcb(struct bufferevent *bev, short what, void *ctx) +{ + enum regress_openssl_type type; + type = (enum regress_openssl_type)ctx; + + TT_BLATHER(("Got event %d", (int)what)); + if (what & BEV_EVENT_CONNECTED) { + SSL *ssl; + X509 *peer_cert; + ++n_connected; + ssl = bufferevent_openssl_get_ssl(bev); + tt_assert(ssl); + peer_cert = SSL_get_peer_certificate(ssl); + if (type & REGRESS_OPENSSL_SERVER) { + tt_assert(peer_cert == NULL); + } else { + tt_assert(peer_cert != NULL); + } + if (stop_when_connected) { + if (--pending_connect_events == 0) + event_base_loopexit(exit_base, NULL); + } + } else if (what & BEV_EVENT_EOF) { + TT_BLATHER(("Got a good EOF")); + ++got_close; + if (type & REGRESS_OPENSSL_FD) { + bufferevent_openssl_check_fd(bev, type & REGRESS_OPENSSL_FILTER); + } + if (type & REGRESS_OPENSSL_FREED) { + bufferevent_openssl_check_freed(bev); + } + bufferevent_free(bev); + } else if (what & BEV_EVENT_ERROR) { + TT_BLATHER(("Got an error.")); + ++got_error; + if (type & REGRESS_OPENSSL_FD) { + bufferevent_openssl_check_fd(bev, type & REGRESS_OPENSSL_FILTER); + } + if (type & REGRESS_OPENSSL_FREED) { + bufferevent_openssl_check_freed(bev); + } + bufferevent_free(bev); + } else if (what & BEV_EVENT_TIMEOUT) { + TT_BLATHER(("Got timeout.")); + ++got_timeout; + if (type & REGRESS_OPENSSL_FD) { + bufferevent_openssl_check_fd(bev, type & REGRESS_OPENSSL_FILTER); + } + if (type & REGRESS_OPENSSL_FREED) { + bufferevent_openssl_check_freed(bev); + } + bufferevent_free(bev); + } +end: + ; +} + +static void +open_ssl_bufevs(struct bufferevent **bev1_out, struct bufferevent **bev2_out, + struct event_base *base, int is_open, int flags, SSL *ssl1, SSL *ssl2, + evutil_socket_t *fd_pair, struct bufferevent **underlying_pair, + enum regress_openssl_type type) +{ + int state1 = is_open ? BUFFEREVENT_SSL_OPEN :BUFFEREVENT_SSL_CONNECTING; + int state2 = is_open ? BUFFEREVENT_SSL_OPEN :BUFFEREVENT_SSL_ACCEPTING; + int dirty_shutdown = type & REGRESS_OPENSSL_DIRTY_SHUTDOWN; + if (fd_pair) { + *bev1_out = bufferevent_openssl_socket_new( + base, fd_pair[0], ssl1, state1, flags); + *bev2_out = bufferevent_openssl_socket_new( + base, fd_pair[1], ssl2, state2, flags); + } else { + *bev1_out = bufferevent_openssl_filter_new( + base, underlying_pair[0], ssl1, state1, flags); + *bev2_out = bufferevent_openssl_filter_new( + base, underlying_pair[1], ssl2, state2, flags); + + } + bufferevent_setcb(*bev1_out, respond_to_number, done_writing_cb, + eventcb, (void*)(REGRESS_OPENSSL_CLIENT | (long)type)); + bufferevent_setcb(*bev2_out, respond_to_number, done_writing_cb, + eventcb, (void*)(REGRESS_OPENSSL_SERVER | (long)type)); + + bufferevent_openssl_set_allow_dirty_shutdown(*bev1_out, dirty_shutdown); + bufferevent_openssl_set_allow_dirty_shutdown(*bev2_out, dirty_shutdown); +} + +static void +regress_bufferevent_openssl(void *arg) +{ + struct basic_test_data *data = arg; + + struct bufferevent *bev1, *bev2; + SSL *ssl1, *ssl2; + X509 *cert = ssl_getcert(); + EVP_PKEY *key = ssl_getkey(); + int flags = BEV_OPT_DEFER_CALLBACKS; + struct bufferevent *bev_ll[2] = { NULL, NULL }; + evutil_socket_t *fd_pair = NULL; + + enum regress_openssl_type type; + type = (enum regress_openssl_type)data->setup_data; + + tt_assert(cert); + tt_assert(key); + + init_ssl(); + + if (type & REGRESS_OPENSSL_RENEGOTIATE) { + if (SSLeay() >= 0x10001000 && + SSLeay() < 0x1000104f) { + /* 1.0.1 up to 1.0.1c has a bug where TLS1.1 and 1.2 + * can't renegotiate with themselves. Disable. */ + disable_tls_11_and_12 = 1; + } + renegotiate_at = 600; + } + + ssl1 = SSL_new(get_ssl_ctx()); + ssl2 = SSL_new(get_ssl_ctx()); + + SSL_use_certificate(ssl2, cert); + SSL_use_PrivateKey(ssl2, key); + + if (!(type & REGRESS_OPENSSL_OPEN)) + flags |= BEV_OPT_CLOSE_ON_FREE; + + if (!(type & REGRESS_OPENSSL_FILTER)) { + tt_assert(type & REGRESS_OPENSSL_SOCKETPAIR); + fd_pair = data->pair; + } else { + bev_ll[0] = bufferevent_socket_new(data->base, data->pair[0], + BEV_OPT_CLOSE_ON_FREE); + bev_ll[1] = bufferevent_socket_new(data->base, data->pair[1], + BEV_OPT_CLOSE_ON_FREE); + } + + open_ssl_bufevs(&bev1, &bev2, data->base, 0, flags, ssl1, ssl2, + fd_pair, bev_ll, type); + + if (!(type & REGRESS_OPENSSL_FILTER)) { + tt_int_op(bufferevent_getfd(bev1), ==, data->pair[0]); + } else { + tt_ptr_op(bufferevent_get_underlying(bev1), ==, bev_ll[0]); + } + + if (type & REGRESS_OPENSSL_OPEN) { + pending_connect_events = 2; + stop_when_connected = 1; + exit_base = data->base; + event_base_dispatch(data->base); + /* Okay, now the renegotiation is done. Make new + * bufferevents to test opening in BUFFEREVENT_SSL_OPEN */ + flags |= BEV_OPT_CLOSE_ON_FREE; + bufferevent_free(bev1); + bufferevent_free(bev2); + bev1 = bev2 = NULL; + open_ssl_bufevs(&bev1, &bev2, data->base, 1, flags, ssl1, ssl2, + fd_pair, bev_ll, type); + } + + if (!(type & REGRESS_OPENSSL_TIMEOUT)) { + bufferevent_enable(bev1, EV_READ|EV_WRITE); + bufferevent_enable(bev2, EV_READ|EV_WRITE); + + evbuffer_add_printf(bufferevent_get_output(bev1), "1\n"); + + event_base_dispatch(data->base); + + tt_assert(test_is_done == 1); + tt_assert(n_connected == 2); + + /* We don't handle shutdown properly yet */ + if (type & REGRESS_OPENSSL_DIRTY_SHUTDOWN) { + tt_int_op(got_close, ==, 1); + tt_int_op(got_error, ==, 0); + } else { + tt_int_op(got_error, ==, 1); + } + tt_int_op(got_timeout, ==, 0); + } else { + struct timeval t = { 2, 0 }; + + bufferevent_enable(bev1, EV_READ|EV_WRITE); + bufferevent_disable(bev2, EV_READ|EV_WRITE); + + bufferevent_set_timeouts(bev1, &t, &t); + + evbuffer_add_printf(bufferevent_get_output(bev1), "1\n"); + + event_base_dispatch(data->base); + + tt_assert(test_is_done == 0); + tt_assert(n_connected == 0); + + tt_int_op(got_close, ==, 0); + tt_int_op(got_error, ==, 0); + tt_int_op(got_timeout, ==, 1); + } +end: + return; +} + +static void +acceptcb_deferred(evutil_socket_t fd, short events, void *arg) +{ + struct bufferevent *bev = arg; + bufferevent_enable(bev, EV_READ|EV_WRITE); +} +static void +acceptcb(struct evconnlistener *listener, evutil_socket_t fd, + struct sockaddr *addr, int socklen, void *arg) +{ + struct basic_test_data *data = arg; + struct bufferevent *bev; + enum regress_openssl_type type; + SSL *ssl = SSL_new(get_ssl_ctx()); + + type = (enum regress_openssl_type)data->setup_data; + + SSL_use_certificate(ssl, ssl_getcert()); + SSL_use_PrivateKey(ssl, ssl_getkey()); + + bev = bufferevent_openssl_socket_new( + data->base, + fd, + ssl, + BUFFEREVENT_SSL_ACCEPTING, + BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); + + bufferevent_setcb(bev, respond_to_number, NULL, eventcb, + (void*)(REGRESS_OPENSSL_SERVER)); + + if (type & REGRESS_OPENSSL_SLEEP) { + struct timeval when = { 1, 0 }; + event_base_once(data->base, -1, EV_TIMEOUT, + acceptcb_deferred, bev, &when); + bufferevent_disable(bev, EV_READ|EV_WRITE); + } else { + bufferevent_enable(bev, EV_READ|EV_WRITE); + } + + /* Only accept once, then disable ourself. */ + evconnlistener_disable(listener); +} + +struct rwcount +{ + int fd; + size_t read; + size_t write; +}; +static int +bio_rwcount_new(BIO *b) +{ + b->init = 0; + b->num = -1; + b->ptr = NULL; + b->flags = 0; + return 1; +} +static int +bio_rwcount_free(BIO *b) +{ + if (!b) + return 0; + if (b->shutdown) { + b->init = 0; + b->flags = 0; + b->ptr = NULL; + } + return 1; +} +static int +bio_rwcount_read(BIO *b, char *out, int outlen) +{ + struct rwcount *rw = b->ptr; + ev_ssize_t ret = read(rw->fd, out, outlen); + ++rw->read; + if (ret == -1 && errno == EAGAIN) { + BIO_set_retry_read(b); + } + return ret; +} +static int +bio_rwcount_write(BIO *b, const char *in, int inlen) +{ + + struct rwcount *rw = b->ptr; + ev_ssize_t ret = write(rw->fd, in, inlen); + ++rw->write; + if (ret == -1 && errno == EAGAIN) { + BIO_set_retry_write(b); + } + return ret; +} +static long +bio_rwcount_ctrl(BIO *b, int cmd, long num, void *ptr) +{ + long ret = 0; + switch (cmd) { + case BIO_CTRL_GET_CLOSE: + ret = b->shutdown; + break; + case BIO_CTRL_SET_CLOSE: + b->shutdown = (int)num; + break; + case BIO_CTRL_PENDING: + ret = 0; + break; + case BIO_CTRL_WPENDING: + ret = 0; + break; + case BIO_CTRL_DUP: + case BIO_CTRL_FLUSH: + ret = 1; + break; + } + return ret; +} +static int +bio_rwcount_puts(BIO *b, const char *s) +{ + return bio_rwcount_write(b, s, strlen(s)); +} +#define BIO_TYPE_LIBEVENT_RWCOUNT 0xff1 +static BIO_METHOD methods_rwcount = { + BIO_TYPE_LIBEVENT_RWCOUNT, "rwcount", + bio_rwcount_write, + bio_rwcount_read, + bio_rwcount_puts, + NULL /* bio_rwcount_gets */, + bio_rwcount_ctrl, + bio_rwcount_new, + bio_rwcount_free, + NULL /* callback_ctrl */, +}; +static BIO_METHOD * +BIO_s_rwcount(void) +{ + return &methods_rwcount; +} +static BIO * +BIO_new_rwcount(int close_flag) +{ + BIO *result; + if (!(result = BIO_new(BIO_s_rwcount()))) + return NULL; + result->init = 1; + result->ptr = NULL; + result->shutdown = !!close_flag; + return result; +} + +static void +regress_bufferevent_openssl_connect(void *arg) +{ + struct basic_test_data *data = arg; + + struct event_base *base = data->base; + + struct evconnlistener *listener; + struct bufferevent *bev; + struct sockaddr_in sin; + struct sockaddr_storage ss; + ev_socklen_t slen; + SSL *ssl; + BIO *bio; + struct rwcount rw = { -1, 0, 0 }; + enum regress_openssl_type type; + + type = (enum regress_openssl_type)data->setup_data; + + init_ssl(); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(0x7f000001); + + memset(&ss, 0, sizeof(ss)); + slen = sizeof(ss); + + listener = evconnlistener_new_bind(base, acceptcb, data, + LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, + -1, (struct sockaddr *)&sin, sizeof(sin)); + + tt_assert(listener); + tt_assert(evconnlistener_get_fd(listener) >= 0); + + ssl = SSL_new(get_ssl_ctx()); + tt_assert(ssl); + + bev = bufferevent_openssl_socket_new( + data->base, -1, ssl, + BUFFEREVENT_SSL_CONNECTING, + BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); + tt_assert(bev); + + bufferevent_setcb(bev, respond_to_number, NULL, eventcb, + (void*)(REGRESS_OPENSSL_CLIENT)); + + tt_assert(getsockname(evconnlistener_get_fd(listener), + (struct sockaddr*)&ss, &slen) == 0); + tt_assert(slen == sizeof(struct sockaddr_in)); + tt_int_op(((struct sockaddr*)&ss)->sa_family, ==, AF_INET); + + tt_assert(0 == + bufferevent_socket_connect(bev, (struct sockaddr*)&ss, slen)); + /* Possible only when we have fd, since be_openssl can and will overwrite + * bio otherwise before */ + if (type & REGRESS_OPENSSL_SLEEP) { + rw.fd = bufferevent_getfd(bev); + bio = BIO_new_rwcount(0); + tt_assert(bio); + bio->ptr = &rw; + SSL_set_bio(ssl, bio, bio); + } + evbuffer_add_printf(bufferevent_get_output(bev), "1\n"); + bufferevent_enable(bev, EV_READ|EV_WRITE); + + event_base_dispatch(base); + + tt_int_op(rw.read, <=, 100); + tt_int_op(rw.write, <=, 100); +end: + ; +} + +struct testcase_t ssl_testcases[] = { +#define T(a) ((void *)(a)) + { "bufferevent_socketpair", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, T(REGRESS_OPENSSL_SOCKETPAIR) }, + { "bufferevent_filter", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, T(REGRESS_OPENSSL_FILTER) }, + { "bufferevent_renegotiate_socketpair", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_RENEGOTIATE) }, + { "bufferevent_renegotiate_filter", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_RENEGOTIATE) }, + { "bufferevent_socketpair_startopen", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_OPEN) }, + { "bufferevent_filter_startopen", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_OPEN) }, + + { "bufferevent_socketpair_dirty_shutdown", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, + { "bufferevent_filter_dirty_shutdown", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, + { "bufferevent_renegotiate_socketpair_dirty_shutdown", + regress_bufferevent_openssl, + TT_ISOLATED, + &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_RENEGOTIATE | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, + { "bufferevent_renegotiate_filter_dirty_shutdown", + regress_bufferevent_openssl, + TT_ISOLATED, + &basic_setup, + T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_RENEGOTIATE | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, + { "bufferevent_socketpair_startopen_dirty_shutdown", + regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_OPEN | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, + { "bufferevent_filter_startopen_dirty_shutdown", + regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_OPEN | REGRESS_OPENSSL_DIRTY_SHUTDOWN) }, + + { "bufferevent_socketpair_fd", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_FD) }, + { "bufferevent_socketpair_freed", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_FREED) }, + { "bufferevent_socketpair_freed_fd", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_FREED | REGRESS_OPENSSL_FD) }, + { "bufferevent_filter_freed_fd", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_FILTER | REGRESS_OPENSSL_FREED | REGRESS_OPENSSL_FD) }, + + { "bufferevent_socketpair_timeout", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_TIMEOUT) }, + { "bufferevent_socketpair_timeout_freed_fd", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_TIMEOUT | REGRESS_OPENSSL_FREED | REGRESS_OPENSSL_FD) }, + + { "bufferevent_connect", regress_bufferevent_openssl_connect, + TT_FORK|TT_NEED_BASE, &basic_setup, NULL }, + { "bufferevent_connect_sleep", regress_bufferevent_openssl_connect, + TT_FORK|TT_NEED_BASE, &basic_setup, T(REGRESS_OPENSSL_SLEEP) }, + +#undef T + + END_OF_TESTCASES, +}; diff --git a/libs/libevent/docs/test/regress_testutils.c b/libs/libevent/docs/test/regress_testutils.c new file mode 100644 index 0000000000..7554a5413f --- /dev/null +++ b/libs/libevent/docs/test/regress_testutils.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2010-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "../util-internal.h" + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#include <ws2tcpip.h> +#endif + +#include "event2/event-config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/queue.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <signal.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#endif +#ifdef EVENT__HAVE_NETINET_IN6_H +#include <netinet/in6.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "event2/dns.h" +#include "event2/dns_struct.h" +#include "event2/event.h" +#include "event2/event_compat.h" +#include "event2/util.h" +#include "event2/listener.h" +#include "event2/bufferevent.h" +#include "log-internal.h" +#include "regress.h" +#include "regress_testutils.h" + +/* globals */ +static struct evdns_server_port *dns_port; +evutil_socket_t dns_sock = -1; + +/* Helper: return the port that a socket is bound on, in host order. */ +int +regress_get_socket_port(evutil_socket_t fd) +{ + struct sockaddr_storage ss; + ev_socklen_t socklen = sizeof(ss); + if (getsockname(fd, (struct sockaddr*)&ss, &socklen) != 0) + return -1; + if (ss.ss_family == AF_INET) + return ntohs( ((struct sockaddr_in*)&ss)->sin_port); + else if (ss.ss_family == AF_INET6) + return ntohs( ((struct sockaddr_in6*)&ss)->sin6_port); + else + return -1; +} + +struct evdns_server_port * +regress_get_dnsserver(struct event_base *base, + ev_uint16_t *portnum, + evutil_socket_t *psock, + evdns_request_callback_fn_type cb, + void *arg) +{ + struct evdns_server_port *port = NULL; + evutil_socket_t sock; + struct sockaddr_in my_addr; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + tt_abort_perror("socket"); + } + + evutil_make_socket_nonblocking(sock); + + memset(&my_addr, 0, sizeof(my_addr)); + my_addr.sin_family = AF_INET; + my_addr.sin_port = htons(*portnum); + my_addr.sin_addr.s_addr = htonl(0x7f000001UL); + if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) < 0) { + evutil_closesocket(sock); + tt_abort_perror("bind"); + } + port = evdns_add_server_port_with_base(base, sock, 0, cb, arg); + if (!*portnum) + *portnum = regress_get_socket_port(sock); + if (psock) + *psock = sock; + + return port; +end: + return NULL; +} + +void +regress_clean_dnsserver(void) +{ + if (dns_port) + evdns_close_server_port(dns_port); + if (dns_sock >= 0) + evutil_closesocket(dns_sock); +} + +static void strtolower(char *s) +{ + while (*s) { + *s = EVUTIL_TOLOWER_(*s); + ++s; + } +} +void +regress_dns_server_cb(struct evdns_server_request *req, void *data) +{ + struct regress_dns_server_table *tab = data; + char *question; + + if (req->nquestions != 1) + TT_DIE(("Only handling one question at a time; got %d", + req->nquestions)); + + question = req->questions[0]->name; + + while (tab->q && evutil_ascii_strcasecmp(question, tab->q) && + strcmp("*", tab->q)) + ++tab; + if (tab->q == NULL) + TT_DIE(("Unexpected question: '%s'", question)); + + ++tab->seen; + + if (tab->lower) + strtolower(question); + + if (!strcmp(tab->anstype, "err")) { + int err = atoi(tab->ans); + tt_assert(! evdns_server_request_respond(req, err)); + return; + } else if (!strcmp(tab->anstype, "errsoa")) { + int err = atoi(tab->ans); + char soa_record[] = + "\x04" "dns1" "\x05" "icann" "\x03" "org" "\0" + "\x0a" "hostmaster" "\x05" "icann" "\x03" "org" "\0" + "\x77\xde\x5e\xba" /* serial */ + "\x00\x00\x1c\x20" /* refreshtime = 2h */ + "\x00\x00\x0e\x10" /* retry = 1h */ + "\x00\x12\x75\x00" /* expiration = 14d */ + "\x00\x00\x0e\x10" /* min.ttl = 1h */ + ; + evdns_server_request_add_reply( + req, EVDNS_AUTHORITY_SECTION, + "example.com", EVDNS_TYPE_SOA, EVDNS_CLASS_INET, + 42, sizeof(soa_record) - 1, 0, soa_record); + tt_assert(! evdns_server_request_respond(req, err)); + return; + } else if (!strcmp(tab->anstype, "A")) { + struct in_addr in; + if (!evutil_inet_pton(AF_INET, tab->ans, &in)) { + TT_DIE(("Bad A value %s in table", tab->ans)); + } + evdns_server_request_add_a_reply(req, question, 1, &in.s_addr, + 100); + } else if (!strcmp(tab->anstype, "AAAA")) { + struct in6_addr in6; + if (!evutil_inet_pton(AF_INET6, tab->ans, &in6)) { + TT_DIE(("Bad AAAA value %s in table", tab->ans)); + } + evdns_server_request_add_aaaa_reply(req, + question, 1, &in6.s6_addr, 100); + } else { + TT_DIE(("Weird table entry with type '%s'", tab->anstype)); + } + tt_assert(! evdns_server_request_respond(req, 0)) + return; +end: + tt_want(! evdns_server_request_drop(req)); +} + +int +regress_dnsserver(struct event_base *base, ev_uint16_t *port, + struct regress_dns_server_table *search_table) +{ + dns_port = regress_get_dnsserver(base, port, &dns_sock, + regress_dns_server_cb, search_table); + return dns_port != NULL; +} + +int +regress_get_listener_addr(struct evconnlistener *lev, + struct sockaddr *sa, ev_socklen_t *socklen) +{ + evutil_socket_t s = evconnlistener_get_fd(lev); + if (s <= 0) + return -1; + return getsockname(s, sa, socklen); +} diff --git a/libs/libevent/docs/test/regress_testutils.h b/libs/libevent/docs/test/regress_testutils.h new file mode 100644 index 0000000000..040516a585 --- /dev/null +++ b/libs/libevent/docs/test/regress_testutils.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2010-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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 REGRESS_TESTUTILS_H_INCLUDED_ +#define REGRESS_TESTUTILS_H_INCLUDED_ + +#include "event2/dns.h" + +struct regress_dns_server_table { + const char *q; + const char *anstype; + const char *ans; + int seen; + int lower; +}; + +struct evdns_server_port * +regress_get_dnsserver(struct event_base *base, + ev_uint16_t *portnum, + evutil_socket_t *psock, + evdns_request_callback_fn_type cb, + void *arg); + +/* Helper: return the port that a socket is bound on, in host order. */ +int regress_get_socket_port(evutil_socket_t fd); + +/* used to look up pre-canned responses in a search table */ +void regress_dns_server_cb( + struct evdns_server_request *req, void *data); + +/* globally allocates a dns server that serves from a search table */ +int regress_dnsserver(struct event_base *base, ev_uint16_t *port, + struct regress_dns_server_table *seach_table); + +/* clean up the global dns server resources */ +void regress_clean_dnsserver(void); + +struct evconnlistener; +struct sockaddr; +int regress_get_listener_addr(struct evconnlistener *lev, + struct sockaddr *sa, ev_socklen_t *socklen); + +#endif /* REGRESS_TESTUTILS_H_INCLUDED_ */ + diff --git a/libs/libevent/docs/test/regress_thread.c b/libs/libevent/docs/test/regress_thread.c new file mode 100644 index 0000000000..9ff6a8fa88 --- /dev/null +++ b/libs/libevent/docs/test/regress_thread.c @@ -0,0 +1,590 @@ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "util-internal.h" + +/* The old tests here need assertions to work. */ +#undef NDEBUG + +#include "event2/event-config.h" + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef EVENT__HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef EVENT__HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif + +#ifdef EVENT__HAVE_PTHREADS +#include <pthread.h> +#elif defined(_WIN32) +#include <process.h> +#endif +#include <assert.h> +#ifdef EVENT__HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <time.h> + +#include "sys/queue.h" + +#include "event2/event.h" +#include "event2/event_struct.h" +#include "event2/thread.h" +#include "event2/util.h" +#include "evthread-internal.h" +#include "event-internal.h" +#include "defer-internal.h" +#include "regress.h" +#include "tinytest_macros.h" +#include "time-internal.h" +#include "regress_thread.h" + +struct cond_wait { + void *lock; + void *cond; +}; + +static void +wake_all_timeout(evutil_socket_t fd, short what, void *arg) +{ + struct cond_wait *cw = arg; + EVLOCK_LOCK(cw->lock, 0); + EVTHREAD_COND_BROADCAST(cw->cond); + EVLOCK_UNLOCK(cw->lock, 0); + +} + +static void +wake_one_timeout(evutil_socket_t fd, short what, void *arg) +{ + struct cond_wait *cw = arg; + EVLOCK_LOCK(cw->lock, 0); + EVTHREAD_COND_SIGNAL(cw->cond); + EVLOCK_UNLOCK(cw->lock, 0); +} + +#define NUM_THREADS 100 +#define NUM_ITERATIONS 100 +void *count_lock; +static int count; + +static THREAD_FN +basic_thread(void *arg) +{ + struct cond_wait cw; + struct event_base *base = arg; + struct event ev; + int i = 0; + + EVTHREAD_ALLOC_LOCK(cw.lock, 0); + EVTHREAD_ALLOC_COND(cw.cond); + assert(cw.lock); + assert(cw.cond); + + evtimer_assign(&ev, base, wake_all_timeout, &cw); + for (i = 0; i < NUM_ITERATIONS; i++) { + struct timeval tv; + evutil_timerclear(&tv); + tv.tv_sec = 0; + tv.tv_usec = 3000; + + EVLOCK_LOCK(cw.lock, 0); + /* we need to make sure that event does not happen before + * we get to wait on the conditional variable */ + assert(evtimer_add(&ev, &tv) == 0); + + assert(EVTHREAD_COND_WAIT(cw.cond, cw.lock) == 0); + EVLOCK_UNLOCK(cw.lock, 0); + + EVLOCK_LOCK(count_lock, 0); + ++count; + EVLOCK_UNLOCK(count_lock, 0); + } + + /* exit the loop only if all threads fired all timeouts */ + EVLOCK_LOCK(count_lock, 0); + if (count >= NUM_THREADS * NUM_ITERATIONS) + event_base_loopexit(base, NULL); + EVLOCK_UNLOCK(count_lock, 0); + + EVTHREAD_FREE_LOCK(cw.lock, 0); + EVTHREAD_FREE_COND(cw.cond); + + THREAD_RETURN(); +} + +static int notification_fd_used = 0; +#ifndef _WIN32 +static int got_sigchld = 0; +static void +sigchld_cb(evutil_socket_t fd, short event, void *arg) +{ + struct timeval tv; + struct event_base *base = arg; + + got_sigchld++; + tv.tv_usec = 100000; + tv.tv_sec = 0; + event_base_loopexit(base, &tv); +} + + +static void +notify_fd_cb(evutil_socket_t fd, short event, void *arg) +{ + ++notification_fd_used; +} +#endif + +static void +thread_basic(void *arg) +{ + THREAD_T threads[NUM_THREADS]; + struct event ev; + struct timeval tv; + int i; + struct basic_test_data *data = arg; + struct event_base *base = data->base; + + struct event *notification_event = NULL; + struct event *sigchld_event = NULL; + + EVTHREAD_ALLOC_LOCK(count_lock, 0); + tt_assert(count_lock); + + tt_assert(base); + if (evthread_make_base_notifiable(base)<0) { + tt_abort_msg("Couldn't make base notifiable!"); + } + +#ifndef _WIN32 + if (data->setup_data && !strcmp(data->setup_data, "forking")) { + pid_t pid; + int status; + sigchld_event = evsignal_new(base, SIGCHLD, sigchld_cb, base); + /* This piggybacks on the th_notify_fd weirdly, and looks + * inside libevent internals. Not a good idea in non-testing + * code! */ + notification_event = event_new(base, + base->th_notify_fd[0], EV_READ|EV_PERSIST, notify_fd_cb, + NULL); + event_add(sigchld_event, NULL); + event_add(notification_event, NULL); + + if ((pid = fork()) == 0) { + event_del(notification_event); + if (event_reinit(base) < 0) { + TT_FAIL(("reinit")); + exit(1); + } + event_assign(notification_event, base, + base->th_notify_fd[0], EV_READ|EV_PERSIST, + notify_fd_cb, NULL); + event_add(notification_event, NULL); + goto child; + } + + event_base_dispatch(base); + + if (waitpid(pid, &status, 0) == -1) + tt_abort_perror("waitpid"); + TT_BLATHER(("Waitpid okay\n")); + + tt_assert(got_sigchld); + tt_int_op(notification_fd_used, ==, 0); + + goto end; + } + +child: +#endif + for (i = 0; i < NUM_THREADS; ++i) + THREAD_START(threads[i], basic_thread, base); + + evtimer_assign(&ev, base, NULL, NULL); + evutil_timerclear(&tv); + tv.tv_sec = 1000; + event_add(&ev, &tv); + + event_base_dispatch(base); + + for (i = 0; i < NUM_THREADS; ++i) + THREAD_JOIN(threads[i]); + + event_del(&ev); + + tt_int_op(count, ==, NUM_THREADS * NUM_ITERATIONS); + + EVTHREAD_FREE_LOCK(count_lock, 0); + + TT_BLATHER(("notifiations==%d", notification_fd_used)); + +end: + + if (notification_event) + event_free(notification_event); + if (sigchld_event) + event_free(sigchld_event); +} + +#undef NUM_THREADS +#define NUM_THREADS 10 + +struct alerted_record { + struct cond_wait *cond; + struct timeval delay; + struct timeval alerted_at; + int timed_out; +}; + +static THREAD_FN +wait_for_condition(void *arg) +{ + struct alerted_record *rec = arg; + int r; + + EVLOCK_LOCK(rec->cond->lock, 0); + if (rec->delay.tv_sec || rec->delay.tv_usec) { + r = EVTHREAD_COND_WAIT_TIMED(rec->cond->cond, rec->cond->lock, + &rec->delay); + } else { + r = EVTHREAD_COND_WAIT(rec->cond->cond, rec->cond->lock); + } + EVLOCK_UNLOCK(rec->cond->lock, 0); + + evutil_gettimeofday(&rec->alerted_at, NULL); + if (r == 1) + rec->timed_out = 1; + + THREAD_RETURN(); +} + +static void +thread_conditions_simple(void *arg) +{ + struct timeval tv_signal, tv_timeout, tv_broadcast; + struct alerted_record alerted[NUM_THREADS]; + THREAD_T threads[NUM_THREADS]; + struct cond_wait cond; + int i; + struct timeval launched_at; + struct event wake_one; + struct event wake_all; + struct basic_test_data *data = arg; + struct event_base *base = data->base; + int n_timed_out=0, n_signal=0, n_broadcast=0; + + tv_signal.tv_sec = tv_timeout.tv_sec = tv_broadcast.tv_sec = 0; + tv_signal.tv_usec = 30*1000; + tv_timeout.tv_usec = 150*1000; + tv_broadcast.tv_usec = 500*1000; + + EVTHREAD_ALLOC_LOCK(cond.lock, EVTHREAD_LOCKTYPE_RECURSIVE); + EVTHREAD_ALLOC_COND(cond.cond); + tt_assert(cond.lock); + tt_assert(cond.cond); + for (i = 0; i < NUM_THREADS; ++i) { + memset(&alerted[i], 0, sizeof(struct alerted_record)); + alerted[i].cond = &cond; + } + + /* Threads 5 and 6 will be allowed to time out */ + memcpy(&alerted[5].delay, &tv_timeout, sizeof(tv_timeout)); + memcpy(&alerted[6].delay, &tv_timeout, sizeof(tv_timeout)); + + evtimer_assign(&wake_one, base, wake_one_timeout, &cond); + evtimer_assign(&wake_all, base, wake_all_timeout, &cond); + + evutil_gettimeofday(&launched_at, NULL); + + /* Launch the threads... */ + for (i = 0; i < NUM_THREADS; ++i) { + THREAD_START(threads[i], wait_for_condition, &alerted[i]); + } + + /* Start the timers... */ + tt_int_op(event_add(&wake_one, &tv_signal), ==, 0); + tt_int_op(event_add(&wake_all, &tv_broadcast), ==, 0); + + /* And run for a bit... */ + event_base_dispatch(base); + + /* And wait till the threads are done. */ + for (i = 0; i < NUM_THREADS; ++i) + THREAD_JOIN(threads[i]); + + /* Now, let's see what happened. At least one of 5 or 6 should + * have timed out. */ + n_timed_out = alerted[5].timed_out + alerted[6].timed_out; + tt_int_op(n_timed_out, >=, 1); + tt_int_op(n_timed_out, <=, 2); + + for (i = 0; i < NUM_THREADS; ++i) { + const struct timeval *target_delay; + struct timeval target_time, actual_delay; + if (alerted[i].timed_out) { + TT_BLATHER(("%d looks like a timeout\n", i)); + target_delay = &tv_timeout; + tt_assert(i == 5 || i == 6); + } else if (evutil_timerisset(&alerted[i].alerted_at)) { + long diff1,diff2; + evutil_timersub(&alerted[i].alerted_at, + &launched_at, &actual_delay); + diff1 = timeval_msec_diff(&actual_delay, + &tv_signal); + diff2 = timeval_msec_diff(&actual_delay, + &tv_broadcast); + if (labs(diff1) < labs(diff2)) { + TT_BLATHER(("%d looks like a signal\n", i)); + target_delay = &tv_signal; + ++n_signal; + } else { + TT_BLATHER(("%d looks like a broadcast\n", i)); + target_delay = &tv_broadcast; + ++n_broadcast; + } + } else { + TT_FAIL(("Thread %d never got woken", i)); + continue; + } + evutil_timeradd(target_delay, &launched_at, &target_time); + test_timeval_diff_leq(&target_time, &alerted[i].alerted_at, + 0, 50); + } + tt_int_op(n_broadcast + n_signal + n_timed_out, ==, NUM_THREADS); + tt_int_op(n_signal, ==, 1); + +end: + EVTHREAD_FREE_LOCK(cond.lock, EVTHREAD_LOCKTYPE_RECURSIVE); + EVTHREAD_FREE_COND(cond.cond); +} + +#define CB_COUNT 128 +#define QUEUE_THREAD_COUNT 8 + +static void +SLEEP_MS(int ms) +{ + struct timeval tv; + tv.tv_sec = ms/1000; + tv.tv_usec = (ms%1000)*1000; + evutil_usleep_(&tv); +} + +struct deferred_test_data { + struct event_callback cbs[CB_COUNT]; + struct event_base *queue; +}; + +static struct timeval timer_start = {0,0}; +static struct timeval timer_end = {0,0}; +static unsigned callback_count = 0; +static THREAD_T load_threads[QUEUE_THREAD_COUNT]; +static struct deferred_test_data deferred_data[QUEUE_THREAD_COUNT]; + +static void +deferred_callback(struct event_callback *cb, void *arg) +{ + SLEEP_MS(1); + callback_count += 1; +} + +static THREAD_FN +load_deferred_queue(void *arg) +{ + struct deferred_test_data *data = arg; + size_t i; + + for (i = 0; i < CB_COUNT; ++i) { + event_deferred_cb_init_(&data->cbs[i], 0, deferred_callback, + NULL); + event_deferred_cb_schedule_(data->queue, &data->cbs[i]); + SLEEP_MS(1); + } + + THREAD_RETURN(); +} + +static void +timer_callback(evutil_socket_t fd, short what, void *arg) +{ + evutil_gettimeofday(&timer_end, NULL); +} + +static void +start_threads_callback(evutil_socket_t fd, short what, void *arg) +{ + int i; + + for (i = 0; i < QUEUE_THREAD_COUNT; ++i) { + THREAD_START(load_threads[i], load_deferred_queue, + &deferred_data[i]); + } +} + +static void +thread_deferred_cb_skew(void *arg) +{ + struct timeval tv_timer = {1, 0}; + struct event_base *base = NULL; + struct event_config *cfg = NULL; + struct timeval elapsed; + int elapsed_usec; + int i; + + cfg = event_config_new(); + tt_assert(cfg); + event_config_set_max_dispatch_interval(cfg, NULL, 16, 0); + + base = event_base_new_with_config(cfg); + tt_assert(base); + + for (i = 0; i < QUEUE_THREAD_COUNT; ++i) + deferred_data[i].queue = base; + + evutil_gettimeofday(&timer_start, NULL); + event_base_once(base, -1, EV_TIMEOUT, timer_callback, NULL, + &tv_timer); + event_base_once(base, -1, EV_TIMEOUT, start_threads_callback, + NULL, NULL); + event_base_dispatch(base); + + evutil_timersub(&timer_end, &timer_start, &elapsed); + TT_BLATHER(("callback count, %u", callback_count)); + elapsed_usec = + (unsigned)(elapsed.tv_sec*1000000 + elapsed.tv_usec); + TT_BLATHER(("elapsed time, %u usec", elapsed_usec)); + + /* XXX be more intelligent here. just make sure skew is + * within .4 seconds for now. */ + tt_assert(elapsed_usec >= 600000 && elapsed_usec <= 1400000); + +end: + for (i = 0; i < QUEUE_THREAD_COUNT; ++i) + THREAD_JOIN(load_threads[i]); + if (base) + event_base_free(base); + if (cfg) + event_config_free(cfg); +} + +static struct event time_events[5]; +static struct timeval times[5]; +static struct event_base *exit_base = NULL; +static void +note_time_cb(evutil_socket_t fd, short what, void *arg) +{ + evutil_gettimeofday(arg, NULL); + if (arg == ×[4]) { + event_base_loopbreak(exit_base); + } +} +static THREAD_FN +register_events_subthread(void *arg) +{ + struct timeval tv = {0,0}; + SLEEP_MS(100); + event_active(&time_events[0], EV_TIMEOUT, 1); + SLEEP_MS(100); + event_active(&time_events[1], EV_TIMEOUT, 1); + SLEEP_MS(100); + tv.tv_usec = 100*1000; + event_add(&time_events[2], &tv); + tv.tv_usec = 150*1000; + event_add(&time_events[3], &tv); + SLEEP_MS(200); + event_active(&time_events[4], EV_TIMEOUT, 1); + + THREAD_RETURN(); +} + +static void +thread_no_events(void *arg) +{ + THREAD_T thread; + struct basic_test_data *data = arg; + struct timeval starttime, endtime; + int i; + exit_base = data->base; + + memset(times,0,sizeof(times)); + for (i=0;i<5;++i) { + event_assign(&time_events[i], data->base, + -1, 0, note_time_cb, ×[i]); + } + + evutil_gettimeofday(&starttime, NULL); + THREAD_START(thread, register_events_subthread, data->base); + event_base_loop(data->base, EVLOOP_NO_EXIT_ON_EMPTY); + evutil_gettimeofday(&endtime, NULL); + tt_assert(event_base_got_break(data->base)); + THREAD_JOIN(thread); + for (i=0; i<5; ++i) { + struct timeval diff; + double sec; + evutil_timersub(×[i], &starttime, &diff); + sec = diff.tv_sec + diff.tv_usec/1.0e6; + TT_BLATHER(("event %d at %.4f seconds", i, sec)); + } + test_timeval_diff_eq(&starttime, ×[0], 100); + test_timeval_diff_eq(&starttime, ×[1], 200); + test_timeval_diff_eq(&starttime, ×[2], 400); + test_timeval_diff_eq(&starttime, ×[3], 450); + test_timeval_diff_eq(&starttime, ×[4], 500); + test_timeval_diff_eq(&starttime, &endtime, 500); + +end: + ; +} + +#define TEST(name) \ + { #name, thread_##name, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE, \ + &basic_setup, NULL } + +struct testcase_t thread_testcases[] = { + { "basic", thread_basic, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE, + &basic_setup, NULL }, +#ifndef _WIN32 + { "forking", thread_basic, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE, + &basic_setup, (char*)"forking" }, +#endif + TEST(conditions_simple), + { "deferred_cb_skew", thread_deferred_cb_skew, + TT_FORK|TT_NEED_THREADS|TT_OFF_BY_DEFAULT, + &basic_setup, NULL }, +#ifndef _WIN32 + /****** XXX TODO FIXME windows seems to be having some timing trouble, + * looking into it now. / ellzey + ******/ + TEST(no_events), +#endif + END_OF_TESTCASES +}; + diff --git a/libs/libevent/docs/test/regress_thread.h b/libs/libevent/docs/test/regress_thread.h new file mode 100644 index 0000000000..831b51e507 --- /dev/null +++ b/libs/libevent/docs/test/regress_thread.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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 REGRESS_THREAD_H_INCLUDED_ +#define REGRESS_THREAD_H_INCLUDED_ + +#ifdef EVENT__HAVE_PTHREADS +#define THREAD_T pthread_t +#define THREAD_FN void * +#define THREAD_RETURN() return (NULL) +#define THREAD_START(threadvar, fn, arg) \ + pthread_create(&(threadvar), NULL, fn, arg) +#define THREAD_JOIN(th) pthread_join(th, NULL) +#else +#define THREAD_T HANDLE +#define THREAD_FN unsigned __stdcall +#define THREAD_RETURN() return (0) +#define THREAD_START(threadvar, fn, arg) do { \ + uintptr_t threadhandle = _beginthreadex(NULL,0,fn,(arg),0,NULL); \ + (threadvar) = (HANDLE) threadhandle; \ + } while (0) +#define THREAD_JOIN(th) WaitForSingleObject(th, INFINITE) +#endif + +#endif diff --git a/libs/libevent/docs/test/regress_util.c b/libs/libevent/docs/test/regress_util.c new file mode 100644 index 0000000000..60f085bf1d --- /dev/null +++ b/libs/libevent/docs/test/regress_util.c @@ -0,0 +1,1413 @@ +/* + * Copyright (c) 2009-2012 Nick Mathewson and Niels Provos + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "../util-internal.h" + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#include <ws2tcpip.h> +#endif + +#include "event2/event-config.h" + +#include <sys/types.h> + +#ifndef _WIN32 +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#endif +#ifdef EVENT__HAVE_NETINET_IN6_H +#include <netinet/in6.h> +#endif +#ifdef EVENT__HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "event2/event.h" +#include "event2/util.h" +#include "../ipv6-internal.h" +#include "../log-internal.h" +#include "../strlcpy-internal.h" +#include "../mm-internal.h" +#include "../time-internal.h" + +#include "regress.h" + +enum entry_status { NORMAL, CANONICAL, BAD }; + +/* This is a big table of results we expect from generating and parsing */ +static struct ipv4_entry { + const char *addr; + ev_uint32_t res; + enum entry_status status; +} ipv4_entries[] = { + { "1.2.3.4", 0x01020304u, CANONICAL }, + { "255.255.255.255", 0xffffffffu, CANONICAL }, + { "256.0.0.0", 0, BAD }, + { "ABC", 0, BAD }, + { "1.2.3.4.5", 0, BAD }, + { "176.192.208.244", 0xb0c0d0f4, CANONICAL }, + { NULL, 0, BAD }, +}; + +static struct ipv6_entry { + const char *addr; + ev_uint32_t res[4]; + enum entry_status status; +} ipv6_entries[] = { + { "::", { 0, 0, 0, 0, }, CANONICAL }, + { "0:0:0:0:0:0:0:0", { 0, 0, 0, 0, }, NORMAL }, + { "::1", { 0, 0, 0, 1, }, CANONICAL }, + { "::1.2.3.4", { 0, 0, 0, 0x01020304, }, CANONICAL }, + { "ffff:1::", { 0xffff0001u, 0, 0, 0, }, CANONICAL }, + { "ffff:0000::", { 0xffff0000u, 0, 0, 0, }, NORMAL }, + { "ffff::1234", { 0xffff0000u, 0, 0, 0x1234, }, CANONICAL }, + { "0102::1.2.3.4", {0x01020000u, 0, 0, 0x01020304u }, NORMAL }, + { "::9:c0a8:1:1", { 0, 0, 0x0009c0a8u, 0x00010001u }, CANONICAL }, + { "::ffff:1.2.3.4", { 0, 0, 0x000ffffu, 0x01020304u }, CANONICAL }, + { "FFFF::", { 0xffff0000u, 0, 0, 0 }, NORMAL }, + { "foobar.", { 0, 0, 0, 0 }, BAD }, + { "foobar", { 0, 0, 0, 0 }, BAD }, + { "fo:obar", { 0, 0, 0, 0 }, BAD }, + { "ffff", { 0, 0, 0, 0 }, BAD }, + { "fffff::", { 0, 0, 0, 0 }, BAD }, + { "fffff::", { 0, 0, 0, 0 }, BAD }, + { "::1.0.1.1000", { 0, 0, 0, 0 }, BAD }, + { "1:2:33333:4::", { 0, 0, 0, 0 }, BAD }, + { "1:2:3:4:5:6:7:8:9", { 0, 0, 0, 0 }, BAD }, + { "1::2::3", { 0, 0, 0, 0 }, BAD }, + { ":::1", { 0, 0, 0, 0 }, BAD }, + { NULL, { 0, 0, 0, 0, }, BAD }, +}; + +static void +regress_ipv4_parse(void *ptr) +{ + int i; + for (i = 0; ipv4_entries[i].addr; ++i) { + char written[128]; + struct ipv4_entry *ent = &ipv4_entries[i]; + struct in_addr in; + int r; + r = evutil_inet_pton(AF_INET, ent->addr, &in); + if (r == 0) { + if (ent->status != BAD) { + TT_FAIL(("%s did not parse, but it's a good address!", + ent->addr)); + } + continue; + } + if (ent->status == BAD) { + TT_FAIL(("%s parsed, but we expected an error", ent->addr)); + continue; + } + if (ntohl(in.s_addr) != ent->res) { + TT_FAIL(("%s parsed to %lx, but we expected %lx", ent->addr, + (unsigned long)ntohl(in.s_addr), + (unsigned long)ent->res)); + continue; + } + if (ent->status == CANONICAL) { + const char *w = evutil_inet_ntop(AF_INET, &in, written, + sizeof(written)); + if (!w) { + TT_FAIL(("Tried to write out %s; got NULL.", ent->addr)); + continue; + } + if (strcmp(written, ent->addr)) { + TT_FAIL(("Tried to write out %s; got %s", + ent->addr, written)); + continue; + } + } + + } + +} + +static void +regress_ipv6_parse(void *ptr) +{ +#ifdef AF_INET6 + int i, j; + + for (i = 0; ipv6_entries[i].addr; ++i) { + char written[128]; + struct ipv6_entry *ent = &ipv6_entries[i]; + struct in6_addr in6; + int r; + r = evutil_inet_pton(AF_INET6, ent->addr, &in6); + if (r == 0) { + if (ent->status != BAD) + TT_FAIL(("%s did not parse, but it's a good address!", + ent->addr)); + continue; + } + if (ent->status == BAD) { + TT_FAIL(("%s parsed, but we expected an error", ent->addr)); + continue; + } + for (j = 0; j < 4; ++j) { + /* Can't use s6_addr32 here; some don't have it. */ + ev_uint32_t u = + ((ev_uint32_t)in6.s6_addr[j*4 ] << 24) | + ((ev_uint32_t)in6.s6_addr[j*4+1] << 16) | + ((ev_uint32_t)in6.s6_addr[j*4+2] << 8) | + ((ev_uint32_t)in6.s6_addr[j*4+3]); + if (u != ent->res[j]) { + TT_FAIL(("%s did not parse as expected.", ent->addr)); + continue; + } + } + if (ent->status == CANONICAL) { + const char *w = evutil_inet_ntop(AF_INET6, &in6, written, + sizeof(written)); + if (!w) { + TT_FAIL(("Tried to write out %s; got NULL.", ent->addr)); + continue; + } + if (strcmp(written, ent->addr)) { + TT_FAIL(("Tried to write out %s; got %s", ent->addr, written)); + continue; + } + } + + } +#else + TT_BLATHER(("Skipping IPv6 address parsing.")); +#endif +} + +static struct sa_port_ent { + const char *parse; + int safamily; + const char *addr; + int port; +} sa_port_ents[] = { + { "[ffff::1]:1000", AF_INET6, "ffff::1", 1000 }, + { "[ffff::1]", AF_INET6, "ffff::1", 0 }, + { "[ffff::1", 0, NULL, 0 }, + { "[ffff::1]:65599", 0, NULL, 0 }, + { "[ffff::1]:0", 0, NULL, 0 }, + { "[ffff::1]:-1", 0, NULL, 0 }, + { "::1", AF_INET6, "::1", 0 }, + { "1:2::1", AF_INET6, "1:2::1", 0 }, + { "192.168.0.1:50", AF_INET, "192.168.0.1", 50 }, + { "1.2.3.4", AF_INET, "1.2.3.4", 0 }, + { NULL, 0, NULL, 0 }, +}; + +static void +regress_sockaddr_port_parse(void *ptr) +{ + struct sockaddr_storage ss; + int i, r; + + for (i = 0; sa_port_ents[i].parse; ++i) { + struct sa_port_ent *ent = &sa_port_ents[i]; + int len = sizeof(ss); + memset(&ss, 0, sizeof(ss)); + r = evutil_parse_sockaddr_port(ent->parse, (struct sockaddr*)&ss, &len); + if (r < 0) { + if (ent->safamily) + TT_FAIL(("Couldn't parse %s!", ent->parse)); + continue; + } else if (! ent->safamily) { + TT_FAIL(("Shouldn't have been able to parse %s!", ent->parse)); + continue; + } + if (ent->safamily == AF_INET) { + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); +#ifdef EVENT__HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sin.sin_len = sizeof(sin); +#endif + sin.sin_family = AF_INET; + sin.sin_port = htons(ent->port); + r = evutil_inet_pton(AF_INET, ent->addr, &sin.sin_addr); + if (1 != r) { + TT_FAIL(("Couldn't parse ipv4 target %s.", ent->addr)); + } else if (memcmp(&sin, &ss, sizeof(sin))) { + TT_FAIL(("Parse for %s was not as expected.", ent->parse)); + } else if (len != sizeof(sin)) { + TT_FAIL(("Length for %s not as expected.",ent->parse)); + } + } else { + struct sockaddr_in6 sin6; + memset(&sin6, 0, sizeof(sin6)); +#ifdef EVENT__HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN + sin6.sin6_len = sizeof(sin6); +#endif + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(ent->port); + r = evutil_inet_pton(AF_INET6, ent->addr, &sin6.sin6_addr); + if (1 != r) { + TT_FAIL(("Couldn't parse ipv6 target %s.", ent->addr)); + } else if (memcmp(&sin6, &ss, sizeof(sin6))) { + TT_FAIL(("Parse for %s was not as expected.", ent->parse)); + } else if (len != sizeof(sin6)) { + TT_FAIL(("Length for %s not as expected.",ent->parse)); + } + } + } +} + + +static void +regress_sockaddr_port_format(void *ptr) +{ + struct sockaddr_storage ss; + int len; + const char *cp; + char cbuf[128]; + int r; + + len = sizeof(ss); + r = evutil_parse_sockaddr_port("192.168.1.1:80", + (struct sockaddr*)&ss, &len); + tt_int_op(r,==,0); + cp = evutil_format_sockaddr_port_( + (struct sockaddr*)&ss, cbuf, sizeof(cbuf)); + tt_ptr_op(cp,==,cbuf); + tt_str_op(cp,==,"192.168.1.1:80"); + + len = sizeof(ss); + r = evutil_parse_sockaddr_port("[ff00::8010]:999", + (struct sockaddr*)&ss, &len); + tt_int_op(r,==,0); + cp = evutil_format_sockaddr_port_( + (struct sockaddr*)&ss, cbuf, sizeof(cbuf)); + tt_ptr_op(cp,==,cbuf); + tt_str_op(cp,==,"[ff00::8010]:999"); + + ss.ss_family=99; + cp = evutil_format_sockaddr_port_( + (struct sockaddr*)&ss, cbuf, sizeof(cbuf)); + tt_ptr_op(cp,==,cbuf); + tt_str_op(cp,==,"<addr with socktype 99>"); +end: + ; +} + +static struct sa_pred_ent { + const char *parse; + + int is_loopback; +} sa_pred_entries[] = { + { "127.0.0.1", 1 }, + { "127.0.3.2", 1 }, + { "128.1.2.3", 0 }, + { "18.0.0.1", 0 }, + { "129.168.1.1", 0 }, + + { "::1", 1 }, + { "::0", 0 }, + { "f::1", 0 }, + { "::501", 0 }, + { NULL, 0 }, + +}; + +static void +test_evutil_sockaddr_predicates(void *ptr) +{ + struct sockaddr_storage ss; + int r, i; + + for (i=0; sa_pred_entries[i].parse; ++i) { + struct sa_pred_ent *ent = &sa_pred_entries[i]; + int len = sizeof(ss); + + r = evutil_parse_sockaddr_port(ent->parse, (struct sockaddr*)&ss, &len); + + if (r<0) { + TT_FAIL(("Couldn't parse %s!", ent->parse)); + continue; + } + + /* sockaddr_is_loopback */ + if (ent->is_loopback != evutil_sockaddr_is_loopback_((struct sockaddr*)&ss)) { + TT_FAIL(("evutil_sockaddr_loopback(%s) not as expected", + ent->parse)); + } + } +} + +static void +test_evutil_strtoll(void *ptr) +{ + const char *s; + char *endptr; + + tt_want(evutil_strtoll("5000000000", NULL, 10) == + ((ev_int64_t)5000000)*1000); + tt_want(evutil_strtoll("-5000000000", NULL, 10) == + ((ev_int64_t)5000000)*-1000); + s = " 99999stuff"; + tt_want(evutil_strtoll(s, &endptr, 10) == (ev_int64_t)99999); + tt_want(endptr == s+6); + tt_want(evutil_strtoll("foo", NULL, 10) == 0); + } + +static void +test_evutil_snprintf(void *ptr) +{ + char buf[16]; + int r; + ev_uint64_t u64 = ((ev_uint64_t)1000000000)*200; + ev_int64_t i64 = -1 * (ev_int64_t) u64; + size_t size = 8000; + ev_ssize_t ssize = -9000; + + r = evutil_snprintf(buf, sizeof(buf), "%d %d", 50, 100); + tt_str_op(buf, ==, "50 100"); + tt_int_op(r, ==, 6); + + r = evutil_snprintf(buf, sizeof(buf), "longish %d", 1234567890); + tt_str_op(buf, ==, "longish 1234567"); + tt_int_op(r, ==, 18); + + r = evutil_snprintf(buf, sizeof(buf), EV_U64_FMT, EV_U64_ARG(u64)); + tt_str_op(buf, ==, "200000000000"); + tt_int_op(r, ==, 12); + + r = evutil_snprintf(buf, sizeof(buf), EV_I64_FMT, EV_I64_ARG(i64)); + tt_str_op(buf, ==, "-200000000000"); + tt_int_op(r, ==, 13); + + r = evutil_snprintf(buf, sizeof(buf), EV_SIZE_FMT" "EV_SSIZE_FMT, + EV_SIZE_ARG(size), EV_SSIZE_ARG(ssize)); + tt_str_op(buf, ==, "8000 -9000"); + tt_int_op(r, ==, 10); + + end: + ; +} + +static void +test_evutil_casecmp(void *ptr) +{ + tt_int_op(evutil_ascii_strcasecmp("ABC", "ABC"), ==, 0); + tt_int_op(evutil_ascii_strcasecmp("ABC", "abc"), ==, 0); + tt_int_op(evutil_ascii_strcasecmp("ABC", "abcd"), <, 0); + tt_int_op(evutil_ascii_strcasecmp("ABC", "abb"), >, 0); + tt_int_op(evutil_ascii_strcasecmp("ABCd", "abc"), >, 0); + + tt_int_op(evutil_ascii_strncasecmp("Libevent", "LibEvEnT", 100), ==, 0); + tt_int_op(evutil_ascii_strncasecmp("Libevent", "LibEvEnT", 4), ==, 0); + tt_int_op(evutil_ascii_strncasecmp("Libevent", "LibEXXXX", 4), ==, 0); + tt_int_op(evutil_ascii_strncasecmp("Libevent", "LibE", 4), ==, 0); + tt_int_op(evutil_ascii_strncasecmp("Libe", "LibEvEnT", 4), ==, 0); + tt_int_op(evutil_ascii_strncasecmp("Lib", "LibEvEnT", 4), <, 0); + tt_int_op(evutil_ascii_strncasecmp("abc", "def", 99), <, 0); + tt_int_op(evutil_ascii_strncasecmp("Z", "qrst", 1), >, 0); +end: + ; +} + +static void +test_evutil_rtrim(void *ptr) +{ +#define TEST_TRIM(s, result) \ + do { \ + if (cp) mm_free(cp); \ + cp = mm_strdup(s); \ + tt_assert(cp); \ + evutil_rtrim_lws_(cp); \ + tt_str_op(cp, ==, result); \ + } while(0) + + char *cp = NULL; + (void) ptr; + + TEST_TRIM("", ""); + TEST_TRIM("a", "a"); + TEST_TRIM("abcdef ghi", "abcdef ghi"); + + TEST_TRIM(" ", ""); + TEST_TRIM(" ", ""); + TEST_TRIM("a ", "a"); + TEST_TRIM("abcdef gH ", "abcdef gH"); + + TEST_TRIM("\t\t", ""); + TEST_TRIM(" \t", ""); + TEST_TRIM("\t", ""); + TEST_TRIM("a \t", "a"); + TEST_TRIM("a\t ", "a"); + TEST_TRIM("a\t", "a"); + TEST_TRIM("abcdef gH \t ", "abcdef gH"); + +end: + if (cp) + mm_free(cp); +} + +static int logsev = 0; +static char *logmsg = NULL; + +static void +logfn(int severity, const char *msg) +{ + logsev = severity; + tt_want(msg); + if (msg) { + if (logmsg) + free(logmsg); + logmsg = strdup(msg); + } +} + +static int fatal_want_severity = 0; +static const char *fatal_want_message = NULL; +static void +fatalfn(int exitcode) +{ + if (logsev != fatal_want_severity || + !logmsg || + strcmp(logmsg, fatal_want_message)) + exit(0); + else + exit(exitcode); +} + +#ifndef _WIN32 +#define CAN_CHECK_ERR +static void +check_error_logging(void (*fn)(void), int wantexitcode, + int wantseverity, const char *wantmsg) +{ + pid_t pid; + int status = 0, exitcode; + fatal_want_severity = wantseverity; + fatal_want_message = wantmsg; + if ((pid = regress_fork()) == 0) { + /* child process */ + fn(); + exit(0); /* should be unreachable. */ + } else { + wait(&status); + exitcode = WEXITSTATUS(status); + tt_int_op(wantexitcode, ==, exitcode); + } +end: + ; +} + +static void +errx_fn(void) +{ + event_errx(2, "Fatal error; too many kumquats (%d)", 5); +} + +static void +err_fn(void) +{ + errno = ENOENT; + event_err(5,"Couldn't open %s", "/very/bad/file"); +} + +static void +sock_err_fn(void) +{ + evutil_socket_t fd = socket(AF_INET, SOCK_STREAM, 0); +#ifdef _WIN32 + EVUTIL_SET_SOCKET_ERROR(WSAEWOULDBLOCK); +#else + errno = EAGAIN; +#endif + event_sock_err(20, fd, "Unhappy socket"); +} +#endif + +static void +test_evutil_log(void *ptr) +{ + evutil_socket_t fd = -1; + char buf[128]; + + event_set_log_callback(logfn); + event_set_fatal_callback(fatalfn); +#define RESET() do { \ + logsev = 0; \ + if (logmsg) free(logmsg); \ + logmsg = NULL; \ + } while (0) +#define LOGEQ(sev,msg) do { \ + tt_int_op(logsev,==,sev); \ + tt_assert(logmsg != NULL); \ + tt_str_op(logmsg,==,msg); \ + } while (0) + +#ifdef CAN_CHECK_ERR + /* We need to disable these tests for now. Previously, the logging + * module didn't enforce the requirement that a fatal callback + * actually exit. Now, it exits no matter what, so if we wan to + * reinstate these tests, we'll need to fork for each one. */ + check_error_logging(errx_fn, 2, EVENT_LOG_ERR, + "Fatal error; too many kumquats (5)"); + RESET(); +#endif + + event_warnx("Far too many %s (%d)", "wombats", 99); + LOGEQ(EVENT_LOG_WARN, "Far too many wombats (99)"); + RESET(); + + event_msgx("Connecting lime to coconut"); + LOGEQ(EVENT_LOG_MSG, "Connecting lime to coconut"); + RESET(); + + event_debug(("A millisecond passed! We should log that!")); +#ifdef USE_DEBUG + LOGEQ(EVENT_LOG_DEBUG, "A millisecond passed! We should log that!"); +#else + tt_int_op(logsev,==,0); + tt_ptr_op(logmsg,==,NULL); +#endif + RESET(); + + /* Try with an errno. */ + errno = ENOENT; + event_warn("Couldn't open %s", "/bad/file"); + evutil_snprintf(buf, sizeof(buf), + "Couldn't open /bad/file: %s",strerror(ENOENT)); + LOGEQ(EVENT_LOG_WARN,buf); + RESET(); + +#ifdef CAN_CHECK_ERR + evutil_snprintf(buf, sizeof(buf), + "Couldn't open /very/bad/file: %s",strerror(ENOENT)); + check_error_logging(err_fn, 5, EVENT_LOG_ERR, buf); + RESET(); +#endif + + /* Try with a socket errno. */ + fd = socket(AF_INET, SOCK_STREAM, 0); +#ifdef _WIN32 + evutil_snprintf(buf, sizeof(buf), + "Unhappy socket: %s", + evutil_socket_error_to_string(WSAEWOULDBLOCK)); + EVUTIL_SET_SOCKET_ERROR(WSAEWOULDBLOCK); +#else + evutil_snprintf(buf, sizeof(buf), + "Unhappy socket: %s", strerror(EAGAIN)); + errno = EAGAIN; +#endif + event_sock_warn(fd, "Unhappy socket"); + LOGEQ(EVENT_LOG_WARN, buf); + RESET(); + +#ifdef CAN_CHECK_ERR + check_error_logging(sock_err_fn, 20, EVENT_LOG_ERR, buf); + RESET(); +#endif + +#undef RESET +#undef LOGEQ +end: + if (logmsg) + free(logmsg); + if (fd >= 0) + evutil_closesocket(fd); +} + +static void +test_evutil_strlcpy(void *arg) +{ + char buf[8]; + + /* Successful case. */ + tt_int_op(5, ==, strlcpy(buf, "Hello", sizeof(buf))); + tt_str_op(buf, ==, "Hello"); + + /* Overflow by a lot. */ + tt_int_op(13, ==, strlcpy(buf, "pentasyllabic", sizeof(buf))); + tt_str_op(buf, ==, "pentasy"); + + /* Overflow by exactly one. */ + tt_int_op(8, ==, strlcpy(buf, "overlong", sizeof(buf))); + tt_str_op(buf, ==, "overlon"); +end: + ; +} + +struct example_struct { + const char *a; + const char *b; + long c; +}; + +static void +test_evutil_upcast(void *arg) +{ + struct example_struct es1; + const char **cp; + es1.a = "World"; + es1.b = "Hello"; + es1.c = -99; + + tt_int_op(evutil_offsetof(struct example_struct, b), ==, sizeof(char*)); + + cp = &es1.b; + tt_ptr_op(EVUTIL_UPCAST(cp, struct example_struct, b), ==, &es1); + +end: + ; +} + +static void +test_evutil_integers(void *arg) +{ + ev_int64_t i64; + ev_uint64_t u64; + ev_int32_t i32; + ev_uint32_t u32; + ev_int16_t i16; + ev_uint16_t u16; + ev_int8_t i8; + ev_uint8_t u8; + + void *ptr; + ev_intptr_t iptr; + ev_uintptr_t uptr; + + ev_ssize_t ssize; + + tt_int_op(sizeof(u64), ==, 8); + tt_int_op(sizeof(i64), ==, 8); + tt_int_op(sizeof(u32), ==, 4); + tt_int_op(sizeof(i32), ==, 4); + tt_int_op(sizeof(u16), ==, 2); + tt_int_op(sizeof(i16), ==, 2); + tt_int_op(sizeof(u8), ==, 1); + tt_int_op(sizeof(i8), ==, 1); + + tt_int_op(sizeof(ev_ssize_t), ==, sizeof(size_t)); + tt_int_op(sizeof(ev_intptr_t), >=, sizeof(void *)); + tt_int_op(sizeof(ev_uintptr_t), ==, sizeof(intptr_t)); + + u64 = 1000000000; + u64 *= 1000000000; + tt_assert(u64 / 1000000000 == 1000000000); + i64 = -1000000000; + i64 *= 1000000000; + tt_assert(i64 / 1000000000 == -1000000000); + + u64 = EV_UINT64_MAX; + i64 = EV_INT64_MAX; + tt_assert(u64 > 0); + tt_assert(i64 > 0); + u64++; +/* i64++; */ + tt_assert(u64 == 0); +/* tt_assert(i64 == EV_INT64_MIN); */ +/* tt_assert(i64 < 0); */ + + u32 = EV_UINT32_MAX; + i32 = EV_INT32_MAX; + tt_assert(u32 > 0); + tt_assert(i32 > 0); + u32++; +/* i32++; */ + tt_assert(u32 == 0); +/* tt_assert(i32 == EV_INT32_MIN); */ +/* tt_assert(i32 < 0); */ + + u16 = EV_UINT16_MAX; + i16 = EV_INT16_MAX; + tt_assert(u16 > 0); + tt_assert(i16 > 0); + u16++; +/* i16++; */ + tt_assert(u16 == 0); +/* tt_assert(i16 == EV_INT16_MIN); */ +/* tt_assert(i16 < 0); */ + + u8 = EV_UINT8_MAX; + i8 = EV_INT8_MAX; + tt_assert(u8 > 0); + tt_assert(i8 > 0); + u8++; +/* i8++;*/ + tt_assert(u8 == 0); +/* tt_assert(i8 == EV_INT8_MIN); */ +/* tt_assert(i8 < 0); */ + +/* + ssize = EV_SSIZE_MAX; + tt_assert(ssize > 0); + ssize++; + tt_assert(ssize < 0); + tt_assert(ssize == EV_SSIZE_MIN); +*/ + + ptr = &ssize; + iptr = (ev_intptr_t)ptr; + uptr = (ev_uintptr_t)ptr; + ptr = (void *)iptr; + tt_assert(ptr == &ssize); + ptr = (void *)uptr; + tt_assert(ptr == &ssize); + + iptr = -1; + tt_assert(iptr < 0); +end: + ; +} + +struct evutil_addrinfo * +ai_find_by_family(struct evutil_addrinfo *ai, int family) +{ + while (ai) { + if (ai->ai_family == family) + return ai; + ai = ai->ai_next; + } + return NULL; +} + +struct evutil_addrinfo * +ai_find_by_protocol(struct evutil_addrinfo *ai, int protocol) +{ + while (ai) { + if (ai->ai_protocol == protocol) + return ai; + ai = ai->ai_next; + } + return NULL; +} + + +int +test_ai_eq_(const struct evutil_addrinfo *ai, const char *sockaddr_port, + int socktype, int protocol, int line) +{ + struct sockaddr_storage ss; + int slen = sizeof(ss); + int gotport; + char buf[128]; + memset(&ss, 0, sizeof(ss)); + if (socktype > 0) + tt_int_op(ai->ai_socktype, ==, socktype); + if (protocol > 0) + tt_int_op(ai->ai_protocol, ==, protocol); + + if (evutil_parse_sockaddr_port( + sockaddr_port, (struct sockaddr*)&ss, &slen)<0) { + TT_FAIL(("Couldn't parse expected address %s on line %d", + sockaddr_port, line)); + return -1; + } + if (ai->ai_family != ss.ss_family) { + TT_FAIL(("Address family %d did not match %d on line %d", + ai->ai_family, ss.ss_family, line)); + return -1; + } + if (ai->ai_addr->sa_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in*)ai->ai_addr; + evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf)); + gotport = ntohs(sin->sin_port); + if (ai->ai_addrlen != sizeof(struct sockaddr_in)) { + TT_FAIL(("Addr size mismatch on line %d", line)); + return -1; + } + } else { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)ai->ai_addr; + evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof(buf)); + gotport = ntohs(sin6->sin6_port); + if (ai->ai_addrlen != sizeof(struct sockaddr_in6)) { + TT_FAIL(("Addr size mismatch on line %d", line)); + return -1; + } + } + if (evutil_sockaddr_cmp(ai->ai_addr, (struct sockaddr*)&ss, 1)) { + TT_FAIL(("Wanted %s, got %s:%d on line %d", sockaddr_port, + buf, gotport, line)); + return -1; + } else { + TT_BLATHER(("Wanted %s, got %s:%d on line %d", sockaddr_port, + buf, gotport, line)); + } + return 0; +end: + TT_FAIL(("Test failed on line %d", line)); + return -1; +} + +static void +test_evutil_rand(void *arg) +{ + char buf1[32]; + char buf2[32]; + int counts[256]; + int i, j, k, n=0; + struct evutil_weakrand_state seed = { 12346789U }; + + memset(buf2, 0, sizeof(buf2)); + memset(counts, 0, sizeof(counts)); + + for (k=0;k<32;++k) { + /* Try a few different start and end points; try to catch + * the various misaligned cases of arc4random_buf */ + int startpoint = evutil_weakrand_(&seed) % 4; + int endpoint = 32 - (evutil_weakrand_(&seed) % 4); + + memset(buf2, 0, sizeof(buf2)); + + /* Do 6 runs over buf1, or-ing the result into buf2 each + * time, to make sure we're setting each byte that we mean + * to set. */ + for (i=0;i<8;++i) { + memset(buf1, 0, sizeof(buf1)); + evutil_secure_rng_get_bytes(buf1 + startpoint, + endpoint-startpoint); + n += endpoint - startpoint; + for (j=0; j<32; ++j) { + if (j >= startpoint && j < endpoint) { + buf2[j] |= buf1[j]; + ++counts[(unsigned char)buf1[j]]; + } else { + tt_assert(buf1[j] == 0); + tt_int_op(buf1[j], ==, 0); + + } + } + } + + /* This will give a false positive with P=(256**8)==(2**64) + * for each character. */ + for (j=startpoint;j<endpoint;++j) { + tt_int_op(buf2[j], !=, 0); + } + } + + evutil_weakrand_seed_(&seed, 0); + for (i = 0; i < 10000; ++i) { + ev_int32_t r = evutil_weakrand_range_(&seed, 9999); + tt_int_op(0, <=, r); + tt_int_op(r, <, 9999); + } + + /* for (i=0;i<256;++i) { printf("%3d %2d\n", i, counts[i]); } */ +end: + ; +} + +static void +test_evutil_getaddrinfo(void *arg) +{ + struct evutil_addrinfo *ai = NULL, *a; + struct evutil_addrinfo hints; + int r; + + /* Try using it as a pton. */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + r = evutil_getaddrinfo("1.2.3.4", "8080", &hints, &ai); + tt_int_op(r, ==, 0); + tt_assert(ai); + tt_ptr_op(ai->ai_next, ==, NULL); /* no ambiguity */ + test_ai_eq(ai, "1.2.3.4:8080", SOCK_STREAM, IPPROTO_TCP); + evutil_freeaddrinfo(ai); + ai = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_protocol = IPPROTO_UDP; + r = evutil_getaddrinfo("1001:b0b::f00f", "4321", &hints, &ai); + tt_int_op(r, ==, 0); + tt_assert(ai); + tt_ptr_op(ai->ai_next, ==, NULL); /* no ambiguity */ + test_ai_eq(ai, "[1001:b0b::f00f]:4321", SOCK_DGRAM, IPPROTO_UDP); + evutil_freeaddrinfo(ai); + ai = NULL; + + /* Try out the behavior of nodename=NULL */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = EVUTIL_AI_PASSIVE; /* as if for bind */ + r = evutil_getaddrinfo(NULL, "9999", &hints, &ai); + tt_int_op(r,==,0); + tt_assert(ai); + tt_ptr_op(ai->ai_next, ==, NULL); + test_ai_eq(ai, "0.0.0.0:9999", SOCK_STREAM, IPPROTO_TCP); + evutil_freeaddrinfo(ai); + ai = NULL; + hints.ai_flags = 0; /* as if for connect */ + r = evutil_getaddrinfo(NULL, "9998", &hints, &ai); + tt_assert(ai); + tt_int_op(r,==,0); + test_ai_eq(ai, "127.0.0.1:9998", SOCK_STREAM, IPPROTO_TCP); + tt_ptr_op(ai->ai_next, ==, NULL); + evutil_freeaddrinfo(ai); + ai = NULL; + + hints.ai_flags = 0; /* as if for connect */ + hints.ai_family = PF_INET6; + r = evutil_getaddrinfo(NULL, "9997", &hints, &ai); + tt_assert(ai); + tt_int_op(r,==,0); + tt_ptr_op(ai->ai_next, ==, NULL); + test_ai_eq(ai, "[::1]:9997", SOCK_STREAM, IPPROTO_TCP); + evutil_freeaddrinfo(ai); + ai = NULL; + + hints.ai_flags = EVUTIL_AI_PASSIVE; /* as if for bind. */ + hints.ai_family = PF_INET6; + r = evutil_getaddrinfo(NULL, "9996", &hints, &ai); + tt_assert(ai); + tt_int_op(r,==,0); + tt_ptr_op(ai->ai_next, ==, NULL); + test_ai_eq(ai, "[::]:9996", SOCK_STREAM, IPPROTO_TCP); + evutil_freeaddrinfo(ai); + ai = NULL; + + /* Now try an unspec one. We should get a v6 and a v4. */ + hints.ai_family = PF_UNSPEC; + r = evutil_getaddrinfo(NULL, "9996", &hints, &ai); + tt_assert(ai); + tt_int_op(r,==,0); + a = ai_find_by_family(ai, PF_INET6); + tt_assert(a); + test_ai_eq(a, "[::]:9996", SOCK_STREAM, IPPROTO_TCP); + a = ai_find_by_family(ai, PF_INET); + tt_assert(a); + test_ai_eq(a, "0.0.0.0:9996", SOCK_STREAM, IPPROTO_TCP); + evutil_freeaddrinfo(ai); + ai = NULL; + + /* Try out AI_NUMERICHOST: successful case. Also try + * multiprotocol. */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_flags = EVUTIL_AI_NUMERICHOST; + r = evutil_getaddrinfo("1.2.3.4", NULL, &hints, &ai); + tt_int_op(r, ==, 0); + a = ai_find_by_protocol(ai, IPPROTO_TCP); + tt_assert(a); + test_ai_eq(a, "1.2.3.4", SOCK_STREAM, IPPROTO_TCP); + a = ai_find_by_protocol(ai, IPPROTO_UDP); + tt_assert(a); + test_ai_eq(a, "1.2.3.4", SOCK_DGRAM, IPPROTO_UDP); + evutil_freeaddrinfo(ai); + ai = NULL; + + /* Try the failing case of AI_NUMERICHOST */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_flags = EVUTIL_AI_NUMERICHOST; + r = evutil_getaddrinfo("www.google.com", "80", &hints, &ai); + tt_int_op(r, ==, EVUTIL_EAI_NONAME); + tt_ptr_op(ai, ==, NULL); + + /* Try symbolic service names wit AI_NUMERICSERV */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = EVUTIL_AI_NUMERICSERV; + r = evutil_getaddrinfo("1.2.3.4", "http", &hints, &ai); + tt_int_op(r,==,EVUTIL_EAI_NONAME); + + /* Try symbolic service names */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + r = evutil_getaddrinfo("1.2.3.4", "http", &hints, &ai); + if (r!=0) { + TT_DECLARE("SKIP", ("Symbolic service names seem broken.")); + } else { + tt_assert(ai); + test_ai_eq(ai, "1.2.3.4:80", SOCK_STREAM, IPPROTO_TCP); + evutil_freeaddrinfo(ai); + ai = NULL; + } + +end: + if (ai) + evutil_freeaddrinfo(ai); +} + +static void +test_evutil_getaddrinfo_live(void *arg) +{ + struct evutil_addrinfo *ai = NULL; + struct evutil_addrinfo hints; + + struct sockaddr_in6 *sin6; + struct sockaddr_in *sin; + char buf[128]; + const char *cp; + int r; + + /* Now do some actual lookups. */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_INET; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_socktype = SOCK_STREAM; + r = evutil_getaddrinfo("www.google.com", "80", &hints, &ai); + if (r != 0) { + TT_DECLARE("SKIP", ("Couldn't resolve www.google.com")); + } else { + tt_assert(ai); + tt_int_op(ai->ai_family, ==, PF_INET); + tt_int_op(ai->ai_protocol, ==, IPPROTO_TCP); + tt_int_op(ai->ai_socktype, ==, SOCK_STREAM); + tt_int_op(ai->ai_addrlen, ==, sizeof(struct sockaddr_in)); + sin = (struct sockaddr_in*)ai->ai_addr; + tt_int_op(sin->sin_family, ==, AF_INET); + tt_int_op(sin->sin_port, ==, htons(80)); + tt_int_op(sin->sin_addr.s_addr, !=, 0xffffffff); + + cp = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf)); + TT_BLATHER(("www.google.com resolved to %s", + cp?cp:"<unwriteable>")); + evutil_freeaddrinfo(ai); + ai = NULL; + } + + hints.ai_family = PF_INET6; + r = evutil_getaddrinfo("ipv6.google.com", "80", &hints, &ai); + if (r != 0) { + TT_BLATHER(("Couldn't do an ipv6 lookup for ipv6.google.com")); + } else { + tt_assert(ai); + tt_int_op(ai->ai_family, ==, PF_INET6); + tt_int_op(ai->ai_addrlen, ==, sizeof(struct sockaddr_in6)); + sin6 = (struct sockaddr_in6*)ai->ai_addr; + tt_int_op(sin6->sin6_port, ==, htons(80)); + + cp = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, + sizeof(buf)); + TT_BLATHER(("ipv6.google.com resolved to %s", + cp?cp:"<unwriteable>")); + } + +end: + if (ai) + evutil_freeaddrinfo(ai); +} + +#ifdef _WIN32 +static void +test_evutil_loadsyslib(void *arg) +{ + HMODULE h=NULL; + + h = evutil_load_windows_system_library_(TEXT("kernel32.dll")); + tt_assert(h); + +end: + if (h) + CloseHandle(h); + +} +#endif + +/** Test mm_malloc(). */ +static void +test_event_malloc(void *arg) +{ + void *p = NULL; + (void)arg; + + /* mm_malloc(0) should simply return NULL. */ +#ifndef EVENT__DISABLE_MM_REPLACEMENT + errno = 0; + p = mm_malloc(0); + tt_assert(p == NULL); + tt_int_op(errno, ==, 0); +#endif + + /* Trivial case. */ + errno = 0; + p = mm_malloc(8); + tt_assert(p != NULL); + tt_int_op(errno, ==, 0); + mm_free(p); + + end: + errno = 0; + return; +} + +static void +test_event_calloc(void *arg) +{ + void *p = NULL; + (void)arg; + +#ifndef EVENT__DISABLE_MM_REPLACEMENT + /* mm_calloc() should simply return NULL + * if either argument is zero. */ + errno = 0; + p = mm_calloc(0, 0); + tt_assert(p == NULL); + tt_int_op(errno, ==, 0); + errno = 0; + p = mm_calloc(0, 1); + tt_assert(p == NULL); + tt_int_op(errno, ==, 0); + errno = 0; + p = mm_calloc(1, 0); + tt_assert(p == NULL); + tt_int_op(errno, ==, 0); +#endif + + /* Trivial case. */ + errno = 0; + p = mm_calloc(8, 8); + tt_assert(p != NULL); + tt_int_op(errno, ==, 0); + mm_free(p); + p = NULL; + + /* mm_calloc() should set errno = ENOMEM and return NULL + * in case of potential overflow. */ + errno = 0; + p = mm_calloc(EV_SIZE_MAX/2, EV_SIZE_MAX/2 + 8); + tt_assert(p == NULL); + tt_int_op(errno, ==, ENOMEM); + + end: + errno = 0; + if (p) + mm_free(p); + + return; +} + +static void +test_event_strdup(void *arg) +{ + void *p = NULL; + (void)arg; + +#ifndef EVENT__DISABLE_MM_REPLACEMENT + /* mm_strdup(NULL) should set errno = EINVAL and return NULL. */ + errno = 0; + p = mm_strdup(NULL); + tt_assert(p == NULL); + tt_int_op(errno, ==, EINVAL); +#endif + + /* Trivial cases. */ + + errno = 0; + p = mm_strdup(""); + tt_assert(p != NULL); + tt_int_op(errno, ==, 0); + tt_str_op(p, ==, ""); + mm_free(p); + + errno = 0; + p = mm_strdup("foo"); + tt_assert(p != NULL); + tt_int_op(errno, ==, 0); + tt_str_op(p, ==, "foo"); + mm_free(p); + + /* XXX + * mm_strdup(str) where str is a string of length EV_SIZE_MAX + * should set errno = ENOMEM and return NULL. */ + + end: + errno = 0; + return; +} + +static void +test_evutil_usleep(void *arg) +{ + struct timeval tv1, tv2, tv3, diff1, diff2; + const struct timeval quarter_sec = {0, 250*1000}; + const struct timeval tenth_sec = {0, 100*1000}; + long usec1, usec2; + + evutil_gettimeofday(&tv1, NULL); + evutil_usleep_(&quarter_sec); + evutil_gettimeofday(&tv2, NULL); + evutil_usleep_(&tenth_sec); + evutil_gettimeofday(&tv3, NULL); + + evutil_timersub(&tv2, &tv1, &diff1); + evutil_timersub(&tv3, &tv2, &diff2); + usec1 = diff1.tv_sec * 1000000 + diff1.tv_usec; + usec2 = diff2.tv_sec * 1000000 + diff2.tv_usec; + + tt_int_op(usec1, >, 200000); + tt_int_op(usec1, <, 300000); + tt_int_op(usec2, >, 80000); + tt_int_op(usec2, <, 120000); + +end: + ; +} + +static void +test_evutil_monotonic_res(void *data_) +{ + /* Basic santity-test for monotonic timers. What we'd really like + * to do is make sure that they can't go backwards even when the + * system clock goes backwards. But we haven't got a good way to + * move the system clock backwards. + */ + struct basic_test_data *data = data_; + struct evutil_monotonic_timer timer; + const int precise = strstr(data->setup_data, "precise") != NULL; + const int fallback = strstr(data->setup_data, "fallback") != NULL; + struct timeval tv[10], delay; + int total_diff = 0; + + int flags = 0, wantres, acceptdiff, i; + if (precise) + flags |= EV_MONOT_PRECISE; + if (fallback) + flags |= EV_MONOT_FALLBACK; + if (precise || fallback) { +#ifdef _WIN32 + wantres = 10*1000; + acceptdiff = 1000; +#else + wantres = 1000; + acceptdiff = 300; +#endif + } else { + wantres = 40*1000; + acceptdiff = 20*1000; + } + + TT_BLATHER(("Precise = %d", precise)); + TT_BLATHER(("Fallback = %d", fallback)); + + /* First, make sure we match up with usleep. */ + + delay.tv_sec = 0; + delay.tv_usec = wantres; + + tt_int_op(evutil_configure_monotonic_time_(&timer, flags), ==, 0); + + for (i = 0; i < 10; ++i) { + evutil_gettime_monotonic_(&timer, &tv[i]); + evutil_usleep_(&delay); + } + + for (i = 0; i < 9; ++i) { + struct timeval diff; + tt_assert(evutil_timercmp(&tv[i], &tv[i+1], <)); + evutil_timersub(&tv[i+1], &tv[i], &diff); + tt_int_op(diff.tv_sec, ==, 0); + total_diff += diff.tv_usec; + TT_BLATHER(("Difference = %d", (int)diff.tv_usec)); + } + tt_int_op(abs(total_diff/9 - wantres), <, acceptdiff); + +end: + ; +} + +static void +test_evutil_monotonic_prc(void *data_) +{ + struct basic_test_data *data = data_; + struct evutil_monotonic_timer timer; + const int precise = strstr(data->setup_data, "precise") != NULL; + const int fallback = strstr(data->setup_data, "fallback") != NULL; + struct timeval tv[10]; + int total_diff = 0; + int i, maxstep = 25*1000,flags=0; + if (precise) + maxstep = 500; + if (precise) + flags |= EV_MONOT_PRECISE; + if (fallback) + flags |= EV_MONOT_FALLBACK; + tt_int_op(evutil_configure_monotonic_time_(&timer, flags), ==, 0); + + /* find out what precision we actually see. */ + + evutil_gettime_monotonic_(&timer, &tv[0]); + for (i = 1; i < 10; ++i) { + do { + evutil_gettime_monotonic_(&timer, &tv[i]); + } while (evutil_timercmp(&tv[i-1], &tv[i], ==)); + } + + total_diff = 0; + for (i = 0; i < 9; ++i) { + struct timeval diff; + tt_assert(evutil_timercmp(&tv[i], &tv[i+1], <)); + evutil_timersub(&tv[i+1], &tv[i], &diff); + tt_int_op(diff.tv_sec, ==, 0); + total_diff += diff.tv_usec; + TT_BLATHER(("Step difference = %d", (int)diff.tv_usec)); + } + TT_BLATHER(("Average step difference = %d", total_diff / 9)); + tt_int_op(total_diff/9, <, maxstep); + +end: + ; +} + +struct testcase_t util_testcases[] = { + { "ipv4_parse", regress_ipv4_parse, 0, NULL, NULL }, + { "ipv6_parse", regress_ipv6_parse, 0, NULL, NULL }, + { "sockaddr_port_parse", regress_sockaddr_port_parse, 0, NULL, NULL }, + { "sockaddr_port_format", regress_sockaddr_port_format, 0, NULL, NULL }, + { "sockaddr_predicates", test_evutil_sockaddr_predicates, 0,NULL,NULL }, + { "evutil_snprintf", test_evutil_snprintf, 0, NULL, NULL }, + { "evutil_strtoll", test_evutil_strtoll, 0, NULL, NULL }, + { "evutil_casecmp", test_evutil_casecmp, 0, NULL, NULL }, + { "evutil_rtrim", test_evutil_rtrim, 0, NULL, NULL }, + { "strlcpy", test_evutil_strlcpy, 0, NULL, NULL }, + { "log", test_evutil_log, TT_FORK, NULL, NULL }, + { "upcast", test_evutil_upcast, 0, NULL, NULL }, + { "integers", test_evutil_integers, 0, NULL, NULL }, + { "rand", test_evutil_rand, TT_FORK, NULL, NULL }, + { "getaddrinfo", test_evutil_getaddrinfo, TT_FORK, NULL, NULL }, + { "getaddrinfo_live", test_evutil_getaddrinfo_live, TT_FORK|TT_OFF_BY_DEFAULT, NULL, NULL }, +#ifdef _WIN32 + { "loadsyslib", test_evutil_loadsyslib, TT_FORK, NULL, NULL }, +#endif + { "mm_malloc", test_event_malloc, 0, NULL, NULL }, + { "mm_calloc", test_event_calloc, 0, NULL, NULL }, + { "mm_strdup", test_event_strdup, 0, NULL, NULL }, + { "usleep", test_evutil_usleep, 0, NULL, NULL }, + { "monotonic_res", test_evutil_monotonic_res, 0, &basic_setup, (void*)"" }, + { "monotonic_res_precise", test_evutil_monotonic_res, TT_OFF_BY_DEFAULT, &basic_setup, (void*)"precise" }, + { "monotonic_res_fallback", test_evutil_monotonic_res, TT_OFF_BY_DEFAULT, &basic_setup, (void*)"fallback" }, + { "monotonic_prc", test_evutil_monotonic_prc, 0, &basic_setup, (void*)"" }, + { "monotonic_prc_precise", test_evutil_monotonic_prc, 0, &basic_setup, (void*)"precise" }, + { "monotonic_prc_fallback", test_evutil_monotonic_prc, 0, &basic_setup, (void*)"fallback" }, + END_OF_TESTCASES, +}; + diff --git a/libs/libevent/docs/test/regress_zlib.c b/libs/libevent/docs/test/regress_zlib.c new file mode 100644 index 0000000000..8406676932 --- /dev/null +++ b/libs/libevent/docs/test/regress_zlib.c @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2008-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ + +/* The old tests here need assertions to work. */ +#undef NDEBUG + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#endif + +#include "event2/event-config.h" + +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <sys/wait.h> +#include <unistd.h> +#include <netdb.h> +#endif +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <assert.h> +#include <errno.h> + +#include "event2/util.h" +#include "event2/event.h" +#include "event2/event_compat.h" +#include "event2/buffer.h" +#include "event2/bufferevent.h" + +#include "regress.h" +#include "mm-internal.h" + +/* zlib 1.2.4 and 1.2.5 do some "clever" things with macros. Instead of + saying "(defined(FOO) ? FOO : 0)" they like to say "FOO-0", on the theory + that nobody will care if the compile outputs a no-such-identifier warning. + + Sorry, but we like -Werror over here, so I guess we need to define these. + I hope that zlib 1.2.6 doesn't break these too. +*/ +#ifndef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE 0 +#endif +#ifndef _LFS64_LARGEFILE +#define _LFS64_LARGEFILE 0 +#endif +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 0 +#endif +#ifndef off64_t +#define off64_t ev_int64_t +#endif + +#include <zlib.h> + +static int infilter_calls; +static int outfilter_calls; +static int readcb_finished; +static int writecb_finished; +static int errorcb_invoked; + +/* + * Zlib filters + */ + +static void +zlib_deflate_free(void *ctx) +{ + z_streamp p = ctx; + + assert(deflateEnd(p) == Z_OK); + mm_free(p); +} + +static void +zlib_inflate_free(void *ctx) +{ + z_streamp p = ctx; + + assert(inflateEnd(p) == Z_OK); + mm_free(p); +} + +static int +getstate(enum bufferevent_flush_mode state) +{ + switch (state) { + case BEV_FINISHED: + return Z_FINISH; + case BEV_FLUSH: + return Z_SYNC_FLUSH; + case BEV_NORMAL: + default: + return Z_NO_FLUSH; + } +} + +/* + * The input filter is triggered only on new input read from the network. + * That means all input data needs to be consumed or the filter needs to + * initiate its own triggering via a timeout. + */ +static enum bufferevent_filter_result +zlib_input_filter(struct evbuffer *src, struct evbuffer *dst, + ev_ssize_t lim, enum bufferevent_flush_mode state, void *ctx) +{ + struct evbuffer_iovec v_in[1]; + struct evbuffer_iovec v_out[1]; + int nread, nwrite; + int res, n; + + z_streamp p = ctx; + + do { + /* let's do some decompression */ + n = evbuffer_peek(src, -1, NULL, v_in, 1); + if (n) { + p->avail_in = v_in[0].iov_len; + p->next_in = v_in[0].iov_base; + } else { + p->avail_in = 0; + p->next_in = 0; + } + + evbuffer_reserve_space(dst, 4096, v_out, 1); + p->next_out = v_out[0].iov_base; + p->avail_out = v_out[0].iov_len; + + /* we need to flush zlib if we got a flush */ + res = inflate(p, getstate(state)); + + /* let's figure out how much was compressed */ + nread = v_in[0].iov_len - p->avail_in; + nwrite = v_out[0].iov_len - p->avail_out; + + evbuffer_drain(src, nread); + v_out[0].iov_len = nwrite; + evbuffer_commit_space(dst, v_out, 1); + + if (res==Z_BUF_ERROR) { + /* We're out of space, or out of decodeable input. + Only if nwrite == 0 assume the latter. + */ + if (nwrite == 0) + return BEV_NEED_MORE; + } else { + assert(res == Z_OK || res == Z_STREAM_END); + } + + } while (evbuffer_get_length(src) > 0); + + ++infilter_calls; + + return (BEV_OK); +} + +static enum bufferevent_filter_result +zlib_output_filter(struct evbuffer *src, struct evbuffer *dst, + ev_ssize_t lim, enum bufferevent_flush_mode state, void *ctx) +{ + struct evbuffer_iovec v_in[1]; + struct evbuffer_iovec v_out[1]; + int nread, nwrite; + int res, n; + + z_streamp p = ctx; + + do { + /* let's do some compression */ + n = evbuffer_peek(src, -1, NULL, v_in, 1); + if (n) { + p->avail_in = v_in[0].iov_len; + p->next_in = v_in[0].iov_base; + } else { + p->avail_in = 0; + p->next_in = 0; + } + + evbuffer_reserve_space(dst, 4096, v_out, 1); + p->next_out = v_out[0].iov_base; + p->avail_out = v_out[0].iov_len; + + /* we need to flush zlib if we got a flush */ + res = deflate(p, getstate(state)); + + /* let's figure out how much was decompressed */ + nread = v_in[0].iov_len - p->avail_in; + nwrite = v_out[0].iov_len - p->avail_out; + + evbuffer_drain(src, nread); + v_out[0].iov_len = nwrite; + evbuffer_commit_space(dst, v_out, 1); + + if (res==Z_BUF_ERROR) { + /* We're out of space, or out of decodeable input. + Only if nwrite == 0 assume the latter. + */ + if (nwrite == 0) + return BEV_NEED_MORE; + } else { + assert(res == Z_OK || res == Z_STREAM_END); + } + + } while (evbuffer_get_length(src) > 0); + + ++outfilter_calls; + + return (BEV_OK); +} + +/* + * simple bufferevent test (over transparent zlib treatment) + */ + +static void +readcb(struct bufferevent *bev, void *arg) +{ + if (evbuffer_get_length(bufferevent_get_input(bev)) == 8333) { + struct evbuffer *evbuf = evbuffer_new(); + assert(evbuf != NULL); + + /* gratuitous test of bufferevent_read_buffer */ + bufferevent_read_buffer(bev, evbuf); + + bufferevent_disable(bev, EV_READ); + + if (evbuffer_get_length(evbuf) == 8333) { + ++readcb_finished; + } + + evbuffer_free(evbuf); + } +} + +static void +writecb(struct bufferevent *bev, void *arg) +{ + if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) { + ++writecb_finished; + } +} + +static void +errorcb(struct bufferevent *bev, short what, void *arg) +{ + errorcb_invoked = 1; +} + +void +test_bufferevent_zlib(void *arg) +{ + struct bufferevent *bev1=NULL, *bev2=NULL; + char buffer[8333]; + z_stream *z_input, *z_output; + int i, r; + evutil_socket_t pair[2] = {-1, -1}; + (void)arg; + + infilter_calls = outfilter_calls = readcb_finished = writecb_finished + = errorcb_invoked = 0; + + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { + tt_abort_perror("socketpair"); + } + + evutil_make_socket_nonblocking(pair[0]); + evutil_make_socket_nonblocking(pair[1]); + + bev1 = bufferevent_socket_new(NULL, pair[0], 0); + bev2 = bufferevent_socket_new(NULL, pair[1], 0); + + z_output = mm_calloc(sizeof(*z_output), 1); + r = deflateInit(z_output, Z_DEFAULT_COMPRESSION); + tt_int_op(r, ==, Z_OK); + z_input = mm_calloc(sizeof(*z_input), 1); + r = inflateInit(z_input); + tt_int_op(r, ==, Z_OK); + + /* initialize filters */ + bev1 = bufferevent_filter_new(bev1, NULL, zlib_output_filter, + BEV_OPT_CLOSE_ON_FREE, zlib_deflate_free, z_output); + bev2 = bufferevent_filter_new(bev2, zlib_input_filter, + NULL, BEV_OPT_CLOSE_ON_FREE, zlib_inflate_free, z_input); + bufferevent_setcb(bev1, readcb, writecb, errorcb, NULL); + bufferevent_setcb(bev2, readcb, writecb, errorcb, NULL); + + bufferevent_disable(bev1, EV_READ); + bufferevent_enable(bev1, EV_WRITE); + + bufferevent_enable(bev2, EV_READ); + + for (i = 0; i < (int)sizeof(buffer); i++) + buffer[i] = i; + + /* break it up into multiple buffer chains */ + bufferevent_write(bev1, buffer, 1800); + bufferevent_write(bev1, buffer + 1800, sizeof(buffer) - 1800); + + /* we are done writing - we need to flush everything */ + bufferevent_flush(bev1, EV_WRITE, BEV_FINISHED); + + event_dispatch(); + + tt_want(infilter_calls); + tt_want(outfilter_calls); + tt_want(readcb_finished); + tt_want(writecb_finished); + tt_want(!errorcb_invoked); + + test_ok = 1; +end: + if (bev1) + bufferevent_free(bev1); + if (bev2) + bufferevent_free(bev2); + + if (pair[0] >= 0) + evutil_closesocket(pair[0]); + if (pair[1] >= 0) + evutil_closesocket(pair[1]); +} diff --git a/libs/libevent/docs/test/rpcgen_wrapper.sh b/libs/libevent/docs/test/rpcgen_wrapper.sh new file mode 100644 index 0000000000..aaa03031a1 --- /dev/null +++ b/libs/libevent/docs/test/rpcgen_wrapper.sh @@ -0,0 +1,52 @@ +#!/bin/sh +# libevent rpcgen_wrapper.sh +# Transforms event_rpcgen.py failure into success for make, only if +# regress.gen.c and regress.gen.h already exist in $srcdir. This +# is needed for "make distcheck" to pass the read-only $srcdir build, +# as with read-only sources fresh from tarball, regress.gen.[ch] will +# be correct in $srcdir but unwritable. This previously triggered +# Makefile.am to create stub regress.gen.c and regress.gen.h in the +# distcheck _build directory, which were then detected as leftover +# files in the build tree after distclean, breaking distcheck. +# Note that regress.gen.[ch] are not in fresh git clones, making +# working Python a requirement for make distcheck of a git tree. + +exit_updated() { +# echo "Updated ${srcdir}/regress.gen.c and ${srcdir}/regress.gen.h" + exit 0 +} + +exit_reuse() { + echo "event_rpcgen.py failed, ${srcdir}/regress.gen.\[ch\] will be reused." >&2 + exit 0 +} + +exit_failed() { + echo "Could not generate regress.gen.\[ch\] using event_rpcgen.sh" >&2 + exit 1 +} + +if [ -x /usr/bin/python2 ] ; then + PYTHON2=/usr/bin/python2 +elif [ "x`which python2`" != x ] ; then + PYTHON2=python2 +else + PYTHON2=python +fi + +srcdir=$1 +srcdir=${srcdir:-.} + +${PYTHON2} ${srcdir}/../event_rpcgen.py --quiet ${srcdir}/regress.rpc \ + test/regress.gen.h test/regress.gen.c + +case "$?" in + 0) + exit_updated + ;; + *) + test -r ${srcdir}/regress.gen.c -a -r ${srcdir}/regress.gen.h && \ + exit_reuse + exit_failed + ;; +esac diff --git a/libs/libevent/docs/test/test-changelist.c b/libs/libevent/docs/test/test-changelist.c new file mode 100644 index 0000000000..6e2466d5a5 --- /dev/null +++ b/libs/libevent/docs/test/test-changelist.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2010-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ + +#include "event2/event-config.h" + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#else +#include <unistd.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#ifdef EVENT__HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "event2/event.h" +#include "event2/util.h" +#include <time.h> + +struct cpu_usage_timer { +#ifdef _WIN32 + HANDLE thread; + FILETIME usertimeBegin; + FILETIME kerneltimeBegin; +#else + clock_t ticksBegin; +#endif + struct timeval timeBegin; +}; +static void +start_cpu_usage_timer(struct cpu_usage_timer *timer) +{ +#ifdef _WIN32 + int r; + FILETIME createtime, exittime; + timer->thread = GetCurrentThread(); + r = GetThreadTimes(timer->thread, &createtime, &exittime, + &timer->usertimeBegin, &timer->kerneltimeBegin); + if (r==0) printf("GetThreadTimes failed."); +#else + timer->ticksBegin = clock(); +#endif + + evutil_gettimeofday(&timer->timeBegin, NULL); +} +#ifdef _WIN32 +static ev_int64_t +filetime_to_100nsec(const FILETIME *ft) +{ + /* Number of 100-nanosecond units */ + ev_int64_t n = ft->dwHighDateTime; + n <<= 32; + n += ft->dwLowDateTime; + return n; +} +static double +filetime_diff(const FILETIME *ftStart, const FILETIME *ftEnd) +{ + ev_int64_t s, e, diff; + double r; + s = filetime_to_100nsec(ftStart); + e = filetime_to_100nsec(ftEnd); + diff = e - s; + r = (double) diff; + return r / 1.0e7; +} +#endif + +static void +get_cpu_usage(struct cpu_usage_timer *timer, double *secElapsedOut, + double *secUsedOut, double *usageOut) +{ +#ifdef _WIN32 + double usertime_seconds, kerneltime_seconds; + FILETIME createtime, exittime, usertimeEnd, kerneltimeEnd; + int r; +#else + clock_t ticksEnd; +#endif + struct timeval timeEnd, timeDiff; + double secondsPassed, secondsUsed; + +#ifdef _WIN32 + r = GetThreadTimes(timer->thread, &createtime, &exittime, + &usertimeEnd, &kerneltimeEnd); + if (r==0) printf("GetThreadTimes failed."); + usertime_seconds = filetime_diff(&timer->usertimeBegin, &usertimeEnd); + kerneltime_seconds = filetime_diff(&timer->kerneltimeBegin, &kerneltimeEnd); + secondsUsed = kerneltime_seconds + usertime_seconds; +#else + ticksEnd = clock(); + secondsUsed = (ticksEnd - timer->ticksBegin) / (double)CLOCKS_PER_SEC; +#endif + evutil_gettimeofday(&timeEnd, NULL); + evutil_timersub(&timeEnd, &timer->timeBegin, &timeDiff); + secondsPassed = timeDiff.tv_sec + (timeDiff.tv_usec / 1.0e6); + + *secElapsedOut = secondsPassed; + *secUsedOut = secondsUsed; + *usageOut = secondsUsed / secondsPassed; +} + +static void +write_cb(evutil_socket_t fd, short event, void *arg) +{ + printf("write callback. should only see this once\n"); + + /* got what we want remove the event */ + event_del(*(struct event**)arg); + + /* opps changed my mind add it back again */ + event_add(*(struct event**)arg,NULL); + + /* not a good day for decisiveness, I really didn't want it after all */ + event_del(*(struct event**)arg); + +} + +static void +timeout_cb(evutil_socket_t fd, short event, void *arg) +{ + printf("timeout fired, time to end test\n"); + event_del(*(struct event**)arg); + return; +} + +int +main(int argc, char **argv) +{ + struct event* ev; + struct event* timeout; + struct event_base* base; + + evutil_socket_t pair[2]; + struct timeval tv; + struct cpu_usage_timer timer; + + double usage, secPassed, secUsed; + +#ifdef _WIN32 + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 2); + + (void) WSAStartup(wVersionRequested, &wsaData); +#endif + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) + return (1); + + /* Initalize the event library */ + if (!(base = event_base_new())) + return (1); + + /* Initalize a timeout to terminate the test */ + timeout = evtimer_new(base,timeout_cb,&timeout); + /* and watch for writability on one end of the pipe */ + ev = event_new(base,pair[1],EV_WRITE | EV_PERSIST, write_cb, &ev); + + tv.tv_sec = 1; + tv.tv_usec = 500*1000; + + evtimer_add(timeout, &tv); + + event_add(ev, NULL); + + start_cpu_usage_timer(&timer); + + event_base_dispatch(base); + + event_free(ev); + event_free(timeout); + event_base_free(base); + + get_cpu_usage(&timer, &secPassed, &secUsed, &usage); + + /* attempt to calculate our cpu usage over the test should be + virtually nil */ + + printf("usec used=%d, usec passed=%d, cpu usage=%.2f%%\n", + (int)(secUsed*1e6), + (int)(secPassed*1e6), + usage*100); + + if (usage > 50.0) /* way too high */ + return 1; + + return 0; +} + diff --git a/libs/libevent/docs/test/test-closed.c b/libs/libevent/docs/test/test-closed.c new file mode 100644 index 0000000000..5b04f354ac --- /dev/null +++ b/libs/libevent/docs/test/test-closed.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2002-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2013 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "event2/event-config.h" + +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <unistd.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef EVENT__HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include <event.h> +#include <evutil.h> + +#ifdef EVENT____func__ +#define __func__ EVENT____func__ +#endif + +struct timeval timeout = {3, 0}; + +static void +closed_cb(evutil_socket_t fd, short event, void *arg) +{ + if (EV_TIMEOUT & event) { + printf("%s: Timeout!\n", __func__); + exit(1); + } + + if (EV_CLOSED & event) { + printf("%s: detected socket close with success\n", __func__); + return; + } + + printf("%s: unable to detect socket close\n", __func__); + exit(1); +} + +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + +int +main(int argc, char **argv) +{ + struct event_base *base; + struct event_config *cfg; + struct event *ev; + const char *test = "test string"; + evutil_socket_t pair[2]; + + /* Initialize the library and check if the backend + supports EV_FEATURE_EARLY_CLOSE + */ + cfg = event_config_new(); + event_config_require_features(cfg, EV_FEATURE_EARLY_CLOSE); + base = event_base_new_with_config(cfg); + event_config_free(cfg); + if (!base) { + /* Backend doesn't support EV_FEATURE_EARLY_CLOSE */ + return 0; + } + + /* Create a pair of sockets */ + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) + return (1); + + /* Send some data on socket 0 and immediately close it */ + if (send(pair[0], test, (int)strlen(test)+1, 0) < 0) + return (1); + shutdown(pair[0], SHUT_WR); + + /* Dispatch */ + ev = event_new(base, pair[1], EV_CLOSED | EV_TIMEOUT, closed_cb, event_self_cbarg()); + event_add(ev, &timeout); + event_base_dispatch(base); + + /* Finalize library */ + event_base_free(base); + return 0; +} + diff --git a/libs/libevent/docs/test/test-dumpevents.c b/libs/libevent/docs/test/test-dumpevents.c new file mode 100644 index 0000000000..1c272d4c01 --- /dev/null +++ b/libs/libevent/docs/test/test-dumpevents.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "util-internal.h" +#include "event2/event-config.h" + +#ifdef _WIN32 +#include <winsock2.h> +#include <windows.h> +#else +#include <unistd.h> +#endif + +#include <stdio.h> +#include <event2/event.h> +#include <signal.h> + +static void +sock_perror(const char *s) +{ +#ifdef _WIN32 + const char *err = evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()); + fprintf(stderr, "%s: %s\n", s, err); +#else + perror(s); +#endif +} + +static void +callback1(evutil_socket_t fd, short events, void *arg) +{ +} +static void +callback2(evutil_socket_t fd, short events, void *arg) +{ +} + +/* Testing code for event_base_dump_events(). + + Notes that just because we have code to exercise this function, + doesn't mean that *ANYTHING* about the output format is guaranteed to + remain in the future. + */ +int +main(int argc, char **argv) +{ +#define N_EVENTS 13 + int i; + struct event *ev[N_EVENTS]; + evutil_socket_t pair1[2]; + evutil_socket_t pair2[2]; + struct timeval tv_onesec = {1,0}; + struct timeval tv_two5sec = {2,500*1000}; + const struct timeval *tv_onesec_common; + const struct timeval *tv_two5sec_common; + struct event_base *base; + struct timeval now; + +#ifdef _WIN32 + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 2); + + WSAStartup(wVersionRequested, &wsaData); +#endif + +#ifdef _WIN32 +#define LOCAL_SOCKETPAIR_AF AF_INET +#else +#define LOCAL_SOCKETPAIR_AF AF_UNIX +#endif + + if (evutil_make_internal_pipe_(pair1) < 0 || + evutil_make_internal_pipe_(pair2) < 0) { + sock_perror("evutil_make_internal_pipe_"); + return 1; + } + + if (!(base = event_base_new())) { + fprintf(stderr,"Couldn't make event_base\n"); + return 2; + } + + tv_onesec_common = event_base_init_common_timeout(base, &tv_onesec); + tv_two5sec_common = event_base_init_common_timeout(base, &tv_two5sec); + + ev[0] = event_new(base, pair1[0], EV_WRITE, callback1, NULL); + ev[1] = event_new(base, pair1[1], EV_READ|EV_PERSIST, callback1, NULL); + ev[2] = event_new(base, pair2[0], EV_WRITE|EV_PERSIST, callback2, NULL); + ev[3] = event_new(base, pair2[1], EV_READ, callback2, NULL); + + /* For timers */ + ev[4] = evtimer_new(base, callback1, NULL); + ev[5] = evtimer_new(base, callback1, NULL); + ev[6] = evtimer_new(base, callback1, NULL); + ev[7] = event_new(base, -1, EV_PERSIST, callback2, NULL); + ev[8] = event_new(base, -1, EV_PERSIST, callback2, NULL); + ev[9] = event_new(base, -1, EV_PERSIST, callback2, NULL); + + /* To activate */ + ev[10] = event_new(base, -1, 0, callback1, NULL); + ev[11] = event_new(base, -1, 0, callback2, NULL); + + /* Signals */ + ev[12] = evsignal_new(base, SIGINT, callback2, NULL); + + event_add(ev[0], NULL); + event_add(ev[1], &tv_onesec); + event_add(ev[2], tv_onesec_common); + event_add(ev[3], tv_two5sec_common); + + event_add(ev[4], tv_onesec_common); + event_add(ev[5], tv_onesec_common); + event_add(ev[6], &tv_onesec); + event_add(ev[7], tv_two5sec_common); + event_add(ev[8], tv_onesec_common); + event_add(ev[9], &tv_two5sec); + + event_active(ev[10], EV_READ, 1); + event_active(ev[11], EV_READ|EV_WRITE|EV_TIMEOUT, 1); + event_active(ev[1], EV_READ, 1); + + event_add(ev[12], NULL); + + evutil_gettimeofday(&now,NULL); + puts("=====expected"); + printf("Now= %ld.%06d\n",(long)now.tv_sec,(int)now.tv_usec); + puts("Inserted:"); + printf(" %p [fd %ld] Write\n",ev[0],(long)pair1[0]); + printf(" %p [fd %ld] Read Persist Timeout=T+1\n",ev[1],(long)pair1[1]); + printf(" %p [fd %ld] Write Persist Timeout=T+1\n",ev[2],(long)pair2[0]); + printf(" %p [fd %ld] Read Timeout=T+2.5\n",ev[3],(long)pair2[1]); + printf(" %p [fd -1] Timeout=T+1\n",ev[4]); + printf(" %p [fd -1] Timeout=T+1\n",ev[5]); + printf(" %p [fd -1] Timeout=T+1\n",ev[6]); + printf(" %p [fd -1] Persist Timeout=T+2.5\n",ev[7]); + printf(" %p [fd -1] Persist Timeout=T+1\n",ev[8]); + printf(" %p [fd -1] Persist Timeout=T+2.5\n",ev[9]); + printf(" %p [sig %d] Signal Persist\n", ev[12], (int)SIGINT); + + puts("Active:"); + printf(" %p [fd -1, priority=0] Read active\n", ev[10]); + printf(" %p [fd -1, priority=0] Read Write Timeout active\n", ev[11]); + printf(" %p [fd %ld, priority=0] Read active\n", ev[1], (long)pair1[1]); + + puts("======received"); + event_base_dump_events(base, stdout); + + for (i = 0; i < N_EVENTS; ++i) { + event_free(ev[i]); + } + event_base_free(base); + + return 0; +} + diff --git a/libs/libevent/docs/test/test-eof.c b/libs/libevent/docs/test/test-eof.c new file mode 100644 index 0000000000..a9ca5343a2 --- /dev/null +++ b/libs/libevent/docs/test/test-eof.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2002-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "event2/event-config.h" + +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <unistd.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef EVENT__HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include <event.h> +#include <evutil.h> + +#ifdef EVENT____func__ +#define __func__ EVENT____func__ +#endif + +int test_okay = 1; +int called = 0; +struct timeval timeout = {60, 0}; + +static void +read_cb(evutil_socket_t fd, short event, void *arg) +{ + char buf[256]; + int len; + + if (EV_TIMEOUT & event) { + printf("%s: Timeout!\n", __func__); + exit(1); + } + + len = recv(fd, buf, sizeof(buf), 0); + + printf("%s: read %d%s\n", __func__, + len, len ? "" : " - means EOF"); + + if (len) { + if (!called) + event_add(arg, &timeout); + } else if (called == 1) + test_okay = 0; + + called++; +} + +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + +int +main(int argc, char **argv) +{ + struct event ev; + const char *test = "test string"; + evutil_socket_t pair[2]; + +#ifdef _WIN32 + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 2); + + (void) WSAStartup(wVersionRequested, &wsaData); +#endif + + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) + return (1); + + + if (send(pair[0], test, (int)strlen(test)+1, 0) < 0) + return (1); + shutdown(pair[0], SHUT_WR); + + /* Initalize the event library */ + event_init(); + + /* Initalize one event */ + event_set(&ev, pair[1], EV_READ | EV_TIMEOUT, read_cb, &ev); + + event_add(&ev, &timeout); + + event_dispatch(); + + return (test_okay); +} + diff --git a/libs/libevent/docs/test/test-fdleak.c b/libs/libevent/docs/test/test-fdleak.c new file mode 100644 index 0000000000..4c4eba25e7 --- /dev/null +++ b/libs/libevent/docs/test/test-fdleak.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2012 Ross Lagerwall <rosslagerwall@gmail.com> + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ + +#include "event2/event-config.h" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef EVENT__HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif +#ifdef EVENT__HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif + +#include "event2/event.h" +#include "event2/bufferevent.h" +#include "event2/buffer.h" +#include "event2/listener.h" + +/* Number of requests to make. Setting this too high might result in the machine + running out of ephemeral ports */ +#ifdef _WIN32 +#define MAX_REQUESTS 1000 +#else +#define MAX_REQUESTS 4000 +#endif + +/* Provide storage for the address, both for the server & the clients */ +static struct sockaddr_in saddr; + +/* Number of sucessful requests so far */ +static int num_requests; + +static void start_client(struct event_base *base); + +static void +my_perror(const char *s) +{ + fprintf(stderr, "%s: %s", + s, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())); +} + +/* +=============================================== +Server functions +=============================================== +*/ + +/* Read a byte from the client and write it back */ +static void +server_read_cb(struct bufferevent *bev, void *ctx) +{ + while (evbuffer_get_length(bufferevent_get_input(bev))) { + unsigned char tmp; + bufferevent_read(bev, &tmp, 1); + bufferevent_write(bev, &tmp, 1); + } +} + +/* Wait for an EOF and then free the bufferevent */ +static void +server_event_cb(struct bufferevent *bev, short events, void *ctx) +{ + if (events & BEV_EVENT_ERROR) { + my_perror("Error from bufferevent"); + exit(1); + } else if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) { + bufferevent_free(bev); + } +} + +/* Accept a client socket and set it up to for reading & writing */ +static void +listener_accept_cb(struct evconnlistener *listener, evutil_socket_t sock, + struct sockaddr *addr, int len, void *ptr) +{ + struct event_base *base = evconnlistener_get_base(listener); + struct bufferevent *bev = bufferevent_socket_new(base, sock, + BEV_OPT_CLOSE_ON_FREE); + + bufferevent_setcb(bev, server_read_cb, NULL, server_event_cb, NULL); + bufferevent_enable(bev, EV_READ|EV_WRITE); +} + +/* Start the server listening on a random port and start the first client. */ +static void +start_loop(void) +{ + struct event_base *base; + struct evconnlistener *listener; + struct sockaddr_storage ss; + ev_socklen_t socklen = sizeof(ss); + evutil_socket_t fd; + + base = event_base_new(); + if (base == NULL) { + puts("Could not open event base!"); + exit(1); + } + + listener = evconnlistener_new_bind(base, listener_accept_cb, NULL, + LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, + -1, (struct sockaddr *)&saddr, sizeof(saddr)); + if (listener == NULL) { + my_perror("Could not create listener!"); + exit(1); + } + fd = evconnlistener_get_fd(listener); + if (fd < 0) { + puts("Couldn't get fd from listener"); + exit(1); + } + if (getsockname(fd, (struct sockaddr *)&ss, &socklen) < 0) { + my_perror("getsockname()"); + exit(1); + } + memcpy(&saddr, &ss, sizeof(saddr)); + if (saddr.sin_family != AF_INET) { + puts("AF mismatch from getsockname()."); + exit(1); + } + + start_client(base); + + event_base_dispatch(base); +} + +/* +=============================================== +Client functions +=============================================== +*/ + +/* Check that the server sends back the same byte that the client sent. + If MAX_REQUESTS have been reached, exit. Otherwise, start another client. */ +static void +client_read_cb(struct bufferevent *bev, void *ctx) +{ + unsigned char tmp; + struct event_base *base = bufferevent_get_base(bev); + + bufferevent_read(bev, &tmp, 1); + if (tmp != 'A') { + puts("Incorrect data received!"); + exit(2); + } + bufferevent_free(bev); + + num_requests++; + if (num_requests == MAX_REQUESTS) { + event_base_loopbreak(base); + } else { + start_client(base); + } +} + +/* Send a byte to the server. */ +static void +client_event_cb(struct bufferevent *bev, short events, void *ctx) +{ + if (events & BEV_EVENT_CONNECTED) { + unsigned char tmp = 'A'; + bufferevent_write(bev, &tmp, 1); + } else if (events & BEV_EVENT_ERROR) { + puts("Client socket got error!"); + exit(2); + } + + bufferevent_enable(bev, EV_READ); +} + +/* Open a client socket to connect to localhost on sin */ +static void +start_client(struct event_base *base) +{ + struct bufferevent *bev = bufferevent_socket_new(base, -1, + BEV_OPT_CLOSE_ON_FREE); + bufferevent_setcb(bev, client_read_cb, NULL, client_event_cb, NULL); + + if (bufferevent_socket_connect(bev, (struct sockaddr *)&saddr, + sizeof(saddr)) < 0) { + my_perror("Could not connect!"); + bufferevent_free(bev); + exit(2); + } +} + +int +main(int argc, char **argv) +{ +#ifdef EVENT__HAVE_SETRLIMIT + /* Set the fd limit to a low value so that any fd leak is caught without + making many requests. */ + struct rlimit rl; + rl.rlim_cur = rl.rlim_max = 20; + if (setrlimit(RLIMIT_NOFILE, &rl) == -1) { + my_perror("setrlimit"); + exit(3); + } +#endif + +#ifdef _WIN32 + WSADATA WSAData; + WSAStartup(0x101, &WSAData); +#endif + + /* Set up an address, used by both client & server. */ + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = htonl(0x7f000001); + saddr.sin_port = 0; /* Tell the implementation to pick a port. */ + + start_loop(); + + return 0; +} + +/* XXX why does this test cause so much latency sometimes (OSX 10.5)? */ diff --git a/libs/libevent/docs/test/test-init.c b/libs/libevent/docs/test/test-init.c new file mode 100644 index 0000000000..92fbc6b146 --- /dev/null +++ b/libs/libevent/docs/test/test-init.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "event2/event-config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef EVENT__HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#ifndef _WIN32 +#include <unistd.h> +#endif +#include <errno.h> + +#include <event.h> + +int +main(int argc, char **argv) +{ +#ifdef _WIN32 + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 2); + + (void) WSAStartup(wVersionRequested, &wsaData); +#endif + + /* Initalize the event library */ + event_init(); + + return (0); +} + diff --git a/libs/libevent/docs/test/test-ratelim.c b/libs/libevent/docs/test/test-ratelim.c new file mode 100644 index 0000000000..17babfdcbc --- /dev/null +++ b/libs/libevent/docs/test/test-ratelim.c @@ -0,0 +1,601 @@ +/* + * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "../util-internal.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <math.h> + +#ifdef _WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +# ifdef _XOPEN_SOURCE_EXTENDED +# include <arpa/inet.h> +# endif +#endif +#include <signal.h> + +#include "event2/bufferevent.h" +#include "event2/buffer.h" +#include "event2/event.h" +#include "event2/util.h" +#include "event2/listener.h" +#include "event2/thread.h" + +static struct evutil_weakrand_state weakrand_state; + +static int cfg_verbose = 0; +static int cfg_help = 0; + +static int cfg_n_connections = 30; +static int cfg_duration = 5; +static int cfg_connlimit = 0; +static int cfg_grouplimit = 0; +static int cfg_tick_msec = 1000; +static int cfg_min_share = -1; +static int cfg_group_drain = 0; + +static int cfg_connlimit_tolerance = -1; +static int cfg_grouplimit_tolerance = -1; +static int cfg_stddev_tolerance = -1; + +#ifdef _WIN32 +static int cfg_enable_iocp = 0; +#endif + +static struct timeval cfg_tick = { 0, 500*1000 }; + +static struct ev_token_bucket_cfg *conn_bucket_cfg = NULL; +static struct ev_token_bucket_cfg *group_bucket_cfg = NULL; +struct bufferevent_rate_limit_group *ratelim_group = NULL; +static double seconds_per_tick = 0.0; + +struct client_state { + size_t queued; + ev_uint64_t received; + +}; +static const struct timeval *ms100_common=NULL; + +/* info from check_bucket_levels_cb */ +static int total_n_bev_checks = 0; +static ev_int64_t total_rbucket_level=0; +static ev_int64_t total_wbucket_level=0; +static ev_int64_t total_max_to_read=0; +static ev_int64_t total_max_to_write=0; +static ev_int64_t max_bucket_level=EV_INT64_MIN; +static ev_int64_t min_bucket_level=EV_INT64_MAX; + +/* from check_group_bucket_levels_cb */ +static int total_n_group_bev_checks = 0; +static ev_int64_t total_group_rbucket_level = 0; +static ev_int64_t total_group_wbucket_level = 0; + +static int n_echo_conns_open = 0; + +/* Info on the open connections */ +struct bufferevent **bevs; +struct client_state *states; +struct bufferevent_rate_limit_group *group = NULL; + +static void check_bucket_levels_cb(evutil_socket_t fd, short events, void *arg); + +static void +loud_writecb(struct bufferevent *bev, void *ctx) +{ + struct client_state *cs = ctx; + struct evbuffer *output = bufferevent_get_output(bev); + char buf[1024]; + int r = evutil_weakrand_(&weakrand_state); + memset(buf, r, sizeof(buf)); + while (evbuffer_get_length(output) < 8192) { + evbuffer_add(output, buf, sizeof(buf)); + cs->queued += sizeof(buf); + } +} + +static void +discard_readcb(struct bufferevent *bev, void *ctx) +{ + struct client_state *cs = ctx; + struct evbuffer *input = bufferevent_get_input(bev); + size_t len = evbuffer_get_length(input); + evbuffer_drain(input, len); + cs->received += len; +} + +static void +write_on_connectedcb(struct bufferevent *bev, short what, void *ctx) +{ + if (what & BEV_EVENT_CONNECTED) { + loud_writecb(bev, ctx); + /* XXXX this shouldn't be needed. */ + bufferevent_enable(bev, EV_READ|EV_WRITE); + } +} + +static void +echo_readcb(struct bufferevent *bev, void *ctx) +{ + struct evbuffer *input = bufferevent_get_input(bev); + struct evbuffer *output = bufferevent_get_output(bev); + + evbuffer_add_buffer(output, input); + if (evbuffer_get_length(output) > 1024000) + bufferevent_disable(bev, EV_READ); +} + +static void +echo_writecb(struct bufferevent *bev, void *ctx) +{ + struct evbuffer *output = bufferevent_get_output(bev); + if (evbuffer_get_length(output) < 512000) + bufferevent_enable(bev, EV_READ); +} + +static void +echo_eventcb(struct bufferevent *bev, short what, void *ctx) +{ + if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { + --n_echo_conns_open; + bufferevent_free(bev); + } +} + +static void +echo_listenercb(struct evconnlistener *listener, evutil_socket_t newsock, + struct sockaddr *sourceaddr, int socklen, void *ctx) +{ + struct event_base *base = ctx; + int flags = BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE; + struct bufferevent *bev; + + bev = bufferevent_socket_new(base, newsock, flags); + bufferevent_setcb(bev, echo_readcb, echo_writecb, echo_eventcb, NULL); + if (conn_bucket_cfg) { + struct event *check_event = + event_new(base, -1, EV_PERSIST, check_bucket_levels_cb, bev); + bufferevent_set_rate_limit(bev, conn_bucket_cfg); + + assert(bufferevent_get_token_bucket_cfg(bev) != NULL); + event_add(check_event, ms100_common); + } + if (ratelim_group) + bufferevent_add_to_rate_limit_group(bev, ratelim_group); + ++n_echo_conns_open; + bufferevent_enable(bev, EV_READ|EV_WRITE); +} + +/* Called periodically to check up on how full the buckets are */ +static void +check_bucket_levels_cb(evutil_socket_t fd, short events, void *arg) +{ + struct bufferevent *bev = arg; + + ev_ssize_t r = bufferevent_get_read_limit(bev); + ev_ssize_t w = bufferevent_get_write_limit(bev); + ev_ssize_t rm = bufferevent_get_max_to_read(bev); + ev_ssize_t wm = bufferevent_get_max_to_write(bev); + /* XXXX check that no value is above the cofigured burst + * limit */ + total_rbucket_level += r; + total_wbucket_level += w; + total_max_to_read += rm; + total_max_to_write += wm; +#define B(x) \ + if ((x) > max_bucket_level) \ + max_bucket_level = (x); \ + if ((x) < min_bucket_level) \ + min_bucket_level = (x) + B(r); + B(w); +#undef B + + total_n_bev_checks++; + if (total_n_bev_checks >= .8 * ((double)cfg_duration / cfg_tick_msec) * cfg_n_connections) { + event_free(event_base_get_running_event(bufferevent_get_base(bev))); + } +} + +static void +check_group_bucket_levels_cb(evutil_socket_t fd, short events, void *arg) +{ + if (ratelim_group) { + ev_ssize_t r = bufferevent_rate_limit_group_get_read_limit(ratelim_group); + ev_ssize_t w = bufferevent_rate_limit_group_get_write_limit(ratelim_group); + total_group_rbucket_level += r; + total_group_wbucket_level += w; + } + ++total_n_group_bev_checks; +} + +static void +group_drain_cb(evutil_socket_t fd, short events, void *arg) +{ + bufferevent_rate_limit_group_decrement_read(ratelim_group, cfg_group_drain); + bufferevent_rate_limit_group_decrement_write(ratelim_group, cfg_group_drain); +} + +static int +test_ratelimiting(void) +{ + struct event_base *base; + struct sockaddr_in sin; + struct evconnlistener *listener; + + struct sockaddr_storage ss; + ev_socklen_t slen; + + int i; + + struct timeval tv; + + ev_uint64_t total_received; + double total_sq_persec, total_persec; + double variance; + double expected_total_persec = -1.0, expected_avg_persec = -1.0; + int ok = 1; + struct event_config *base_cfg; + struct event *periodic_level_check; + struct event *group_drain_event=NULL; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ + sin.sin_port = 0; /* unspecified port */ + + if (0) + event_enable_debug_mode(); + + base_cfg = event_config_new(); + +#ifdef _WIN32 + if (cfg_enable_iocp) { + evthread_use_windows_threads(); + event_config_set_flag(base_cfg, EVENT_BASE_FLAG_STARTUP_IOCP); + } +#endif + + base = event_base_new_with_config(base_cfg); + event_config_free(base_cfg); + if (! base) { + fprintf(stderr, "Couldn't create event_base"); + return 1; + } + + listener = evconnlistener_new_bind(base, echo_listenercb, base, + LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1, + (struct sockaddr *)&sin, sizeof(sin)); + if (! listener) { + fprintf(stderr, "Couldn't create listener"); + return 1; + } + + slen = sizeof(ss); + if (getsockname(evconnlistener_get_fd(listener), (struct sockaddr *)&ss, + &slen) < 0) { + perror("getsockname"); + return 1; + } + + if (cfg_connlimit > 0) { + conn_bucket_cfg = ev_token_bucket_cfg_new( + cfg_connlimit, cfg_connlimit * 4, + cfg_connlimit, cfg_connlimit * 4, + &cfg_tick); + assert(conn_bucket_cfg); + } + + if (cfg_grouplimit > 0) { + group_bucket_cfg = ev_token_bucket_cfg_new( + cfg_grouplimit, cfg_grouplimit * 4, + cfg_grouplimit, cfg_grouplimit * 4, + &cfg_tick); + group = ratelim_group = bufferevent_rate_limit_group_new( + base, group_bucket_cfg); + expected_total_persec = cfg_grouplimit - (cfg_group_drain / seconds_per_tick); + expected_avg_persec = cfg_grouplimit / cfg_n_connections; + if (cfg_connlimit > 0 && expected_avg_persec > cfg_connlimit) + expected_avg_persec = cfg_connlimit; + if (cfg_min_share >= 0) + bufferevent_rate_limit_group_set_min_share( + ratelim_group, cfg_min_share); + } + + if (expected_avg_persec < 0 && cfg_connlimit > 0) + expected_avg_persec = cfg_connlimit; + + if (expected_avg_persec > 0) + expected_avg_persec /= seconds_per_tick; + if (expected_total_persec > 0) + expected_total_persec /= seconds_per_tick; + + bevs = calloc(cfg_n_connections, sizeof(struct bufferevent *)); + states = calloc(cfg_n_connections, sizeof(struct client_state)); + + for (i = 0; i < cfg_n_connections; ++i) { + bevs[i] = bufferevent_socket_new(base, -1, + BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE); + assert(bevs[i]); + bufferevent_setcb(bevs[i], discard_readcb, loud_writecb, + write_on_connectedcb, &states[i]); + bufferevent_enable(bevs[i], EV_READ|EV_WRITE); + bufferevent_socket_connect(bevs[i], (struct sockaddr *)&ss, + slen); + } + + tv.tv_sec = cfg_duration - 1; + tv.tv_usec = 995000; + + event_base_loopexit(base, &tv); + + tv.tv_sec = 0; + tv.tv_usec = 100*1000; + ms100_common = event_base_init_common_timeout(base, &tv); + + periodic_level_check = event_new(base, -1, EV_PERSIST, check_group_bucket_levels_cb, NULL); + event_add(periodic_level_check, ms100_common); + + if (cfg_group_drain && ratelim_group) { + group_drain_event = event_new(base, -1, EV_PERSIST, group_drain_cb, NULL); + event_add(group_drain_event, &cfg_tick); + } + + event_base_dispatch(base); + + ratelim_group = NULL; /* So no more responders get added */ + event_free(periodic_level_check); + if (group_drain_event) + event_del(group_drain_event); + + for (i = 0; i < cfg_n_connections; ++i) { + bufferevent_free(bevs[i]); + } + evconnlistener_free(listener); + + /* Make sure no new echo_conns get added to the group. */ + ratelim_group = NULL; + + /* This should get _everybody_ freed */ + while (n_echo_conns_open) { + printf("waiting for %d conns\n", n_echo_conns_open); + tv.tv_sec = 0; + tv.tv_usec = 300000; + event_base_loopexit(base, &tv); + event_base_dispatch(base); + } + + if (group) + bufferevent_rate_limit_group_free(group); + + if (total_n_bev_checks) { + printf("Average read bucket level: %f\n", + (double)total_rbucket_level/total_n_bev_checks); + printf("Average write bucket level: %f\n", + (double)total_wbucket_level/total_n_bev_checks); + printf("Highest read bucket level: %f\n", + (double)max_bucket_level); + printf("Highest write bucket level: %f\n", + (double)min_bucket_level); + printf("Average max-to-read: %f\n", + ((double)total_max_to_read)/total_n_bev_checks); + printf("Average max-to-write: %f\n", + ((double)total_max_to_write)/total_n_bev_checks); + } + if (total_n_group_bev_checks) { + printf("Average group read bucket level: %f\n", + ((double)total_group_rbucket_level)/total_n_group_bev_checks); + printf("Average group write bucket level: %f\n", + ((double)total_group_wbucket_level)/total_n_group_bev_checks); + } + + total_received = 0; + total_persec = 0.0; + total_sq_persec = 0.0; + for (i=0; i < cfg_n_connections; ++i) { + double persec = states[i].received; + persec /= cfg_duration; + total_received += states[i].received; + total_persec += persec; + total_sq_persec += persec*persec; + printf("%d: %f per second\n", i+1, persec); + } + printf(" total: %f per second\n", + ((double)total_received)/cfg_duration); + if (expected_total_persec > 0) { + double diff = expected_total_persec - + ((double)total_received/cfg_duration); + printf(" [Off by %lf]\n", diff); + if (cfg_grouplimit_tolerance > 0 && + fabs(diff) > cfg_grouplimit_tolerance) { + fprintf(stderr, "Group bandwidth out of bounds\n"); + ok = 0; + } + } + + printf(" average: %f per second\n", + (((double)total_received)/cfg_duration)/cfg_n_connections); + if (expected_avg_persec > 0) { + double diff = expected_avg_persec - (((double)total_received)/cfg_duration)/cfg_n_connections; + printf(" [Off by %lf]\n", diff); + if (cfg_connlimit_tolerance > 0 && + fabs(diff) > cfg_connlimit_tolerance) { + fprintf(stderr, "Connection bandwidth out of bounds\n"); + ok = 0; + } + } + + variance = total_sq_persec/cfg_n_connections - total_persec*total_persec/(cfg_n_connections*cfg_n_connections); + + printf(" stddev: %f per second\n", sqrt(variance)); + if (cfg_stddev_tolerance > 0 && + sqrt(variance) > cfg_stddev_tolerance) { + fprintf(stderr, "Connection variance out of bounds\n"); + ok = 0; + } + + event_base_free(base); + free(bevs); + free(states); + + return ok ? 0 : 1; +} + +static struct option { + const char *name; int *ptr; int min; int isbool; +} options[] = { + { "-v", &cfg_verbose, 0, 1 }, + { "-h", &cfg_help, 0, 1 }, + { "-n", &cfg_n_connections, 1, 0 }, + { "-d", &cfg_duration, 1, 0 }, + { "-c", &cfg_connlimit, 0, 0 }, + { "-g", &cfg_grouplimit, 0, 0 }, + { "-G", &cfg_group_drain, -100000, 0 }, + { "-t", &cfg_tick_msec, 10, 0 }, + { "--min-share", &cfg_min_share, 0, 0 }, + { "--check-connlimit", &cfg_connlimit_tolerance, 0, 0 }, + { "--check-grouplimit", &cfg_grouplimit_tolerance, 0, 0 }, + { "--check-stddev", &cfg_stddev_tolerance, 0, 0 }, +#ifdef _WIN32 + { "--iocp", &cfg_enable_iocp, 0, 1 }, +#endif + { NULL, NULL, -1, 0 }, +}; + +static int +handle_option(int argc, char **argv, int *i, const struct option *opt) +{ + long val; + char *endptr = NULL; + if (opt->isbool) { + *opt->ptr = 1; + return 0; + } + if (*i + 1 == argc) { + fprintf(stderr, "Too few arguments to '%s'\n",argv[*i]); + return -1; + } + val = strtol(argv[*i+1], &endptr, 10); + if (*argv[*i+1] == '\0' || !endptr || *endptr != '\0') { + fprintf(stderr, "Couldn't parse numeric value '%s'\n", + argv[*i+1]); + return -1; + } + if (val < opt->min || val > 0x7fffffff) { + fprintf(stderr, "Value '%s' is out-of-range'\n", + argv[*i+1]); + return -1; + } + *opt->ptr = (int)val; + ++*i; + return 0; +} + +static void +usage(void) +{ + fprintf(stderr, +"test-ratelim [-v] [-n INT] [-d INT] [-c INT] [-g INT] [-t INT]\n\n" +"Pushes bytes through a number of possibly rate-limited connections, and\n" +"displays average throughput.\n\n" +" -n INT: Number of connections to open (default: 30)\n" +" -d INT: Duration of the test in seconds (default: 5 sec)\n"); + fprintf(stderr, +" -c INT: Connection-rate limit applied to each connection in bytes per second\n" +" (default: None.)\n" +" -g INT: Group-rate limit applied to sum of all usage in bytes per second\n" +" (default: None.)\n" +" -G INT: drain INT bytes from the group limit every tick. (default: 0)\n" +" -t INT: Granularity of timing, in milliseconds (default: 1000 msec)\n"); +} + +int +main(int argc, char **argv) +{ + int i,j; + double ratio; + +#ifdef _WIN32 + WORD wVersionRequested = MAKEWORD(2,2); + WSADATA wsaData; + + (void) WSAStartup(wVersionRequested, &wsaData); +#endif + + evutil_weakrand_seed_(&weakrand_state, 0); + +#ifndef _WIN32 + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + return 1; +#endif + for (i = 1; i < argc; ++i) { + for (j = 0; options[j].name; ++j) { + if (!strcmp(argv[i],options[j].name)) { + if (handle_option(argc,argv,&i,&options[j])<0) + return 1; + goto again; + } + } + fprintf(stderr, "Unknown option '%s'\n", argv[i]); + usage(); + return 1; + again: + ; + } + if (cfg_help) { + usage(); + return 0; + } + + cfg_tick.tv_sec = cfg_tick_msec / 1000; + cfg_tick.tv_usec = (cfg_tick_msec % 1000)*1000; + + seconds_per_tick = ratio = cfg_tick_msec / 1000.0; + + cfg_connlimit *= ratio; + cfg_grouplimit *= ratio; + + { + struct timeval tv; + evutil_gettimeofday(&tv, NULL); +#ifdef _WIN32 + srand(tv.tv_usec); +#else + srandom(tv.tv_usec); +#endif + } + +#ifndef EVENT__DISABLE_THREAD_SUPPORT + evthread_enable_lock_debugging(); +#endif + + return test_ratelimiting(); +} diff --git a/libs/libevent/docs/test/test-ratelim.sh b/libs/libevent/docs/test/test-ratelim.sh new file mode 100644 index 0000000000..b5e0ca62a9 --- /dev/null +++ b/libs/libevent/docs/test/test-ratelim.sh @@ -0,0 +1,88 @@ +#!/bin/sh + +FAILED=no + +if test "x$TEST_OUTPUT_FILE" = "x" +then + TEST_OUTPUT_FILE=/dev/null +fi + +# /bin/echo is a little more likely to support -n than sh's builtin echo. +if test -x /bin/echo +then + ECHO=/bin/echo +else + ECHO=echo +fi + +if test "$TEST_OUTPUT_FILE" != "/dev/null" +then + touch "$TEST_OUTPUT_FILE" || exit 1 +fi + +TEST_DIR=. + +T=`echo "$0" | sed -e 's/test-ratelim.sh$//'` +if test -x "$T/test-ratelim" +then + TEST_DIR="$T" +fi + +announce () { + echo $@ + echo $@ >>"$TEST_OUTPUT_FILE" +} + +announce_n () { + $ECHO -n $@ + echo $@ >>"$TEST_OUTPUT_FILE" +} + + +run_tests () { + announce_n " Group limits, no connection limit:" + if $TEST_DIR/test-ratelim -g 30000 -n 30 -t 100 --check-grouplimit 1000 --check-stddev 100 >>"$TEST_OUTPUT_FILE" + then + announce OKAY + else + announce FAILED + FAILED=yes + fi + + announce_n " Connection limit, no group limit:" + if $TEST_DIR/test-ratelim -c 1000 -n 30 -t 100 --check-connlimit 50 --check-stddev 50 >>"$TEST_OUTPUT_FILE" + then + announce OKAY ; + else + announce FAILED ; + FAILED=yes + fi + + announce_n " Connection limit and group limit:" + if $TEST_DIR/test-ratelim -c 1000 -g 30000 -n 30 -t 100 --check-grouplimit 1000 --check-connlimit 50 --check-stddev 50 >>"$TEST_OUTPUT_FILE" + then + announce OKAY ; + else + announce FAILED ; + FAILED=yes + fi + + announce_n " Connection limit and group limit with independent drain:" + if $TEST_DIR/test-ratelim -c 1000 -g 35000 -n 30 -t 100 -G 500 --check-grouplimit 1000 --check-connlimit 50 --check-stddev 50 >>"$TEST_OUTPUT_FILE" + then + announce OKAY ; + else + announce FAILED ; + FAILED=yes + fi + + +} + +announce "Running rate-limiting tests:" + +run_tests + +if test "$FAILED" = "yes"; then + exit 1 +fi diff --git a/libs/libevent/docs/test/test-time.c b/libs/libevent/docs/test/test-time.c new file mode 100644 index 0000000000..bcc7086df7 --- /dev/null +++ b/libs/libevent/docs/test/test-time.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2002-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "event2/event-config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#ifndef _WIN32 +#include <unistd.h> +#include <sys/time.h> +#endif +#include <errno.h> + +#include "event2/event.h" +#include "event2/event_compat.h" +#include "event2/event_struct.h" +#include "util-internal.h" + +int called = 0; + +#define NEVENT 20000 + +struct event *ev[NEVENT]; + +struct evutil_weakrand_state weakrand_state; + +static int +rand_int(int n) +{ + return evutil_weakrand_(&weakrand_state) % n; +} + +static void +time_cb(evutil_socket_t fd, short event, void *arg) +{ + struct timeval tv; + int i, j; + + called++; + + if (called < 10*NEVENT) { + for (i = 0; i < 10; i++) { + j = rand_int(NEVENT); + tv.tv_sec = 0; + tv.tv_usec = rand_int(50000); + if (tv.tv_usec % 2 || called < NEVENT) + evtimer_add(ev[j], &tv); + else + evtimer_del(ev[j]); + } + } +} + +int +main(int argc, char **argv) +{ + struct timeval tv; + int i; +#ifdef _WIN32 + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 2); + + (void) WSAStartup(wVersionRequested, &wsaData); +#endif + + evutil_weakrand_seed_(&weakrand_state, 0); + + /* Initalize the event library */ + event_init(); + + for (i = 0; i < NEVENT; i++) { + ev[i] = malloc(sizeof(struct event)); + + /* Initalize one event */ + evtimer_set(ev[i], time_cb, ev[i]); + tv.tv_sec = 0; + tv.tv_usec = rand_int(50000); + evtimer_add(ev[i], &tv); + } + + event_dispatch(); + + + printf("%d, %d\n", called, NEVENT); + return (called < NEVENT); +} + diff --git a/libs/libevent/docs/test/test-weof.c b/libs/libevent/docs/test/test-weof.c new file mode 100644 index 0000000000..c379f287cb --- /dev/null +++ b/libs/libevent/docs/test/test-weof.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2002-2007 Niels Provos <provos@citi.umich.edu> + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#include "event2/event-config.h" + +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <unistd.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#ifdef EVENT__HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef EVENT__HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <errno.h> + +#include "event2/event.h" +#include "event2/event_struct.h" +#include "event2/event_compat.h" +#include "event2/util.h" + +#ifdef EVENT____func__ +#define __func__ EVENT____func__ +#endif + +evutil_socket_t pair[2]; +int test_okay = 1; +int called = 0; + +static void +write_cb(evutil_socket_t fd, short event, void *arg) +{ + const char *test = "test string"; + int len; + + len = send(fd, test, (int)strlen(test) + 1, 0); + + printf("%s: write %d%s\n", __func__, + len, len ? "" : " - means EOF"); + + if (len > 0) { + if (!called) + event_add(arg, NULL); + evutil_closesocket(pair[0]); + } else if (called == 1) + test_okay = 0; + + called++; +} + +int +main(int argc, char **argv) +{ + struct event ev; + +#ifdef _WIN32 + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 2); + + (void) WSAStartup(wVersionRequested, &wsaData); +#endif + +#ifndef _WIN32 + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + return (1); +#endif + + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) + return (1); + + /* Initalize the event library */ + event_init(); + + /* Initalize one event */ + event_set(&ev, pair[1], EV_WRITE, write_cb, &ev); + + event_add(&ev, NULL); + + event_dispatch(); + + return (test_okay); +} + diff --git a/libs/libevent/docs/test/test.sh b/libs/libevent/docs/test/test.sh new file mode 100644 index 0000000000..b73c1adced --- /dev/null +++ b/libs/libevent/docs/test/test.sh @@ -0,0 +1,160 @@ +#!/bin/sh + +BACKENDS="EVPORT KQUEUE EPOLL DEVPOLL POLL SELECT WIN32" +TESTS="test-eof test-closed test-weof test-time test-changelist test-fdleak" +FAILED=no +TEST_OUTPUT_FILE=${TEST_OUTPUT_FILE:-/dev/null} +REGRESS_ARGS=${REGRESS_ARGS:-} + +# /bin/echo is a little more likely to support -n than sh's builtin echo, +# printf is even more likely +if test "`printf %s hello 2>&1`" = "hello" +then + ECHO_N="printf %s" +else + if test -x /bin/echo + then + ECHO_N="/bin/echo -n" + else + ECHO_N="echo -n" + fi +fi + +if test "$TEST_OUTPUT_FILE" != "/dev/null" +then + touch "$TEST_OUTPUT_FILE" || exit 1 +fi + +TEST_DIR=. +TEST_SRC_DIR=. + +T=`echo "$0" | sed -e 's/test.sh$//' | sed -e 's/test-script.sh//' ` +if test -x "$T/test-init" +then + TEST_DIR="$T" +elif test -x "./test/test-init" +then + TEST_DIR="./test" +fi +if test -f "$T/check-dumpevents.py" +then + TEST_SRC_DIR="$T" +elif test -f "./test/check-dumpevents.py" +then + TEST_SRC_DIR="./test" +fi + +setup () { + for i in $BACKENDS; do + eval "EVENT_NO$i=yes; export EVENT_NO$i" + done + unset EVENT_EPOLL_USE_CHANGELIST + unset EVENT_PRECISE_TIMER +} + +announce () { + echo "$@" + echo "$@" >>"$TEST_OUTPUT_FILE" +} + +announce_n () { + $ECHO_N "$@" + echo "$@" >>"$TEST_OUTPUT_FILE" +} + + +run_tests () { + if $TEST_DIR/test-init 2>>"$TEST_OUTPUT_FILE" ; + then + true + else + announce Skipping test + return + fi + for i in $TESTS; do + announce_n " $i: " + if $TEST_DIR/$i >>"$TEST_OUTPUT_FILE" ; + then + announce OKAY ; + else + announce FAILED ; + FAILED=yes + fi + done + announce_n " test-dumpevents: " + if python2 -c 'import sys; assert(sys.version_info >= (2, 4))' 2>/dev/null && test -f $TEST_SRC_DIR/check-dumpevents.py; then + if $TEST_DIR/test-dumpevents | python2 $TEST_SRC_DIR/check-dumpevents.py >> "$TEST_OUTPUT_FILE" ; + then + announce OKAY ; + else + announce FAILED ; + fi + else + # no python + if $TEST_DIR/test-dumpevents >/dev/null; then + announce "OKAY (output not checked)" ; + else + announce "FAILED (output not checked)" ; + fi + fi + + test -x $TEST_DIR/regress || return + announce_n " regress: " + if test "$TEST_OUTPUT_FILE" = "/dev/null" ; + then + $TEST_DIR/regress --quiet $REGRESS_ARGS + else + $TEST_DIR/regress $REGRESS_ARGS >>"$TEST_OUTPUT_FILE" + fi + if test "$?" = "0" ; + then + announce OKAY ; + else + announce FAILED ; + FAILED=yes + fi + + announce_n " regress_debug: " + if test "$TEST_OUTPUT_FILE" = "/dev/null" ; + then + EVENT_DEBUG_MODE=1 $TEST_DIR/regress --quiet $REGRESS_ARGS + else + EVENT_DEBUG_MODE=1 $TEST_DIR/regress $REGRESS_ARGS >>"$TEST_OUTPUT_FILE" + fi + if test "$?" = "0" ; + then + announce OKAY ; + else + announce FAILED ; + FAILED=yes + fi +} + +do_test() { + setup + announce "$1 $2" + unset EVENT_NO$1 + if test "$2" = "(changelist)" ; then + EVENT_EPOLL_USE_CHANGELIST=yes; export EVENT_EPOLL_USE_CHANGELIST + elif test "$2" = "(timerfd)" ; then + EVENT_PRECISE_TIMER=1; export EVENT_PRECISE_TIMER + elif test "$2" = "(timerfd+changelist)" ; then + EVENT_EPOLL_USE_CHANGELIST=yes; export EVENT_EPOLL_USE_CHANGELIST + EVENT_PRECISE_TIMER=1; export EVENT_PRECISE_TIMER + fi + + run_tests +} + +announce "Running tests:" + +do_test EPOLL "(timerfd)" +do_test EPOLL "(changelist)" +do_test EPOLL "(timerfd+changelist)" +for i in $BACKENDS; do + do_test $i +done + +if test "$FAILED" = "yes"; then + exit 1 +fi diff --git a/libs/libevent/docs/test/tinytest.c b/libs/libevent/docs/test/tinytest.c new file mode 100644 index 0000000000..3a8e331055 --- /dev/null +++ b/libs/libevent/docs/test/tinytest.c @@ -0,0 +1,493 @@ +/* tinytest.c -- Copyright 2009-2012 Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ +#ifdef TINYTEST_LOCAL +#include "tinytest_local.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#ifndef NO_FORKING + +#ifdef _WIN32 +#include <windows.h> +#else +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#endif + +#if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) +#if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070) +/* Workaround for a stupid bug in OSX 10.6 */ +#define FORK_BREAKS_GCOV +#include <vproc.h> +#endif +#endif + +#endif /* !NO_FORKING */ + +#ifndef __GNUC__ +#define __attribute__(x) +#endif + +#include "tinytest.h" +#include "tinytest_macros.h" + +#define LONGEST_TEST_NAME 16384 + +static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/ +static int n_ok = 0; /**< Number of tests that have passed */ +static int n_bad = 0; /**< Number of tests that have failed. */ +static int n_skipped = 0; /**< Number of tests that have been skipped. */ + +static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/ +static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */ +static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */ +const char *verbosity_flag = ""; + +const struct testlist_alias_t *cfg_aliases=NULL; + +enum outcome { SKIP=2, OK=1, FAIL=0 }; +static enum outcome cur_test_outcome = 0; +const char *cur_test_prefix = NULL; /**< prefix of the current test group */ +/** Name of the current test, if we haven't logged is yet. Used for --quiet */ +const char *cur_test_name = NULL; + +#ifdef _WIN32 +/* Copy of argv[0] for win32. */ +static char commandname[MAX_PATH+1]; +#endif + +static void usage(struct testgroup_t *groups, int list_groups) + __attribute__((noreturn)); +static int process_test_option(struct testgroup_t *groups, const char *test); + +static enum outcome +testcase_run_bare_(const struct testcase_t *testcase) +{ + void *env = NULL; + int outcome; + if (testcase->setup) { + env = testcase->setup->setup_fn(testcase); + if (!env) + return FAIL; + else if (env == (void*)TT_SKIP) + return SKIP; + } + + cur_test_outcome = OK; + testcase->fn(env); + outcome = cur_test_outcome; + + if (testcase->setup) { + if (testcase->setup->cleanup_fn(testcase, env) == 0) + outcome = FAIL; + } + + return outcome; +} + +#define MAGIC_EXITCODE 42 + +#ifndef NO_FORKING + +static enum outcome +testcase_run_forked_(const struct testgroup_t *group, + const struct testcase_t *testcase) +{ +#ifdef _WIN32 + /* Fork? On Win32? How primitive! We'll do what the smart kids do: + we'll invoke our own exe (whose name we recall from the command + line) with a command line that tells it to run just the test we + want, and this time without forking. + + (No, threads aren't an option. The whole point of forking is to + share no state between tests.) + */ + int ok; + char buffer[LONGEST_TEST_NAME+256]; + STARTUPINFOA si; + PROCESS_INFORMATION info; + DWORD exitcode; + + if (!in_tinytest_main) { + printf("\nERROR. On Windows, testcase_run_forked_ must be" + " called from within tinytest_main.\n"); + abort(); + } + if (opt_verbosity>0) + printf("[forking] "); + + snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s", + commandname, verbosity_flag, group->prefix, testcase->name); + + memset(&si, 0, sizeof(si)); + memset(&info, 0, sizeof(info)); + si.cb = sizeof(si); + + ok = CreateProcessA(commandname, buffer, NULL, NULL, 0, + 0, NULL, NULL, &si, &info); + if (!ok) { + printf("CreateProcess failed!\n"); + return 0; + } + WaitForSingleObject(info.hProcess, INFINITE); + GetExitCodeProcess(info.hProcess, &exitcode); + CloseHandle(info.hProcess); + CloseHandle(info.hThread); + if (exitcode == 0) + return OK; + else if (exitcode == MAGIC_EXITCODE) + return SKIP; + else + return FAIL; +#else + int outcome_pipe[2]; + pid_t pid; + (void)group; + + if (pipe(outcome_pipe)) + perror("opening pipe"); + + if (opt_verbosity>0) + printf("[forking] "); + pid = fork(); +#ifdef FORK_BREAKS_GCOV + vproc_transaction_begin(0); +#endif + if (!pid) { + /* child. */ + int test_r, write_r; + char b[1]; + close(outcome_pipe[0]); + test_r = testcase_run_bare_(testcase); + assert(0<=(int)test_r && (int)test_r<=2); + b[0] = "NYS"[test_r]; + write_r = (int)write(outcome_pipe[1], b, 1); + if (write_r != 1) { + perror("write outcome to pipe"); + exit(1); + } + exit(0); + return FAIL; /* unreachable */ + } else { + /* parent */ + int status, r; + char b[1]; + /* Close this now, so that if the other side closes it, + * our read fails. */ + close(outcome_pipe[1]); + r = (int)read(outcome_pipe[0], b, 1); + if (r == 0) { + printf("[Lost connection!] "); + return 0; + } else if (r != 1) { + perror("read outcome from pipe"); + } + waitpid(pid, &status, 0); + close(outcome_pipe[0]); + return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL); + } +#endif +} + +#endif /* !NO_FORKING */ + +int +testcase_run_one(const struct testgroup_t *group, + const struct testcase_t *testcase) +{ + enum outcome outcome; + + if (testcase->flags & (TT_SKIP|TT_OFF_BY_DEFAULT)) { + if (opt_verbosity>0) + printf("%s%s: %s\n", + group->prefix, testcase->name, + (testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED"); + ++n_skipped; + return SKIP; + } + + if (opt_verbosity>0 && !opt_forked) { + printf("%s%s: ", group->prefix, testcase->name); + } else { + if (opt_verbosity==0) printf("."); + cur_test_prefix = group->prefix; + cur_test_name = testcase->name; + } + +#ifndef NO_FORKING + if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) { + outcome = testcase_run_forked_(group, testcase); + } else { +#else + { +#endif + outcome = testcase_run_bare_(testcase); + } + + if (outcome == OK) { + ++n_ok; + if (opt_verbosity>0 && !opt_forked) + puts(opt_verbosity==1?"OK":""); + } else if (outcome == SKIP) { + ++n_skipped; + if (opt_verbosity>0 && !opt_forked) + puts("SKIPPED"); + } else { + ++n_bad; + if (!opt_forked) + printf("\n [%s FAILED]\n", testcase->name); + } + + if (opt_forked) { + exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1)); + return 1; /* unreachable */ + } else { + return (int)outcome; + } +} + +int +tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, unsigned long flag) +{ + int i, j; + size_t length = LONGEST_TEST_NAME; + char fullname[LONGEST_TEST_NAME]; + int found=0; + if (strstr(arg, "..")) + length = strstr(arg,"..")-arg; + for (i=0; groups[i].prefix; ++i) { + for (j=0; groups[i].cases[j].name; ++j) { + struct testcase_t *testcase = &groups[i].cases[j]; + snprintf(fullname, sizeof(fullname), "%s%s", + groups[i].prefix, testcase->name); + if (!flag) { /* Hack! */ + printf(" %s", fullname); + if (testcase->flags & TT_OFF_BY_DEFAULT) + puts(" (Off by default)"); + else if (testcase->flags & TT_SKIP) + puts(" (DISABLED)"); + else + puts(""); + } + if (!strncmp(fullname, arg, length)) { + if (set) + testcase->flags |= flag; + else + testcase->flags &= ~flag; + ++found; + } + } + } + return found; +} + +static void +usage(struct testgroup_t *groups, int list_groups) +{ + puts("Options are: [--verbose|--quiet|--terse] [--no-fork]"); + puts(" Specify tests by name, or using a prefix ending with '..'"); + puts(" To skip a test, prefix its name with a colon."); + puts(" To enable a disabled test, prefix its name with a plus."); + puts(" Use --list-tests for a list of tests."); + if (list_groups) { + puts("Known tests are:"); + tinytest_set_flag_(groups, "..", 1, 0); + } + exit(0); +} + +static int +process_test_alias(struct testgroup_t *groups, const char *test) +{ + int i, j, n, r; + for (i=0; cfg_aliases && cfg_aliases[i].name; ++i) { + if (!strcmp(cfg_aliases[i].name, test)) { + n = 0; + for (j = 0; cfg_aliases[i].tests[j]; ++j) { + r = process_test_option(groups, cfg_aliases[i].tests[j]); + if (r<0) + return -1; + n += r; + } + return n; + } + } + printf("No such test alias as @%s!",test); + return -1; +} + +static int +process_test_option(struct testgroup_t *groups, const char *test) +{ + int flag = TT_ENABLED_; + int n = 0; + if (test[0] == '@') { + return process_test_alias(groups, test + 1); + } else if (test[0] == ':') { + ++test; + flag = TT_SKIP; + } else if (test[0] == '+') { + ++test; + ++n; + if (!tinytest_set_flag_(groups, test, 0, TT_OFF_BY_DEFAULT)) { + printf("No such test as %s!\n", test); + return -1; + } + } else { + ++n; + } + if (!tinytest_set_flag_(groups, test, 1, flag)) { + printf("No such test as %s!\n", test); + return -1; + } + return n; +} + +void +tinytest_set_aliases(const struct testlist_alias_t *aliases) +{ + cfg_aliases = aliases; +} + +int +tinytest_main(int c, const char **v, struct testgroup_t *groups) +{ + int i, j, n=0; + +#ifdef _WIN32 + const char *sp = strrchr(v[0], '.'); + const char *extension = ""; + if (!sp || stricmp(sp, ".exe")) + extension = ".exe"; /* Add an exe so CreateProcess will work */ + snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension); + commandname[MAX_PATH]='\0'; +#endif + for (i=1; i<c; ++i) { + if (v[i][0] == '-') { + if (!strcmp(v[i], "--RUNNING-FORKED")) { + opt_forked = 1; + } else if (!strcmp(v[i], "--no-fork")) { + opt_nofork = 1; + } else if (!strcmp(v[i], "--quiet")) { + opt_verbosity = -1; + verbosity_flag = "--quiet"; + } else if (!strcmp(v[i], "--verbose")) { + opt_verbosity = 2; + verbosity_flag = "--verbose"; + } else if (!strcmp(v[i], "--terse")) { + opt_verbosity = 0; + verbosity_flag = "--terse"; + } else if (!strcmp(v[i], "--help")) { + usage(groups, 0); + } else if (!strcmp(v[i], "--list-tests")) { + usage(groups, 1); + } else { + printf("Unknown option %s. Try --help\n",v[i]); + return -1; + } + } else { + int r = process_test_option(groups, v[i]); + if (r<0) + return -1; + n += r; + } + } + if (!n) + tinytest_set_flag_(groups, "..", 1, TT_ENABLED_); + +#ifdef _IONBF + setvbuf(stdout, NULL, _IONBF, 0); +#endif + + ++in_tinytest_main; + for (i=0; groups[i].prefix; ++i) + for (j=0; groups[i].cases[j].name; ++j) + if (groups[i].cases[j].flags & TT_ENABLED_) + testcase_run_one(&groups[i], + &groups[i].cases[j]); + + --in_tinytest_main; + + if (opt_verbosity==0) + puts(""); + + if (n_bad) + printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad, + n_bad+n_ok,n_skipped); + else if (opt_verbosity >= 1) + printf("%d tests ok. (%d skipped)\n", n_ok, n_skipped); + + return (n_bad == 0) ? 0 : 1; +} + +int +tinytest_get_verbosity_(void) +{ + return opt_verbosity; +} + +void +tinytest_set_test_failed_(void) +{ + if (opt_verbosity <= 0 && cur_test_name) { + if (opt_verbosity==0) puts(""); + printf("%s%s: ", cur_test_prefix, cur_test_name); + cur_test_name = NULL; + } + cur_test_outcome = 0; +} + +void +tinytest_set_test_skipped_(void) +{ + if (cur_test_outcome==OK) + cur_test_outcome = SKIP; +} + +char * +tinytest_format_hex_(const void *val_, unsigned long len) +{ + const unsigned char *val = val_; + char *result, *cp; + size_t i; + + if (!val) + return strdup("null"); + if (!(result = malloc(len*2+1))) + return strdup("<allocation failure>"); + cp = result; + for (i=0;i<len;++i) { + *cp++ = "0123456789ABCDEF"[val[i] >> 4]; + *cp++ = "0123456789ABCDEF"[val[i] & 0x0f]; + } + *cp = 0; + return result; +} diff --git a/libs/libevent/docs/test/tinytest.h b/libs/libevent/docs/test/tinytest.h new file mode 100644 index 0000000000..ed07b26bc0 --- /dev/null +++ b/libs/libevent/docs/test/tinytest.h @@ -0,0 +1,100 @@ +/* tinytest.h -- Copyright 2009-2012 Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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 TINYTEST_H_INCLUDED_ +#define TINYTEST_H_INCLUDED_ + +/** Flag for a test that needs to run in a subprocess. */ +#define TT_FORK (1<<0) +/** Runtime flag for a test we've decided to skip. */ +#define TT_SKIP (1<<1) +/** Internal runtime flag for a test we've decided to run. */ +#define TT_ENABLED_ (1<<2) +/** Flag for a test that's off by default. */ +#define TT_OFF_BY_DEFAULT (1<<3) +/** If you add your own flags, make them start at this point. */ +#define TT_FIRST_USER_FLAG (1<<4) + +typedef void (*testcase_fn)(void *); + +struct testcase_t; + +/** Functions to initialize/teardown a structure for a testcase. */ +struct testcase_setup_t { + /** Return a new structure for use by a given testcase. */ + void *(*setup_fn)(const struct testcase_t *); + /** Clean/free a structure from setup_fn. Return 1 if ok, 0 on err. */ + int (*cleanup_fn)(const struct testcase_t *, void *); +}; + +/** A single test-case that you can run. */ +struct testcase_t { + const char *name; /**< An identifier for this case. */ + testcase_fn fn; /**< The function to run to implement this case. */ + unsigned long flags; /**< Bitfield of TT_* flags. */ + const struct testcase_setup_t *setup; /**< Optional setup/cleanup fns*/ + void *setup_data; /**< Extra data usable by setup function */ +}; +#define END_OF_TESTCASES { NULL, NULL, 0, NULL, NULL } + +/** A group of tests that are selectable together. */ +struct testgroup_t { + const char *prefix; /**< Prefix to prepend to testnames. */ + struct testcase_t *cases; /** Array, ending with END_OF_TESTCASES */ +}; +#define END_OF_GROUPS { NULL, NULL} + +struct testlist_alias_t { + const char *name; + const char **tests; +}; +#define END_OF_ALIASES { NULL, NULL } + +/** Implementation: called from a test to indicate failure, before logging. */ +void tinytest_set_test_failed_(void); +/** Implementation: called from a test to indicate that we're skipping. */ +void tinytest_set_test_skipped_(void); +/** Implementation: return 0 for quiet, 1 for normal, 2 for loud. */ +int tinytest_get_verbosity_(void); +/** Implementation: Set a flag on tests matching a name; returns number + * of tests that matched. */ +int tinytest_set_flag_(struct testgroup_t *, const char *, int set, unsigned long); +/** Implementation: Put a chunk of memory into hex. */ +char *tinytest_format_hex_(const void *, unsigned long); + +/** Set all tests in 'groups' matching the name 'named' to be skipped. */ +#define tinytest_skip(groups, named) \ + tinytest_set_flag_(groups, named, 1, TT_SKIP) + +/** Run a single testcase in a single group. */ +int testcase_run_one(const struct testgroup_t *,const struct testcase_t *); + +void tinytest_set_aliases(const struct testlist_alias_t *aliases); + +/** Run a set of testcases from an END_OF_GROUPS-terminated array of groups, + as selected from the command line. */ +int tinytest_main(int argc, const char **argv, struct testgroup_t *groups); + +#endif diff --git a/libs/libevent/docs/test/tinytest_demo.c b/libs/libevent/docs/test/tinytest_demo.c new file mode 100644 index 0000000000..f6bfd66a1a --- /dev/null +++ b/libs/libevent/docs/test/tinytest_demo.c @@ -0,0 +1,262 @@ +/* tinytest_demo.c -- Copyright 2009-2012 Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ + + +/* Welcome to the example file for tinytest! I'll show you how to set up + * some simple and not-so-simple testcases. */ + +/* Make sure you include these headers. */ +#include "tinytest.h" +#include "tinytest_macros.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#ifdef _WIN32 +#include <windows.h> +#else +#include <unistd.h> +#endif + +/* ============================================================ */ + +/* First, let's see if strcmp is working. (All your test cases should be + * functions declared to take a single void * as an argument.) */ +void +test_strcmp(void *data) +{ + (void)data; /* This testcase takes no data. */ + + /* Let's make sure the empty string is equal to itself */ + if (strcmp("","")) { + /* This macro tells tinytest to stop the current test + * and go straight to the "end" label. */ + tt_abort_msg("The empty string was not equal to itself"); + } + + /* Pretty often, calling tt_abort_msg to indicate failure is more + heavy-weight than you want. Instead, just say: */ + tt_assert(strcmp("testcase", "testcase") == 0); + + /* Occasionally, you don't want to stop the current testcase just + because a single assertion has failed. In that case, use + tt_want: */ + tt_want(strcmp("tinytest", "testcase") > 0); + + /* You can use the tt_*_op family of macros to compare values and to + fail unless they have the relationship you want. They produce + more useful output than tt_assert, since they display the actual + values of the failing things. + + Fail unless strcmp("abc, "abc") == 0 */ + tt_int_op(strcmp("abc", "abc"), ==, 0); + + /* Fail unless strcmp("abc, "abcd") is less than 0 */ + tt_int_op(strcmp("abc", "abcd"), < , 0); + + /* Incidentally, there's a test_str_op that uses strcmp internally. */ + tt_str_op("abc", <, "abcd"); + + + /* Every test-case function needs to finish with an "end:" + label and (optionally) code to clean up local variables. */ + end: + ; +} + +/* ============================================================ */ + +/* Now let's mess with setup and teardown functions! These are handy if + you have a bunch of tests that all need a similar environment, and you + want to reconstruct that environment freshly for each one. */ + +/* First you declare a type to hold the environment info, and functions to + set it up and tear it down. */ +struct data_buffer { + /* We're just going to have couple of character buffer. Using + setup/teardown functions is probably overkill for this case. + + You could also do file descriptors, complicated handles, temporary + files, etc. */ + char buffer1[512]; + char buffer2[512]; +}; +/* The setup function needs to take a const struct testcase_t and return + void* */ +void * +setup_data_buffer(const struct testcase_t *testcase) +{ + struct data_buffer *db = malloc(sizeof(struct data_buffer)); + + /* If you had a complicated set of setup rules, you might behave + differently here depending on testcase->flags or + testcase->setup_data or even or testcase->name. */ + + /* Returning a NULL here would mean that we couldn't set up for this + test, so we don't need to test db for null. */ + return db; +} +/* The clean function deallocates storage carefully and returns true on + success. */ +int +clean_data_buffer(const struct testcase_t *testcase, void *ptr) +{ + struct data_buffer *db = ptr; + + if (db) { + free(db); + return 1; + } + return 0; +} +/* Finally, declare a testcase_setup_t with these functions. */ +struct testcase_setup_t data_buffer_setup = { + setup_data_buffer, clean_data_buffer +}; + + +/* Now let's write our test. */ +void +test_memcpy(void *ptr) +{ + /* This time, we use the argument. */ + struct data_buffer *db = ptr; + + /* We'll also introduce a local variable that might need cleaning up. */ + char *mem = NULL; + + /* Let's make sure that memcpy does what we'd like. */ + strcpy(db->buffer1, "String 0"); + memcpy(db->buffer2, db->buffer1, sizeof(db->buffer1)); + tt_str_op(db->buffer1, ==, db->buffer2); + + /* This one works if there's an internal NUL. */ + tt_mem_op(db->buffer1, <, db->buffer2, sizeof(db->buffer1)); + + /* Now we've allocated memory that's referenced by a local variable. + The end block of the function will clean it up. */ + mem = strdup("Hello world."); + tt_assert(mem); + + /* Another rather trivial test. */ + tt_str_op(db->buffer1, !=, mem); + + end: + /* This time our end block has something to do. */ + if (mem) + free(mem); +} + +void +test_timeout(void *ptr) +{ + time_t t1, t2; + (void)ptr; + t1 = time(NULL); +#ifdef _WIN32 + Sleep(5000); +#else + sleep(5); +#endif + t2 = time(NULL); + + tt_int_op(t2-t1, >=, 4); + + tt_int_op(t2-t1, <=, 6); + + end: + ; +} + +/* ============================================================ */ + +/* Now we need to make sure that our tests get invoked. First, you take + a bunch of related tests and put them into an array of struct testcase_t. +*/ + +struct testcase_t demo_tests[] = { + /* Here's a really simple test: it has a name you can refer to it + with, and a function to invoke it. */ + { "strcmp", test_strcmp, }, + + /* The second test has a flag, "TT_FORK", to make it run in a + subprocess, and a pointer to the testcase_setup_t that configures + its environment. */ + { "memcpy", test_memcpy, TT_FORK, &data_buffer_setup }, + + /* This flag is off-by-default, since it takes a while to run. You + * can enable it manually by passing +demo/timeout at the command line.*/ + { "timeout", test_timeout, TT_OFF_BY_DEFAULT }, + + /* The array has to end with END_OF_TESTCASES. */ + END_OF_TESTCASES +}; + +/* Next, we make an array of testgroups. This is mandatory. Unlike more + heavy-duty testing frameworks, groups can't nest. */ +struct testgroup_t groups[] = { + + /* Every group has a 'prefix', and an array of tests. That's it. */ + { "demo/", demo_tests }, + + END_OF_GROUPS +}; + +/* We can also define test aliases. These can be used for types of tests that + * cut across groups. */ +const char *alltests[] = { "+..", NULL }; +const char *slowtests[] = { "+demo/timeout", NULL }; +struct testlist_alias_t aliases[] = { + + { "ALL", alltests }, + { "SLOW", slowtests }, + + END_OF_ALIASES +}; + + +int +main(int c, const char **v) +{ + /* Finally, just call tinytest_main(). It lets you specify verbose + or quiet output with --verbose and --quiet. You can list + specific tests: + + tinytest-demo demo/memcpy + + or use a ..-wildcard to select multiple tests with a common + prefix: + + tinytest-demo demo/.. + + If you list no tests, you get them all by default, so that + "tinytest-demo" and "tinytest-demo .." mean the same thing. + + */ + tinytest_set_aliases(aliases); + return tinytest_main(c, v, groups); +} diff --git a/libs/libevent/docs/test/tinytest_local.h b/libs/libevent/docs/test/tinytest_local.h new file mode 100644 index 0000000000..87ec2fa67e --- /dev/null +++ b/libs/libevent/docs/test/tinytest_local.h @@ -0,0 +1,12 @@ + +#include "util-internal.h" +#ifdef _WIN32 +#include <winsock2.h> +#endif + +#include "event2/util.h" + +#ifdef snprintf +#undef snprintf +#endif +#define snprintf evutil_snprintf diff --git a/libs/libevent/docs/test/tinytest_macros.h b/libs/libevent/docs/test/tinytest_macros.h new file mode 100644 index 0000000000..c3728d1fdd --- /dev/null +++ b/libs/libevent/docs/test/tinytest_macros.h @@ -0,0 +1,199 @@ +/* tinytest_macros.h -- Copyright 2009-2012 Nick Mathewson + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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 TINYTEST_MACROS_H_INCLUDED_ +#define TINYTEST_MACROS_H_INCLUDED_ + +/* Helpers for defining statement-like macros */ +#define TT_STMT_BEGIN do { +#define TT_STMT_END } while (0) + +/* Redefine this if your test functions want to abort with something besides + * "goto end;" */ +#ifndef TT_EXIT_TEST_FUNCTION +#define TT_EXIT_TEST_FUNCTION TT_STMT_BEGIN goto end; TT_STMT_END +#endif + +/* Redefine this if you want to note success/failure in some different way. */ +#ifndef TT_DECLARE +#define TT_DECLARE(prefix, args) \ + TT_STMT_BEGIN \ + printf("\n %s %s:%d: ",prefix,__FILE__,__LINE__); \ + printf args ; \ + TT_STMT_END +#endif + +/* Announce a failure. Args are parenthesized printf args. */ +#define TT_GRIPE(args) TT_DECLARE("FAIL", args) + +/* Announce a non-failure if we're verbose. */ +#define TT_BLATHER(args) \ + TT_STMT_BEGIN \ + if (tinytest_get_verbosity_()>1) TT_DECLARE(" OK", args); \ + TT_STMT_END + +#define TT_DIE(args) \ + TT_STMT_BEGIN \ + tinytest_set_test_failed_(); \ + TT_GRIPE(args); \ + TT_EXIT_TEST_FUNCTION; \ + TT_STMT_END + +#define TT_FAIL(args) \ + TT_STMT_BEGIN \ + tinytest_set_test_failed_(); \ + TT_GRIPE(args); \ + TT_STMT_END + +/* Fail and abort the current test for the reason in msg */ +#define tt_abort_printf(msg) TT_DIE(msg) +#define tt_abort_perror(op) TT_DIE(("%s: %s [%d]",(op),strerror(errno), errno)) +#define tt_abort_msg(msg) TT_DIE(("%s", msg)) +#define tt_abort() TT_DIE(("%s", "(Failed.)")) + +/* Fail but do not abort the current test for the reason in msg. */ +#define tt_failprint_f(msg) TT_FAIL(msg) +#define tt_fail_perror(op) TT_FAIL(("%s: %s [%d]",(op),strerror(errno), errno)) +#define tt_fail_msg(msg) TT_FAIL(("%s", msg)) +#define tt_fail() TT_FAIL(("%s", "(Failed.)")) + +/* End the current test, and indicate we are skipping it. */ +#define tt_skip() \ + TT_STMT_BEGIN \ + tinytest_set_test_skipped_(); \ + TT_EXIT_TEST_FUNCTION; \ + TT_STMT_END + +#define tt_want_(b, msg, fail) \ + TT_STMT_BEGIN \ + if (!(b)) { \ + tinytest_set_test_failed_(); \ + TT_GRIPE(("%s",msg)); \ + fail; \ + } else { \ + TT_BLATHER(("%s",msg)); \ + } \ + TT_STMT_END + +/* Assert b, but do not stop the test if b fails. Log msg on failure. */ +#define tt_want_msg(b, msg) \ + tt_want_(b, msg, ); + +/* Assert b and stop the test if b fails. Log msg on failure. */ +#define tt_assert_msg(b, msg) \ + tt_want_(b, msg, TT_EXIT_TEST_FUNCTION); + +/* Assert b, but do not stop the test if b fails. */ +#define tt_want(b) tt_want_msg( (b), "want("#b")") +/* Assert b, and stop the test if b fails. */ +#define tt_assert(b) tt_assert_msg((b), "assert("#b")") + +#define tt_assert_test_fmt_type(a,b,str_test,type,test,printf_type,printf_fmt, \ + setup_block,cleanup_block,die_on_fail) \ + TT_STMT_BEGIN \ + type val1_ = (a); \ + type val2_ = (b); \ + int tt_status_ = (test); \ + if (!tt_status_ || tinytest_get_verbosity_()>1) { \ + printf_type print_; \ + printf_type print1_; \ + printf_type print2_; \ + type value_ = val1_; \ + setup_block; \ + print1_ = print_; \ + value_ = val2_; \ + setup_block; \ + print2_ = print_; \ + TT_DECLARE(tt_status_?" OK":"FAIL", \ + ("assert(%s): "printf_fmt" vs "printf_fmt, \ + str_test, print1_, print2_)); \ + print_ = print1_; \ + cleanup_block; \ + print_ = print2_; \ + cleanup_block; \ + if (!tt_status_) { \ + tinytest_set_test_failed_(); \ + die_on_fail ; \ + } \ + } \ + TT_STMT_END + +#define tt_assert_test_type(a,b,str_test,type,test,fmt,die_on_fail) \ + tt_assert_test_fmt_type(a,b,str_test,type,test,type,fmt, \ + {print_=value_;},{},die_on_fail) + +#define tt_assert_test_type_opt(a,b,str_test,type,test,fmt,die_on_fail) \ + tt_assert_test_fmt_type(a,b,str_test,type,test,type,fmt, \ + {print_=value_?value_:"<NULL>";},{},die_on_fail) + +/* Helper: assert that a op b, when cast to type. Format the values with + * printf format fmt on failure. */ +#define tt_assert_op_type(a,op,b,type,fmt) \ + tt_assert_test_type(a,b,#a" "#op" "#b,type,(val1_ op val2_),fmt, \ + TT_EXIT_TEST_FUNCTION) + +#define tt_int_op(a,op,b) \ + tt_assert_test_type(a,b,#a" "#op" "#b,long,(val1_ op val2_), \ + "%ld",TT_EXIT_TEST_FUNCTION) + +#define tt_uint_op(a,op,b) \ + tt_assert_test_type(a,b,#a" "#op" "#b,unsigned long, \ + (val1_ op val2_),"%lu",TT_EXIT_TEST_FUNCTION) + +#define tt_ptr_op(a,op,b) \ + tt_assert_test_type(a,b,#a" "#op" "#b,const void*, \ + (val1_ op val2_),"%p",TT_EXIT_TEST_FUNCTION) + +#define tt_str_op(a,op,b) \ + tt_assert_test_type_opt(a,b,#a" "#op" "#b,const char *, \ + (val1_ && val2_ && strcmp(val1_,val2_) op 0),"<%s>", \ + TT_EXIT_TEST_FUNCTION) + +#define tt_mem_op(expr1, op, expr2, len) \ + tt_assert_test_fmt_type(expr1,expr2,#expr1" "#op" "#expr2, \ + const void *, \ + (val1_ && val2_ && memcmp(val1_, val2_, len) op 0), \ + char *, "%s", \ + { print_ = tinytest_format_hex_(value_, (len)); }, \ + { if (print_) free(print_); }, \ + TT_EXIT_TEST_FUNCTION \ + ); + +#define tt_want_int_op(a,op,b) \ + tt_assert_test_type(a,b,#a" "#op" "#b,long,(val1_ op val2_),"%ld",(void)0) + +#define tt_want_uint_op(a,op,b) \ + tt_assert_test_type(a,b,#a" "#op" "#b,unsigned long, \ + (val1_ op val2_),"%lu",(void)0) + +#define tt_want_ptr_op(a,op,b) \ + tt_assert_test_type(a,b,#a" "#op" "#b,const void*, \ + (val1_ op val2_),"%p",(void)0) + +#define tt_want_str_op(a,op,b) \ + tt_assert_test_type(a,b,#a" "#op" "#b,const char *, \ + (strcmp(val1_,val2_) op 0),"<%s>",(void)0) + +#endif diff --git a/libs/libevent/docs/whatsnew-2.0.txt b/libs/libevent/docs/whatsnew-2.0.txt new file mode 100644 index 0000000000..3561fcb94c --- /dev/null +++ b/libs/libevent/docs/whatsnew-2.0.txt @@ -0,0 +1,609 @@ +What's New In Libevent 2.0 so far: + +1. Meta-issues + +1.1. About this document + + This document describes the key differences between Libevent 1.4 and + Libevent 2.0, from a user's point of view. It was most recently + updated based on features in git master as of August 2010. + + NOTE: I am very sure that I missed some thing on this list. Caveat + haxxor. + +1.2. Better documentation + + There is now a book-in-progress that explains how to use Libevent and its + growing pile of APIs. As of this writing, it covers everything except the + http and rpc code. Check out the latest draft at + http://www.wangafu.net/~nickm/libevent-book/ . + +2. New and Improved Event APIs + + Many APIs are improved, refactored, or deprecated in Libevent 2.0. + + COMPATIBILITY: + + Nearly all existing code that worked with Libevent 1.4 should still + work correctly with Libevent 2.0. However, if you are writing new code, + or if you want to port old code, we strongly recommend using the new APIs + and avoiding deprecated APIs as much as possible. + + Binaries linked against Libevent 1.4 will need to be recompiled to link + against Libevent 2.0. This is nothing new; we have never been good at + preserving binary compatibility between releases. We'll try harder in the + future, though: see 2.1 below. + +2.1. New header layout for improved forward-compatibility + + Libevent 2.0 has a new header layout to make it easier for programmers to + write good, well-supported libevent code. The new headers are divided + into three types. + + There are *regular headers*, like event2/event.h. These headers contain + the functions that most programmers will want to use. + + There are *backward compatibility headers*, like event2/event_compat.h. + These headers contain declarations for deprecated functions from older + versions of Libevent. Documentation in these headers should suggest what's + wrong with the old functions, and what functions you want to start using + instead of the old ones. Some of these functions might be removed in a + future release. New programs should generally not include these headers. + + Finally, there are *structure headers*, like event2/event_struct.h. + These headers contain definitions of some structures that Libevent has + historically exposed. Exposing them caused problems in the past, + since programs that were compiled to work with one version of Libevent + would often stop working with another version that changed the size or + layout of some object. We've moving them into separate headers so + that programmers can know that their code is not depending on any + unstable aspect of the Libvent ABI. New programs should generally not + include these headers unless they really know what they are doing, are + willing to rebuild their software whenever they want to link it + against a new version of Libevent, and are willing to risk their code + breaking if and when data structures change. + + Functionality that once was located in event.h is now more subdivided. + The core event logic is now in event2/event.h. The "evbuffer" functions + for low-level buffer manipulation are in event2/buffer.h. The + "bufferevent" functions for higher-level buffered IO are in + event2/bufferevent.h. + + COMPATIBILITY: + + All of the old headers (event.h, evdns.h, evhttp.h, evrpc.h, and + evutil.h) will continue to work by including the corresponding new + headers. Old code should not be broken by this change. + +2.2. New thread-safe, binary-compatible, harder-to-mess-up APIs + + Some aspects of the historical Libevent API have encouraged + non-threadsafe code, or forced code built against one version of Libevent + to no longer build with another. The problems with now-deprecated APIs + fell into two categories: + + 1) Dependence on the "current" event_base. In an application with + multiple event_bases, Libevent previously had a notion of the + "current" event_base. New events were linked to this base, and + the caller needed to explicitly reattach them to another base. + This was horribly error-prone. + + Functions like "event_set" that worked with the "current" event_base + are now deprecated but still available (see 2.1). There are new + functions like "event_assign" that take an explicit event_base + argument when setting up a structure. Using these functions will help + prevent errors in your applications, and to be more threadsafe. + + 2) Structure dependence. Applications needed to allocate 'struct + event' themselves, since there was no function in Libevent to do it + for them. But since the size and contents of struct event can + change between libevent versions, this created binary-compatibility + nightmares. All structures of this kind are now isolated in + _struct.h header (see 2.1), and there are new allocate-and- + initialize functions you can use instead of the old initialize-only + functions. For example, instead of malloc and event_set, you + can use event_new(). + + (For people who do really want to allocate a struct event on the + stack, or put one inside another structure, you can still use + event2/event_compat.h.) + + So in the case where old code would look like this: + + #include <event.h> + ... + struct event *ev = malloc(sizeof(struct event)); + /* This call will cause a buffer overrun if you compile with one version + of Libevent and link dynamically against another. */ + event_set(ev, fd, EV_READ, cb, NULL); + /* If you forget this call, your code will break in hard-to-diagnose + ways in the presence of multiple event bases. */ + event_set_base(ev, base); + + New code will look more like this: + + #include <event2/event.h> + ... + struct event *ev; + ev = event_new(base, fd, EV_READ, cb, NULL); + +2.3. Overrideable allocation functions + + If you want to override the allocation functions used by libevent + (for example, to use a specialized allocator, or debug memory + issues, or so on), you can replace them by calling + event_set_mem_functions. It takes replacements for malloc(), + free(), and realloc(). + + If you're going to use this facility, you need to call it _before_ + Libevent does any memory allocation; otherwise, Libevent may allocate some + memory with malloc(), and free it with the free() function you provide. + + You can disable this feature when you are building Libevent by passing + the --disable-malloc-replacement argument to configure. + +2.4. Configurable event_base creation + + Older versions of Libevent would always got the fastest backend + available, unless you reconfigured their behavior with the environment + variables EVENT_NOSELECT, EVENT_NOPOLL, and so forth. This was annoying + to programmers who wanted to pick a backend explicitly without messing + with the environment. + + Also, despite our best efforts, not every backend supports every + operation we might like. Some features (like edge-triggered events, or + working with non-socket file descriptors) only work with some operating + systems' fast backends. Previously, programmers who cared about this + needed to know which backends supported what. This tended to get quite + ungainly. + + There is now an API to choose backends, either by name or by feature. + Here is an example: + + struct event_config_t *config; + struct event_base *base; + + /* Create a new configuration object. */ + config = event_config_new(); + /* We don't want to use the "select" method. */ + event_config_avoid_method(config, "select"); + /* We want a method that can work with non-socket file descriptors */ + event_config_require_features(config, EV_FEATURE_FDS); + + base = event_base_new_with_config(config); + if (!base) { + /* There is no backend method that does what we want. */ + exit(1); + } + event_config_free(config); + + Supported features are documented in event2/event.h + +2.5. Socket is now an abstract type + + All APIs that formerly accepted int as a socket type now accept + "evutil_socket_t". On Unix, this is just an alias for "int" as + before. On Windows, however, it's an alias for SOCKET, which can + be wider than int on 64-bit platforms. + +2.6. Timeouts and persistent events work together. + + Previously, it wasn't useful to set a timeout on a persistent event: + the timeout would trigger once, and never again. This is not what + applications tend to want. Instead, applications tend to want every + triggering of the event to re-set the timeout. So now, if you set + up an event like this: + struct event *ev; + struct timeval tv; + ev = event_new(base, fd, EV_READ|EV_PERSIST, cb, NULL); + tv.tv_sec = 1; + tv.tv_usec = 0; + event_add(ev, &tv); + + The callback 'cb' will be invoked whenever fd is ready to read, OR whenever + a second has passed since the last invocation of cb. + +2.7. Multiple events allowed per fd + + Older versions of Libevent allowed at most one EV_READ event and at most + one EV_WRITE event per socket, per event base. This restriction is no + longer present. + +2.8. evthread_* functions for thread-safe structures. + + Libevent structures can now be built with locking support. This code + makes it safe to add, remove, and activate events on an event base from a + different thread. (Previously, if you wanted to write multithreaded code + with Libevent, you could only an event_base or its events in one thread at + a time.) + + If you want threading support and you're using pthreads, you can just + call evthread_use_pthreads(). (You'll need to link against the + libevent_pthreads library in addition to libevent_core. These functions are + not in libevent_core.) + + If you want threading support and you're using Windows, you can just + call evthread_use_windows_threads(). + + If you are using some locking system besides Windows and pthreads, You + can enable this on a per-event-base level by writing functions to + implement mutexes, conditions, and thread IDs, and passing them to + evthread_set_lock_callbacks and related functions in event2/thread.h. + + Once locking functions are enabled, every new event_base is created with a + lock. You can prevent a single event_base from being built with a lock + disabled by using the EVENT_BASE_FLAG_NOLOCK flag in its + event_config. If an event_base is created with a lock, it is safe to call + event_del, event_add, and event_active on its events from any thread. The + event callbacks themselves are still all executed from the thread running + the event loop. + + To make an evbuffer or a bufferevent object threadsafe, call its + *_enable_locking() function. + + The HTTP api is not currently threadsafe. + + To build Libevent with threading support disabled, pass + --disable-thread-support to the configure script. + +2.9. Edge-triggered events on some backends. + + With some backends, it's now possible to add the EV_ET flag to an event + in order to request that the event's semantics be edge-triggered. Right + now, epoll and kqueue support this. + + The corresponding event_config feature is EV_FEATURE_ET; see 2.4 for more + information. + +2.10. Better support for huge numbers of timeouts + + The heap-based priority queue timer implementation for Libevent 1.4 is good + for randomly distributed timeouts, but suboptimal if you have huge numbers + of timeouts that all expire in the same amount of time after their + creation. The new event_base_init_common_timeout() logic lets you signal + that a given timeout interval will be very common, and should use a linked + list implementation instead of a priority queue. + +2.11. Improved debugging support + + It's been pretty easy to forget to delete all your events before you + re-initialize them, or otherwise put Libevent in an internally inconsistent + state. You can tell libevent to catch these and other common errors with + the new event_enable_debug_mode() call. Just invoke it before you do + any calls to other libevent functions, and it'll catch many common + event-level errors in your code. + +2.12. Functions to access all event fields + + So that you don't have to access the struct event fields directly, Libevent + now provides accessor functions to retrieve everything from an event that + you set during event_new() or event_assign(). + +3. Backend-specific and performance improvements. + +3.1. Change-minimization on O(1) backends + + With previous versions of Libevent, if you called event_del() and + event_add() repeatedly on a single event between trips to the backend's + dispatch function, the backend might wind up making unnecessary calls or + passing unnecessary data to the kernel. The new backend logic batches up + redundant adds and deletes, and performs no more operations than necessary + at the kernel level. + + This logic is on for the kqueue backend, and available (but off by + default) for the epoll backend. To turn it on for the epoll backend, + set the EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST flag in the + event_base_cofig, or set the EVENT_EPOLL_USE_CHANGELIST environment + variable. Doing this with epoll may result in weird bugs if you give + any fds closed by dup() or its variants. + +3.2. Improved notification on Linux + + When we need to wake the event loop up from another thread, we use + an epollfd to do so, instead of a socketpair. This is supposed to be + faster. + +3.3. Windows: better support for everything + + Bufferevents on Windows can use a new mechanism (off-by-default; see below) + to send their data via Windows overlapped IO and get their notifications + via the IOCP API. This should be much faster than using event-based + notification. + + Other functions throughout the code have been fixed to work more + consistently with Windows. Libevent now builds on Windows using either + mingw, or using MSVC (with nmake). Libevent works fine with UNICODE + defined, or not. + + Data structures are a little smarter: our lookups from socket to pending + event are now done with O(1) hash tables rather than O(lg n) red-black + trees. + + Unfortunately, the main Windows backend is still select()-based: from + testing the IOCP backends on the mailing list, it seems that there isn't + actually a way to tell for certain whether a socket is writable with IOCP. + Libevent 2.1 may add a multithreaded WaitForMultipleEvents-based + backend for better performance with many inactive sockets and better + integration with Windows events. + +4. Improvements to evbuffers + + Libevent has long had an "evbuffer" implementation to wrap access to an + input or output memory buffer. In previous versions, the implementation + was very inefficient and lacked some desirable features. We've made many + improvements in Libevent 2.0. + +4.1. Chunked-memory internal representation + + Previously, each evbuffer was a huge chunk of memory. When we ran out of + space in an evbuffer, we used realloc() to grow the chunk of memory. When + data was misaligned, we used memmove to move the data back to the front + of the buffer. + + Needless to say, this is a terrible interface for networked IO. + + Now, evbuffers are implemented as a linked list of memory chunks, like + most Unix kernels use for network IO. (See Linux's skbuf interfaces, + or *BSD's mbufs). Data is added at the end of the linked list and + removed from the front, so that we don't ever need realloc huge chunks + or memmove the whole buffer contents. + + To avoid excessive calls to read and write, we use the readv/writev + interfaces (or WSASend/WSARecv on Windows) to do IO on multiple chunks at + once with a single system call. + + COMPATIBILITY NOTE: + The evbuffer struct is no longer exposed in a header. The code here is + too volatile to expose an official evbuffer structure, and there was never + any means provided to create an evbuffer except via evbuffer_new which + heap-allocated the buffer. + + If you need access to the whole buffer as a linear chunk of memory, the + EVBUFFER_DATA() function still works. Watch out, though: it needs to copy + the buffer's contents in a linear chunk before you can use it. + +4.2. More flexible readline support + + The old evbuffer_readline() function (which accepted any sequence of + CR and LF characters as a newline, and which couldn't handle lines + containing NUL characters), is now deprecated. The preferred + function is evbuffer_readln(), which supports a variety of + line-ending styles, and which can return the number of characters in + the line returned. + + You can also call evbuffer_search_eol() to find the end of a line + in an evbuffer without ever extracting the line. + +4.3. Support for file-based IO in evbuffers. + + You can now add chunks of a file into a evbuffer, and Libevent will have + your OS use mapped-memory functionality, sendfile, or splice to transfer + the data without ever copying it to userspace. On OSs where this is not + supported, Libevent just loads the data. + + There are probably some bugs remaining in this code. On some platforms + (like Windows), it just reads the relevant parts of the file into RAM. + +4.4. Support for zero-copy ("scatter/gather") writes in evbuffers. + + You can add a piece of memory to an evbuffer without copying it. + Instead, Libevent adds a new element to the evbuffer's linked list of + chunks with a pointer to the memory you supplied. You can do this + either with a reference-counted chunk (via evbuffer_add_reference), or + by asking Libevent for a pointer to its internal vectors (via + evbuffer_reserve_space or evbuffer_peek()). + +4.5. Multiple callbacks per evbuffer + + Previously, you could only have one callback active on an evbuffer at a + time. In practice, this meant that if one part of Libevent was using an + evbuffer callback to notice when an internal evbuffer was reading or + writing data, you couldn't have your own callback on that evbuffer. + + Now, you can now use the evbuffer_add_cb() function to add a callback that + does not interfere with any other callbacks. + + The evbuffer_setcb() function is now deprecated. + +4.6. New callback interface + + Previously, evbuffer callbacks were invoked with the old size of the + buffer and the new size of the buffer. This interface could not capture + operations that simultaneously filled _and_ drained a buffer, or handle + cases where we needed to postpone callbacks until multiple operations were + complete. + + Callbacks that are set with evbuffer_setcb still use the old API. + Callbacks added with evbuffer_add_cb() use a new interface that takes a + pointer to a struct holding the total number of bytes drained read and the + total number of bytes written. See event2/buffer.h for full details. + +4.7. Misc new evbuffer features + + You can use evbuffer_remove() to move a given number of bytes from one + buffer to another. + + The evbuffer_search() function lets you search for repeated instances of + a pattern inside an evbuffer. + + You can use evbuffer_freeze() to temporarily suspend drains from or adds + to a given evbuffer. This is useful for code that exposes an evbuffer as + part of its public API, but wants users to treat it as a pure source or + sink. + + There's an evbuffer_copyout() that looks at the data at the start of an + evbuffer without doing a drain. + + You can have an evbuffer defer all of its callbacks, so that rather than + being invoked immediately when the evbuffer's length changes, they are + invoked from within the event_loop. This is useful when you have a + complex set of callbacks that can change the length of other evbuffers, + and you want to avoid having them recurse and overflow your stack. + +5. Bufferevents improvements + + Libevent has long included a "bufferevents" structure and related + functions that were useful for generic buffered IO on a TCP connection. + This is what Libevent uses for its HTTP implementation. In addition to + the improvements that they get for free from the underlying evbuffer + implementation above, there are many new features in Libevent 2.0's + evbuffers. + +5.1. New OO implementations + + The "bufferevent" structure is now an abstract base type with multiple + implementations. This should not break existing code, which always + allocated bufferevents with bufferevent_new(). + + Current implementations of the bufferevent interface are described below. + +5.2. bufferevent_socket_new() replaces bufferevent_new() + + Since bufferevents that use a socket are not the only kind, + bufferevent_new() is now deprecated. Use bufferevent_socket_new() + instead. + +5.3. Filtered bufferevent IO + + You can use bufferevent_filter_new() to create a bufferevent that wraps + around another bufferevent and transforms data it is sending and + receiving. See test/regress_zlib.c for a toy example that uses zlib to + compress data before sending it over a bufferevent. + +5.3. Linked pairs of bufferevents + + You can use bufferevent_pair_new() to produce two linked + bufferevents. This is like using socketpair, but doesn't require + system-calls. + +5.4. SSL support for bufferevents with OpenSSL + + There is now a bufferevent type that supports SSL/TLS using the + OpenSSL library. The code for this is build in a separate + library, libevent_openssl, so that your programs don't need to + link against OpenSSL unless they actually want SSL support. + + There are two ways to construct one of these bufferevents, both + declared in <event2/bufferevent_ssl.h>. If you want to wrap an + SSL layer around an existing bufferevent, you would call the + bufferevent_openssl_filter_new() function. If you want to do SSL + on a socket directly, call bufferevent_openssl_socket_new(). + +5.5. IOCP support for bufferevents on Windows + + There is now a bufferevents backend that supports IOCP on Windows. + Supposedly, this will eventually make Windows IO much faster for + programs using bufferevents. We'll have to see; the code is not + currently optimized at all. To try it out, call the + event_base_start_iocp() method on an event_base before contructing + bufferevents. + + This is tricky code; there are probably some bugs hiding here. + +5.6. Improved connect support for bufferevents. + + You can now create a bufferevent that is not yet connected to any + host, and tell it to connect, either by address or by hostname. + + The functions to do this are bufferevent_socket_connect and + bufferevent_socket_connect_hostname. + +5.7. Rate-limiting for bufferevents + + If you need to limit the number of bytes read/written by a single + bufferevent, or by a group of them, you can do this with a new set of + bufferevent rate-limiting calls. + +6. Other improvements + +6.1. DNS improvements + +6.1.1. DNS: IPv6 nameservers + + The evdns code now lets you have nameservers whose addresses are IPv6. + +6.1.2. DNS: Better security + + Libevent 2.0 tries harder to resist DNS answer-sniping attacks than + earlier versions of evdns. See comments in the code for full details. + + Notably, evdns now supports the "0x20 hack" to make it harder to + impersonate a DNS server. Additionally, Libevent now uses a strong + internal RNG to generate DNS transaction IDs, so you don't need to supply + your own. + +6.1.3. DNS: Getaddrinfo support + + There's now an asynchronous getaddrinfo clone, evdns_getaddrinfo(), + to make the results of the evdns functions more usable. It doesn't + support every feature of a typical platform getaddrinfo() yet, but it + is quite close. + + There is also a blocking evutil_getaddrinfo() declared in + event2/util.h, to provide a getaddrinfo() implementation for + platforms that don't have one, and smooth over the differences in + various platforms implementations of RFC3493. + + Bufferevents provide bufferevent_connect_hostname(), which combines + the name lookup and connect operations. + +6.1.4. DNS: No more evdns globals + + Like an event base, evdns operations are now supposed to use an evdns_base + argument. This makes them easier to wrap for other (more OO) languages, + and easier to control the lifetime of. The old evdns functions will + still, of course, continue working. + +6.2. Listener support + + You can now more easily automate setting up a bound socket to listen for + TCP connections. Just use the evconnlistener_*() functions in the + event2/listener.h header. + + The listener code supports IOCP on Windows if available. + +6.3. Secure RNG support + + Network code very frequently needs a secure, hard-to-predict random number + generator. Some operating systems provide a good C implementation of one; + others do not. Libevent 2.0 now provides a consistent implementation + based on the arc4random code originally from OpenBSD. Libevent (and you) + can use the evutil_secure_rng_*() functions to access a fairly secure + random stream of bytes. + +6.4. HTTP + + The evhttp uriencoding and uridecoding APIs have updated versions + that behave more correctly, and can handle strings with internal NULs. + + The evhttp query parsing and URI parsing logic can now detect errors + more usefully. Moreover, we include an actual URI parsing function + (evhttp_uri_parse()) to correctly parse URIs, so as to discourage + people from rolling their own ad-hoc parsing functions. + + There are now accessor functions for the useful fields of struct http + and friends; it shouldn't be necessary to access them directly any + more. + + Libevent now lets you declare support for all specified HTTP methods, + including OPTIONS, PATCH, and so on. The default list is unchanged. + + Numerous evhttp bugs also got fixed. + +7. Infrastructure improvements + +7.1. Better unit test framework + + We now use a unit test framework that Nick wrote called "tinytest". + The main benefit from Libevent's point of view is that tests which + might mess with global state can all run each in their own + subprocess. This way, when there's a bug that makes one unit test + crash or mess up global state, it doesn't affect any others. + +7.2. Better unit tests + + Despite all the code we've added, our unit tests are much better than + before. Right now, iterating over the different backends on various + platforms, I'm getting between 78% and 81% test coverage, compared + with less than 45% test coverage in Libevent 1.4. + diff --git a/libs/libevent/docs/whatsnew-2.1.txt b/libs/libevent/docs/whatsnew-2.1.txt new file mode 100644 index 0000000000..0be54ae11b --- /dev/null +++ b/libs/libevent/docs/whatsnew-2.1.txt @@ -0,0 +1,690 @@ + What's new in Libevent 2.1 + Nick Mathewson + +0. Before we start + +0.1. About this document + + This document describes the key differences between Libevent 2.0 and + Libevent 2.1, from a user's point of view. It's a work in progress. + + For better documentation about libevent, see the links at + http://libevent.org/ + + Libevent 2.1 would not be possible without the generous help of + numerous volunteers. For a list of who did what in Libevent 2.1, + please see the ChangeLog! + + NOTE: I am very sure that I missed some thing on this list. Caveat + haxxor. + +0.2. Where to get help + + Try looking at the other documentation too. All of the header files + have documentation in the doxygen format; this gets turned into nice + HTML and linked to from the libevent.org website. + + There is a work-in-progress book with reference manual at + http://www.wangafu.net/~nickm/libevent-book/ . + + You can ask questions on the #libevent IRC channel at irc.oftc.net or + on the mailing list at libevent-users@freehaven.net. The mailing list + is subscribers-only, so you will need to subscribe before you post. + +0.3. Compatibility + + Our source-compatibility policy is that correct code (that is to say, + code that uses public interfaces of Libevent and relies only on their + documented behavior) should have forward source compatibility: any + such code that worked with a previous version of Libevent should work + with this version too. + + We don't try to do binary compatibility except within stable release + series, so binaries linked against any version of Libevent 2.0 will + probably need to be recompiled against Libevent 2.1.4-alpha if you + want to use it. It is probable that we'll break binary compatibility + again before Libevent 2.1 is stable. + +1. New APIs and features + +1.1. New ways to build libevent + + We now provide an --enable-gcc-hardening configure option to turn on + GCC features designed for increased code security. + + There is also an --enable-silent-rules configure option to make + compilation run more quietly with automake 1.11 or later. + + You no longer need to use the --enable-gcc-warnings option to turn on + all of the GCC warnings that Libevent uses. The only change from + using that option now is to turn warnings into errors. + + For IDE users, files that are not supposed to be built are now + surrounded with appropriate #ifdef lines to keep your IDE from getting + upset. + + There is now an alternative cmake-based build process; cmake users + should see the relevant sections in the README. + + +1.2. New functions for events and the event loop + + If you're running Libevent with multiple event priorities, you might + want to make sure that Libevent checks for new events frequently, so + that time-consuming or numerous low-priority events don't keep it from + checking for new high-priority events. You can now use the + event_config_set_max_dispatch_interval() interface to ensure that the + loop checks for new events either every N microseconds, every M + callbacks, or both. + + When configuring an event base, you can now choose whether you want + timers to be more efficient, or more precise. (This only has effect + on Linux for now.) Timers are efficient by default: to select more + precise timers, use the EVENT_BASE_FLAG_PRECISE_TIMER flag when + constructing the event_config, or set the EVENT_PRECISE_TIMER + environment variable to a non-empty string. + + There is an EVLOOP_NO_EXIT_ON_EMPTY flag that tells event_base_loop() + to keep looping even when there are no pending events. (Ordinarily, + event_base_loop() will exit as soon as no events are pending.) + + Past versions of Libevent have been annoying to use with some + memory-leak-checking tools, because Libevent allocated some global + singletons but provided no means to free them. There is now a + function, libevent_global_shutdown(), that you can use to free all + globally held resources before exiting, so that your leak-check tools + don't complain. (Note: this function doesn't free non-global things + like events, bufferevents, and so on; and it doesn't free anything + that wouldn't otherwise get cleaned up by the operating system when + your process exit()s. If you aren't using a leak-checking tool, there + is not much reason to call libevent_global_shutdown().) + + There is a new event_base_get_npriorities() function to return the + number of priorities set in the event base. + + Libevent 2.0 added an event_new() function to construct a new struct + event on the heap. Unfortunately, with event_new(), there was no + equivalent for: + + struct event ev; + event_assign(&ev, base, fd, EV_READ, callback, &ev); + + In other words, there was no easy way for event_new() to set up an + event so that the event itself would be its callback argument. + Libevent 2.1 lets you do this by passing "event_self_cbarg()" as the + callback argument: + + struct event *evp; + evp = event_new(base, fd, EV_READ, callback, + event_self_cbarg()); + + There's also a new event_base_get_running_event() function you can + call from within a Libevent callback to get a pointer to the current + event. This should never be strictly necessary, but it's sometimes + convenient. + + The event_base_once() function used to leak some memory if the event + that it added was never actually triggered. Now, its memory is + tracked in the event_base and freed when the event_base is freed. + Note however that Libevent doesn't know how to free any information + passed as the callback argument to event_base_once is still something + you'll might need a way to de-allocate yourself. + + There is an event_get_priority() function to return an event's + priority. + + By analogy to event_base_loopbreak(), there is now an + event_base_loopcontinue() that tells Libevent to stop processing + active event callbacks, and re-scan for new events right away. + + There's a function, event_base_foreach_event(), that can iterate over + every event currently pending or active on an event base, and invoke a + user-supplied callback on each. The callback must not alter the events + or add or remove anything to the event base. + + We now have an event_remove_timer() function to remove the timeout on + an event while leaving its socket and/or signal triggers unchanged. + (If we were designing the API from scratch, this would be the behavior + of "event_add(ev, NULL)" on an already-added event with a timeout. But + that's a no-op in past versions of Libevent, and we don't want to + break compatibility.) + + You can use the new event_base_get_num_events() function to find the + number of events active or pending on an event_base. To find the + largest number of events that there have been since the last call, use + event_base_get_max_events(). + + You can now activate all the events waiting for a given fd or signal + using the event_base_active_by_fd() and event_base_active_by_signal() + APIs. + + On backends that support it (currently epoll), there is now an + EV_CLOSED flag that programs can use to detect when a socket has + closed without having to read all the bytes until receiving an EOF. + +1.3. Event finalization + + [NOTE: This is an experimental feature in Libevent 2.1.3-alpha. Though + it seems solid so far, its API might change between now and the first + release candidate for Libevent 2.1.] + +1.3.1. Why event finalization? + + Libevent 2.1 now supports an API for safely "finalizing" events that + might be running in multiple threads, and provides a way to slightly + change the semantics of event_del() to prevent deadlocks in + multithreaded programs. + + To motivate this feature, consider the following code, in the context + of a mulithreaded Libevent application: + + struct connection *conn = event_get_callback_arg(ev); + event_del(ev); + connection_free(conn); + + Suppose that the event's callback might be running in another thread, + and using the value of "conn" concurrently. We wouldn't want to + execute the connection_free() call until "conn" is no longer in use. + How can we make this code safe? + + Libevent 2.0 answered that question by saying that the event_del() + call should block if the event's callback is running in another + thread. That way, we can be sure that event_del() has canceled the + callback (if the callback hadn't started running yet), or has waited + for the callback to finish. + + But now suppose that the data structure is protected by a lock, and we + have the following code: + + void check_disable(struct connection *connection) { + lock(connection); + if (should_stop_reading(connection)) + event_del(connection->read_event); + unlock(connection); + } + + What happens when we call check_disable() from a callback and from + another thread? Let's say that the other thread gets the lock + first. If it decides to call event_del(), it will wait for the + callback to finish. But meanwhile, the callback will be waiting for + the lock on the connection. Since each threads is waiting for the + other one to release a resource, the program will deadlock. + + This bug showed up in multithreaded bufferevent programs in 2.1, + particularly when freeing bufferevents. (For more information, see + the "Deadlock when calling bufferevent_free from an other thread" + thread on libevent-users starting on 6 August 2012 and running through + February of 2013. You might also like to read my earlier writeup at + http://archives.seul.org/libevent/users/Feb-2012/msg00053.html and + the ensuing discussion.) + +1.3.2. The EV_FINALIZE flag and avoiding deadlock + + To prevent the deadlock condition described above, Libevent + 2.1.3-alpha adds a new flag, "EV_FINALIZE". You can pass it to + event_new() and event_assign() along with EV_READ, EV_WRITE, and the + other event flags. + + When an event is constructed with the EV_FINALIZE flag, event_del() + will not block on that event, even when the event's callback is + running in another thread. By using EV_FINALIZE, you are therefore + promising not to use the "event_del(ev); free(event_get_callback_arg(ev));" + pattern, but rather to use one of the finalization functions below to + clean up the event. + + EV_FINALIZE has no effect on a single-threaded program, or on a + program where events are only used from one thread. + + + There are also two new variants of event_del() that you can use for + more fine-grained control: + event_del_noblock(ev) + event_del_block(ev) + The event_del_noblock() function will never block, even if the event + callback is running in another thread and doesn't have the EV_FINALIZE + flag. The event_del_block() function will _always_ block if the event + callback is running in another thread, even if the event _does_ have + the EV_FINALIZE flag. + + [A future version of Libevent may have a way to make the EV_FINALIZE + flag the default.] + +1.3.3. Safely finalizing events + + To safely tear down an event that may be running, Libevent 2.1.3-alpha + introduces event_finalize() and event_free_finalize(). You call them + on an event, and provide a finalizer callback to be run on the event + and its callback argument once the event is definitely no longer + running. + + With event_free_finalize(), the event is also freed once the finalizer + callback has been invoked. + + A finalized event cannot be re-added or activated. The finalizer + callback must not add events, activate events, or attempt to + "resucitate" the event being finalized in any way. + + If any finalizer callbacks are pending as the event_base is being + freed, they will be invoked. You can override this behavior with the + new function event_base_free_nofinalize(). + +1.4. New debugging features + + You can now turn on debug logs at runtime using a new function, + event_enable_debug_logging(). + + The event_enable_lock_debugging() function is now spelled correctly. + You can still use the old "event_enable_lock_debuging" name, though, + so your old programs shouldnt' break. + + There's also been some work done to try to make the debugging logs + more generally useful. + +1.5. New evbuffer functions + + In Libevent 2.0, we introduced evbuffer_add_file() to add an entire + file's contents to an evbuffer, and then send them using sendfile() or + mmap() as appropriate. This API had some drawbacks, however. + Notably, it created one mapping or fd for every instance of the same + file added to any evbuffer. Also, adding a file to an evbuffer could + make that buffer unusable with SSL bufferevents, filtering + bufferevents, and any code that tried to read the contents of the + evbuffer. + + Libevent 2.1 adds a new evbuffer_file_segment API to solve these + problems. Now, you can use evbuffer_file_segment_new() to construct a + file-segment object, and evbuffer_add_file_segment() to insert it (or + part of it) into an evbuffer. These segments avoid creating redundant + maps or fds. Better still, the code is smart enough (when the OS + supports sendfile) to map the file when that's necessary, and use + sendfile() otherwise. + + File segments can receive callback functions that are invoked when the + file segments are freed. + + The evbuffer_ptr interface has been extended so that an evbuffer_ptr + can now yield a point just after the end of the buffer. This makes + many algorithms simpler to implement. + + There's a new evbuffer_add_buffer() interface that you can use to add + one buffer to another nondestructively. When you say + evbuffer_add_buffer_reference(outbuf, inbuf), outbuf now contains a + reference to the contents of inbuf. + + To aid in adding data in bulk while minimizing evbuffer calls, there + is an evbuffer_add_iovec() function. + + There's a new evbuffer_copyout_from() variant function to enable + copying data nondestructively from the middle of a buffer. + + evbuffer_readln() now supports an EVBUFFER_EOL_NUL argument to fetch + NUL-terminated strings from buffers. + +1.6. New functions and features: bufferevents + + You can now use the bufferevent_getcb() function to find out a + bufferevent's callbacks. Previously, there was no supported way to do + that. + + The largest chunk readable or writeable in a single bufferevent + callback is no longer hardcoded; it's now configurable with + the new functions bufferevent_set_max_single_read() and + bufferevent_set_max_single_write(). + + For consistency, OpenSSL bufferevents now make sure to always set one + of BEV_EVENT_READING or BEV_EVENT_WRITING when invoking an event + callback. + + Calling bufferevent_set_timeouts(bev, NULL, NULL) now removes the + timeouts from socket and ssl bufferevents correctly. + + You can find the priority at which a bufferevent runs with + bufferevent_get_priority(). + + The function bufferevent_get_token_bucket_cfg() can retrieve the + rate-limit settings for a bufferevent; bufferevent_getwatermark() can + return a bufferevent's current watermark settings. + + You can manually trigger a bufferevent's callbacks via + bufferevent_trigger() and bufferevent_trigger_event(). + +1.7. New functions and features: evdns + + The previous evdns interface used an "open a test UDP socket" trick in + order to detect IPv6 support. This was a hack, since it would + sometimes badly confuse people's firewall software, even though no + packets were sent. The current evdns interface-detection code uses + the appropriate OS functions to see which interfaces are configured. + + The evdns_base_new() function now has multiple possible values for its + second (flags) argument. Using 1 and 0 have their old meanings, though the + 1 flag now has a symbolic name of EVDNS_BASE_INITIALIZE_NAMESERVERS. + A second flag is now supported too: the EVDNS_BASE_DISABLE_WHEN_INACTIVE + flag, which tells the evdns_base that it should not prevent Libevent from + exiting while it has no DNS requests in progress. + + There is a new evdns_base_clear_host_addresses() function to remove + all the /etc/hosts addresses registered with an evdns instance. + +1.8. New functions and features: evconnlistener + + Libevent 2.1 adds the following evconnlistener flags: + + LEV_OPT_DEFERRED_ACCEPT -- Tells the OS that it doesn't need to + report sockets as having arrived until the initiator has sent some + data too. This can greatly improve performance with protocols like + HTTP where the client always speaks first. On operating systems + that don't support this functionality, this option has no effect. + + LEV_OPT_DISABLED -- Creates an evconnlistener in the disabled (not + listening) state. + + Libevent 2.1 changes the behavior of the LEV_OPT_CLOSE_ON_EXEC + flag. Previously, it would apply to the listener sockets, but not to + the accepted sockets themselves. That's almost never what you want. + Now, it applies both to the listener and the accepted sockets. + +1.9. New functions and features: evhttp + + ********************************************************************** + NOTE: The evhttp module will eventually be deprecated in favor of Mark + Ellzey's libevhtp library. Don't worry -- this won't happen until + libevhtp provides every feature that evhttp does, and provides a + compatible interface that applications can use to migrate. + ********************************************************************** + + Previously, you could only set evhttp timeouts in increments of one + second. Now, you can use evhttp_set_timeout_tv() and + evhttp_connection_set_timeout_tv() to configure + microsecond-granularity timeouts. + + There are a new pair of functions: evhttp_set_bevcb() and + evhttp_connection_base_bufferevent_new(), that you can use to + configure which bufferevents will be used for incoming and outgoing + http connections respectively. These functions, combined with SSL + bufferevents, should enable HTTPS support. + + There's a new evhttp_foreach_bound_socket() function to iterate over + every listener on an evhttp object. + + Whitespace between lines in headers is now folded into a single space; + whitespace at the end of a header is now removed. + + The socket errno value is now preserved when invoking an http error + callback. + + There's a new kind of request callback for errors; you can set it with + evhttp_request_set_error_cb(). It gets called when there's a request error, + and actually reports the error code and lets you figure out which request + failed. + + You can navigate from an evhttp_connection back to its evhttp with the + new evhttp_connection_get_server() function. + + You can override the default HTTP Content-Type with the new + evhttp_set_default_content_type() function + + There's a new evhttp_connection_get_addr() API to return the peer + address of an evhttp_connection. + + The new evhttp_send_reply_chunk_with_cb() is a variant of + evhttp_send_reply_chunk() with a callback to be invoked when the + chunk is sent. + + The evhttp_request_set_header_cb() facility adds a callback to be + invoked while parsing headers. + + The evhttp_request_set_on_complete_cb() facility adds a callback to be + invoked on request completion. + +1.10. New functions and features: evutil + + There's a function "evutil_secure_rng_set_urandom_device_file()" that + you can use to override the default file that Libevent uses to seed + its (sort-of) secure RNG. + +2. Cross-platform performance improvements + +2.1. Better data structures + + We replaced several users of the sys/queue.h "TAILQ" data structure + with the "LIST" data structure. Because this data type doesn't + require FIFO access, it requires fewer pointer checks and + manipulations to keep it in line. + + All previous versions of Libevent have kept every pending (added) + event in an "eventqueue" data structure. Starting in Libevent 2.0, + however, this structure became redundant: every pending timeout event + is stored in the timeout heap or in one of the common_timeout queues, + and every pending fd or signal event is stored in an evmap. Libevent + 2.1 removes this data structure, and thereby saves all of the code + that we'd been using to keep it updated. + +2.2. Faster activations and timeouts + + It's a common pattern in older code to use event_base_once() with a + 0-second timeout to ensure that a callback will get run 'as soon as + possible' in the current iteration of the Libevent loop. We optimize + this case by calling event_active() directly, and bypassing the + timeout pool. (People who are using this pattern should also consider + using event_active() themselves.) + + Libevent 2.0 would wake up a polling event loop whenever the first + timeout in the event loop was adjusted--whether it had become earlier + or later. We now only notify the event loop when a change causes the + expiration time to become _sooner_ than it would have been otherwise. + + The timeout heap code is now optimized to perform fewer comparisons + and shifts when changing or removing a timeout. + + Instead of checking for a wall-clock time jump every time we call + clock_gettime(), we now check only every 5 seconds. This should save + a huge number of gettimeofday() calls. + +2.3. Microoptimizations + + Internal event list maintainance no longer use the antipattern where + we have one function with multiple totally independent behaviors + depending on an argument: + #define OP1 1 + #define OP2 2 + #define OP3 3 + void func(int operation, struct event *ev) { + switch (op) { + ... + } + } + Instead, these functions are now split into separate functions for + each operation: + void func_op1(struct event *ev) { ... } + void func_op2(struct event *ev) { ... } + void func_op3(struct event *ev) { ... } + + This produces better code generation and inlining decisions on some + compilers, and makes the code easier to read and check. + +2.4. Evbuffer performance improvements + + The EVBUFFER_EOL_CRLF line-ending type is now much faster, thanks to + smart optimizations. + +2.5. HTTP performance improvements + + o Performance tweak to evhttp_parse_request_line. (aee1a97 Mark Ellzey) + o Add missing break to evhttp_parse_request_line (0fcc536) + +2.6. Coarse timers by default on Linux + + Due to limitations of the epoll interface, Libevent programs using epoll + have not previously been able to wait for timeouts with accuracy smaller + than 1 millisecond. But Libevent had been using CLOCK_MONOTONIC for + timekeeping on Linux, which is needlessly expensive: CLOCK_MONOTONIC_COARSE + has approximately the resolution corresponding to epoll, and is much faster + to invoke than CLOCK_MONOTONIC. + + To disable coarse timers, and get a more plausible precision, use the + new EVENT_BASE_FLAG_PRECISE_TIMER flag when setting up your event base. + +3. Backend/OS-specific improvements + +3.1. Linux-specific improvements + + The logic for deciding which arguements to use with epoll_ctl() is now + a table-driven lookup, rather than the previous pile of cascading + branches. This should minimize epoll_ctl() calls and make the epoll + code run a little faster on change-heavy loads. + + Libevent now takes advantage of Linux's support for enhanced APIs + (e.g., SOCK_CLOEXEC, SOCK_NONBLOCK, accept4, pipe2) that allow us to + simultaneously create a socket, make it nonblocking, and make it + close-on-exec. This should save syscalls throughout our codebase, and + avoid race-conditions if an exec() occurs after a socket is socket is + created but before we can make it close-on-execute on it. + +3.2. Windows-specific improvements + + We now use GetSystemTimeAsFileTime to implement gettimeofday. It's + significantly faster and more accurate than our old ftime()-based approach. + +3.3. Improvements in the solaris evport backend. + + The evport backend has been updated to use many of the infrastructure + improvements from Libevent 2.0. Notably, it keeps track of per-fd + information using the evmap infrastructure, and removes a number of + linear scans over recently-added events. This last change makes it + efficient to receive many more events per evport_getn() call, thereby + reducing evport overhead in general. + +3.4. OSX backend improvements + + The OSX select backend doesn't like to have more than a certain number + of fds set unless an "unlimited select" option has been set. + Therefore, we now set it. + +3.5. Monotonic clocks on even more platforms + + Libevent previously used a monotonic clock for its internal timekeeping + only on platforms supporting the POSIX clock_gettime() interface. Now, + Libevent has support for monotonic clocks on OSX and Windows too, and a + fallback implementation for systems without monotonic clocks that will at + least keep time running forwards. + + Using monotonic timers makes Libevent more resilient to changes in the + system time, as can happen in small amounts due to clock adjustments from + NTP, or in large amounts due to users who move their system clocks all over + the timeline in order to keep nagware from nagging them. + +3.6. Faster cross-thread notification on kqueue + + When a thread other than the one in which the main event loop is + running needs to wake the thread running the main event loop, Libevent + usually writes to a socketpair in order to force the main event loop + to wake up. On Linux, we've been able to use eventfd() instead. Now + on BSD and OSX systems (any anywhere else that has kqueue with the + EVFILT_USER extension), we can use EVFILT_USER to wake up the main + thread from kqueue. This should be a tiny bit faster than the + previous approach. + +4. Infrastructure improvements + +4.1. Faster tests + + I've spent some time to try to make the unit tests run faster in + Libevent 2.1. Nearly all of this was a matter of searching slow tests + for unreasonably long timeouts, and cutting them down to reasonably + long delays, though on one or two cases I actually had to parallelize + an operation or improve an algorithm. + + On my desktop, a full "make verify" run of Libevent 2.0.18-stable + requires about 218 seconds. Libevent 2.1.1-alpha cuts this down to + about 78 seconds. + + Faster unit tests are great, since they let programmers test their + changes without losing their train of thought. + +4.2. Finicky tests are now off-by-default + + The Tinytest unit testing framework now supports optional tests, and + Libevent uses them. By default, Libevent's unit testing framework + does not run tests that require a working network, and does not run + tests that tend to fail on heavily loaded systems because of timing + issues. To re-enable all tests, run ./test/regress using the "@all" + alias. + +4.3. Modernized use of autotools + + Our autotools-based build system has been updated to build without + warnings on recent autoconf/automake versions. + + Libevent's autotools makefiles are no longer recursive. This allows + make to use the maximum possible parallelism to do the minimally + necessary amount of work. See Peter Miller's "Recursive Make + Considered Harmful" at http://miller.emu.id.au/pmiller/books/rmch/ for + more information here. + + We now use the "quiet build" option to suppress distracting messages + about which commandlines are running. You can get them back with + "make V=1". + +4.4. Portability + + Libevent now uses large-file support internally on platforms where it + matters. You shouldn't need to set _LARGEFILE or OFFSET_BITS or + anything magic before including the Libevent headers, either, since + Libevent now sets the size of ev_off_t to the size of off_t that it + received at compile time, not to some (possibly different) size based + on current macro definitions when your program is building. + + We now also use the Autoconf AC_USE_SYSTEM_EXTENSIONS mechanism to + enable per-system macros needed to enable not-on-by-default features. + Unlike the rest of the autoconf macros, we output these to an + internal-use-only evconfig-private.h header, since their names need to + survive unmangled. This lets us build correctly on more platforms, + and avoid inconsistencies when some files define _GNU_SOURCE and + others don't. + + Libevent now tries to detect OpenSSL via pkg-config. + +4.5. Standards conformance + + Previous Libevent versions had no consistent convention for internal + vs external identifiers, and used identifiers starting with the "_" + character throughout the codebase. That's no good, since the C + standard says that identifiers beginning with _ are reserved. I'm not + aware of having any collisions with system identifiers, but it's best + to fix these things before they cause trouble. + + We now avoid all use of the _identifiers in the Libevent source code. + These changes were made *mainly* through the use of automated scripts, + so there shouldn't be any mistakes, but you never know. + + As an exception, the names _EVENT_LOG_DEBUG, _EVENT_LOG_MSG_, + _EVENT_LOG_WARN, and _EVENT_LOG_ERR are still exposed in event.h: they + are now deprecated, but to support older code, they will need to stay + around for a while. New code should use EVENT_LOG_DEBUG, + EVENT_LOG_MSG, EVENT_LOG_WARN, and EVENT_LOG_ERR instead. + +4.6. Event and callback refactoring + + As a simplification and optimization to Libevent's "deferred callback" + logic (introduced in 2.0 to avoid callback recursion), Libevent now + treats all of its deferrable callback types using the same logic it + uses for active events. Now deferred events no longer cause priority + inversion, no longer require special code to cancel them, and so on. + + Regular events and deferred callbacks now both descend from an + internal light-weight event_callback supertype, and both support + priorities and take part in the other anti-priority-inversion + mechanisms in Libevent. + + To avoid starvation from callback recursion (which was the reason we + introduced "deferred callbacks" in the first place) the implementation + now allows an event callback to be scheduled as "active later": + instead of running in the current iteration of the event loop, it runs + in the next one. + +5. Testing + + Libevent's test coverage level is more or less unchanged since before: + we still have over 80% line coverage in our tests on Linux and OSX. + There are some under-tested modules, though: we need to fix those. |