Commit 418de21d authored by Yossi Gottlieb's avatar Yossi Gottlieb
Browse files

Squashed 'deps/hiredis/' changes from 00272d669..f8de9a4bd

f8de9a4bd Merge pull request #1046 from redis/rockylinux-ci
a41c9bc8b CentOS 8 is EOL, switch to RockyLinux
be41ed60d Avoid incorrect call to the previous reply's callback (#1040)
f2e8010d9 fix building on AIX and SunOS (#1031)
e73ab2f23 Add timeout support for libuv adapter (#1016)
f2ce5980e Allow sending commands after sending an unsubscribe (#1036)
ff860e55d Correction for command timeout during pubsub (#1038)
24d534493 CMakeLists.txt: allow building without a C++ compiler (#872)
4ece9a02e Fix adapters/libevent.h compilation for 64-bit Windows (#937)
799edfaad Don't link with crypto libs if USE_SSL isn't set.
f74b08182 Makefile: move SSL options into a block and refine rules
f347743b7 Update CMakeLists.txt for more portability (#1005)
f2be74802 Fix integer overflow when format command larger than 4GB (#1030)
58aacdac6 Handle array response in parallell with pubsub using RESP3 (#1014)
d3384260e Support PING while subscribing (RESP2) (#1027)
e3a479e40 FreeBSD build fixes + CI (#1026)
da5a4ff36 Add asynchronous test for pubsub using RESP3 (#1012)
b5716ee82 Valgrind returns error exit code when errors found (#1011)
1aed21a8c Move to using make directly in Cygwin (#1020)
a83f4b890 Correct CMake warning for libevent adapter example
c4333203e Remove unused parameter warning in libev adapter
7ad38dc4a Small tweaks of the async tests
4021726a6 Add asynchronous test for pubsub using RESP2
648763c36 Add build options for enabling async tests
c98c6994d Correcting the build target `coverage` for enabled SSL (#1009)
30ff8d850 Run SSL tests in CI
4a126e8a9 Add valgrind and CMake to tests
b73c2d410 Add Centos8
e9f647384 We should run actions on PRs
6ad4ccf3c Add Cygwin build test
783a3789c Add Windows tests in GitHub actions
0cac8dae1 Switch to GitHub actions
fa900ef76 Fix unused variable warning.
e489846b7 Minor refactor of CVE-2021-32765 fix.
51c740824 Remove extra comma from cmake var. Or it'll be treated as part of the var name.
632bf0718 Merge branch 'release/v1.0.2'
b73128324 Prepare for v1.0.2 GA
d4e6f109a Revert erroneous SONAME bump
a39824a5d Merge branch 'release/v1.0.1'
8d1bfac46  Prepare for v1.0.1 GA
76a7b1000 Fix for integer/buffer overflow CVE-2021-32765
9eca1f36f Allow to override OPENSSL_PREFIX in Linux
2d9d77518 Don't leak memory if an invalid type is set (#906)
f5f31ff9b Added REDIS_NO_AUTO_FREE_REPLIES flag (#962)
5850a8ecd Ensure we curry any connect error to an async context.
b6f86f38c Fix README.md
667dbf536 Merge pull request #935 from kristjanvalur/pr5
9bf6c250e Merge pull request #939 from zmartzone/improve_pr_896_ssl_leak
959af9760 Merge pull request #949 from plan-do-break-fix/Typo-corrections
0743f57bb fix(docs): corrects typos in project README
5f4382247 improve SSL leak fix redis/hiredis#896
e06ecf7e4 Ignore timeout callback from a successful connect
dfa33e60b Change order independant push logic to not change behavior.
6204182aa Handle the case where an invalidation is sent second.
d6a0b192b Merge branch 'reader-updates'
410c24d2a Fix off-by-one error in seekNewline
bd7488d27 read: Validate line items prior to checking for object creation callbacks
5f9242a1f read: Remove obsolete comment on nested multi bulk depth limitation
83c145042 read: Add support for the RESP3 bignum type
c6646cb19 read: Ensure no invalid '\r' or '\n' in simple status/error strings
e43061156 read: Additional validation and test case for RESP3 double
c8adea402 redisReply: Fix parent type assertions during double, nil, bool creation
ff73f1f9e redisReply: Explicitly list nil and bool cases in freeReplyObject() switch.
0f9251884 test: Add test case for RESP3 set
33c06dd50 test: Add test case for RESP3 map
397fe2630 read: Use memchr() in seekNewline() instead of looping over entire string
81c48a982 test: Add test cases for RESP3 bool
51e693f4f read: Add additional RESP3 bool validation
790b4d3b4 test: Add test cases for RESP3 nil
d8899fbc1 read: Add additional RESP3 nil validation
96e8ea611 test: Add test cases for infinite and NaN doubles
f913e9b99 read: Fix double validation and infinity parsing
8039c7d26 test: Add test case for doubles
49539fd1a redisReply: Fix - set len in double objects
53a8144c8 Merge pull request #924 from cheese1/master
9390de006 http -> https
7d99b5635 Merge pull request #917 from Nordix/stack-alloc-dict-iter
4bba72103 Handle OOM during async command callback registration
920128a26 Stack allocate dict iterators
297ecbecb Tiny formatting changes + suppress implicit memcpy warning
f746a28e7 Removed 2 typecasts
940a04f4d Added fuzzer
e4a200040 Merge pull request #896 from ayeganov/bugfix/ssl_leak
aefef8987 Free SSL object when redisSSLConnect fails
e3f88ebcf Merge pull request #894 from jcohen02/fix/issue893
308ffcab8 Updating SSL connection example
297f6551d Merge pull request #889 from redis/wincert
e7dda9785 Formatting
f44945a0a Merge pull request #874 from masariello/position-independent-code
74e78498c Merge pull request #888 from michael-grunder/nil-push-invalidation
b9b9f446f Fix handling of NIL invalidation messages.
acc917548 Merge pull request #885 from gkorland/patch-1
b086f763e clean a warning, remvoe empty else block
b47fae4e7 Merge pull request #881 from timgates42/bugfix_typo_terminated
f989670e5 docs: Fix simple typo, termined -> terminated
773d6ea8a Copy error to redisAsyncContext on timeout
e35300a66 add pdb files to packages for MSVC builds
dde6916b4 Add d suffix to debug libraries so that can packaged together with optimized builds (Release, RelWithDebInfo, etc)
3b68b5018 Enable position-independent code
6693863f4 Add support for system CA certificate store on Windows
2a5a57b90 Remove whitespace
1b40ec509 fixed issue with unit test linking on windows with SSL
d7b1d21e8 Merge branch 'master' of github.com:redis/hiredis
fb0e6c0dd Merge pull request #870 from michael-grunder/cmake-c99
13a35bdb6 Explicitly set c99 in CMake
bea137ca9 Merge pull request #868 from michael-grunder/fix-sockaddr-typo
bd6f86eb6 Fix sockaddr typo
48696e7e5 Don't use non-installed win32.h helper in examples (#863)
faa1c4863 Merge tag 'v1.0.0'
5003906d6 Define a no op assert if we detect NDEBUG (#861)
ea063b7cc Use development specific versions in master
04a27f480 We can run SSL tests everywhere except mingw/Windows (#859)
8966a1fc2 Remove extra whitespace (#858)
34b7f7a0f Keep libev's code style (#857)
07c3618ff Add static library target and cpack support
REVERT: 00272d669 Rename sds calls so they don't conflict in Redis.

git-subtree-dir: deps/hiredis
git-subtree-split: f8de9a4bd433791890572f7b9147e685653ddef9
parent bffbbeaa
name: Build and test
on: [push, pull_request]
jobs:
ubuntu:
name: Ubuntu
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
repository: ${{ env.GITHUB_REPOSITORY }}
ref: ${{ env.GITHUB_HEAD_REF }}
- name: Install dependencies
run: |
sudo add-apt-repository -y ppa:chris-lea/redis-server
sudo apt-get update
sudo apt-get install -y redis-server valgrind libevent-dev
- name: Build using cmake
env:
EXTRA_CMAKE_OPTS: -DENABLE_EXAMPLES:BOOL=ON -DENABLE_SSL:BOOL=ON -DENABLE_SSL_TESTS:BOOL=ON -DENABLE_ASYNC_TESTS:BOOL=ON
CFLAGS: -Werror
CXXFLAGS: -Werror
run: mkdir build-ubuntu && cd build-ubuntu && cmake ..
- name: Build using makefile
run: USE_SSL=1 TEST_ASYNC=1 make
- name: Run tests
env:
SKIPS_AS_FAILS: 1
TEST_SSL: 1
run: $GITHUB_WORKSPACE/test.sh
# - name: Run tests under valgrind
# env:
# SKIPS_AS_FAILS: 1
# TEST_PREFIX: valgrind --error-exitcode=99 --track-origins=yes --leak-check=full
# run: $GITHUB_WORKSPACE/test.sh
centos7:
name: CentOS 7
runs-on: ubuntu-latest
container: centos:7
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
repository: ${{ env.GITHUB_REPOSITORY }}
ref: ${{ env.GITHUB_HEAD_REF }}
- name: Install dependencies
run: |
yum -y install http://rpms.remirepo.net/enterprise/remi-release-7.rpm
yum -y --enablerepo=remi install redis
yum -y install gcc gcc-c++ make openssl openssl-devel cmake3 valgrind libevent-devel
- name: Build using cmake
env:
EXTRA_CMAKE_OPTS: -DENABLE_EXAMPLES:BOOL=ON -DENABLE_SSL:BOOL=ON -DENABLE_SSL_TESTS:BOOL=ON -DENABLE_ASYNC_TESTS:BOOL=ON
CFLAGS: -Werror
CXXFLAGS: -Werror
run: mkdir build-centos7 && cd build-centos7 && cmake3 ..
- name: Build using Makefile
run: USE_SSL=1 TEST_ASYNC=1 make
- name: Run tests
env:
SKIPS_AS_FAILS: 1
TEST_SSL: 1
run: $GITHUB_WORKSPACE/test.sh
- name: Run tests under valgrind
env:
SKIPS_AS_FAILS: 1
TEST_SSL: 1
TEST_PREFIX: valgrind --error-exitcode=99 --track-origins=yes --leak-check=full
run: $GITHUB_WORKSPACE/test.sh
centos8:
name: RockyLinux 8
runs-on: ubuntu-latest
container: rockylinux:8
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
repository: ${{ env.GITHUB_REPOSITORY }}
ref: ${{ env.GITHUB_HEAD_REF }}
- name: Install dependencies
run: |
dnf -y install https://rpms.remirepo.net/enterprise/remi-release-8.rpm
dnf -y module install redis:remi-6.0
dnf -y group install "Development Tools"
dnf -y install openssl-devel cmake valgrind libevent-devel
- name: Build using cmake
env:
EXTRA_CMAKE_OPTS: -DENABLE_EXAMPLES:BOOL=ON -DENABLE_SSL:BOOL=ON -DENABLE_SSL_TESTS:BOOL=ON -DENABLE_ASYNC_TESTS:BOOL=ON
CFLAGS: -Werror
CXXFLAGS: -Werror
run: mkdir build-centos8 && cd build-centos8 && cmake ..
- name: Build using Makefile
run: USE_SSL=1 TEST_ASYNC=1 make
- name: Run tests
env:
SKIPS_AS_FAILS: 1
TEST_SSL: 1
run: $GITHUB_WORKSPACE/test.sh
- name: Run tests under valgrind
env:
SKIPS_AS_FAILS: 1
TEST_SSL: 1
TEST_PREFIX: valgrind --error-exitcode=99 --track-origins=yes --leak-check=full
run: $GITHUB_WORKSPACE/test.sh
freebsd:
runs-on: macos-10.15
name: FreeBSD
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
repository: ${{ env.GITHUB_REPOSITORY }}
ref: ${{ env.GITHUB_HEAD_REF }}
- name: Build in FreeBSD
uses: vmactions/freebsd-vm@v0.1.5
with:
prepare: pkg install -y gmake cmake
run: |
mkdir build && cd build && cmake .. && make && cd ..
gmake
macos:
name: macOS
runs-on: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
repository: ${{ env.GITHUB_REPOSITORY }}
ref: ${{ env.GITHUB_HEAD_REF }}
- name: Install dependencies
run: |
brew install openssl redis
- name: Build hiredis
run: USE_SSL=1 make
- name: Run tests
env:
TEST_SSL: 1
run: $GITHUB_WORKSPACE/test.sh
windows:
name: Windows
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
repository: ${{ env.GITHUB_REPOSITORY }}
ref: ${{ env.GITHUB_HEAD_REF }}
- name: Install dependencies
run: |
choco install -y ninja memurai-developer
- uses: ilammy/msvc-dev-cmd@v1
- name: Build hiredis
run: |
mkdir build && cd build
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DENABLE_EXAMPLES=ON
ninja -v
- name: Run tests
run: |
./build/hiredis-test.exe
- name: Setup cygwin
uses: egor-tensin/setup-cygwin@v3
with:
platform: x64
packages: make git gcc-core
- name: Build in cygwin
env:
HIREDIS_PATH: ${{ github.workspace }}
run: |
build_hiredis() {
cd $(cygpath -u $HIREDIS_PATH)
git clean -xfd
make
}
build_hiredis
shell: C:\tools\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}'
...@@ -17,11 +17,11 @@ branches: ...@@ -17,11 +17,11 @@ branches:
- /^release\/.*$/ - /^release\/.*$/
install: install:
- if [ "$BITS" == "64" ]; then - if [ "$TRAVIS_COMPILER" != "mingw" ]; then
wget https://github.com/redis/redis/archive/6.0.6.tar.gz; wget https://github.com/redis/redis/archive/6.0.6.tar.gz;
tar -xzvf 6.0.6.tar.gz; tar -xzvf 6.0.6.tar.gz;
pushd redis-6.0.6 && BUILD_TLS=yes make && export PATH=$PWD/src:$PATH && popd; pushd redis-6.0.6 && BUILD_TLS=yes make && export PATH=$PWD/src:$PATH && popd;
fi fi;
before_script: before_script:
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then - if [ "$TRAVIS_OS_NAME" == "osx" ]; then
...@@ -33,8 +33,6 @@ before_script: ...@@ -33,8 +33,6 @@ before_script:
addons: addons:
apt: apt:
sources:
- sourceline: 'ppa:chris-lea/redis-server'
packages: packages:
- libc6-dbg - libc6-dbg
- libc6-dev - libc6-dev
...@@ -46,17 +44,13 @@ addons: ...@@ -46,17 +44,13 @@ addons:
- libssl-dev - libssl-dev
- libssl-dev:i386 - libssl-dev:i386
- valgrind - valgrind
- redis
env: env:
- BITS="32" - BITS="32"
- BITS="64" - BITS="64"
script: script:
- EXTRA_CMAKE_OPTS="-DENABLE_EXAMPLES:BOOL=ON -DENABLE_SSL:BOOL=ON"; - EXTRA_CMAKE_OPTS="-DENABLE_EXAMPLES:BOOL=ON -DENABLE_SSL:BOOL=ON -DENABLE_SSL_TESTS:BOOL=ON";
if [ "$BITS" == "64" ]; then
EXTRA_CMAKE_OPTS="$EXTRA_CMAKE_OPTS -DENABLE_SSL_TESTS:BOOL=ON";
fi;
if [ "$TRAVIS_OS_NAME" == "osx" ]; then if [ "$TRAVIS_OS_NAME" == "osx" ]; then
if [ "$BITS" == "32" ]; then if [ "$BITS" == "32" ]; then
CFLAGS="-m32 -Werror"; CFLAGS="-m32 -Werror";
......
## [1.0.2](https://github.com/redis/hiredis/tree/v1.0.2) - (2021-10-07)
Announcing Hiredis v1.0.2, which fixes CVE-2021-32765 but returns the SONAME to the correct value of `1.0.0`.
- [Revert SONAME bump](https://github.com/redis/hiredis/commit/d4e6f109a064690cde64765c654e679fea1d3548)
([Michael Grunder](https://github.com/michael-grunder))
## [1.0.1](https://github.com/redis/hiredis/tree/v1.0.1) - (2021-10-04)
<span style="color:red">This release erroneously bumped the SONAME, please use [1.0.2](https://github.com/redis/hiredis/tree/v1.0.2)</span>
Announcing Hiredis v1.0.1, a security release fixing CVE-2021-32765
- Fix for [CVE-2021-32765](https://github.com/redis/hiredis/security/advisories/GHSA-hfm9-39pp-55p2)
[commit](https://github.com/redis/hiredis/commit/76a7b10005c70babee357a7d0f2becf28ec7ed1e)
([Yossi Gottlieb](https://github.com/yossigo))
_Thanks to [Yossi Gottlieb](https://github.com/yossigo) for the security fix and to [Microsoft Security Vulnerability Research](https://www.microsoft.com/en-us/msrc/msvr) for finding the bug._ :sparkling_heart:
## [1.0.0](https://github.com/redis/hiredis/tree/v1.0.0) - (2020-08-03) ## [1.0.0](https://github.com/redis/hiredis/tree/v1.0.0) - (2020-08-03)
Announcing Hiredis v1.0.0, which adds support for RESP3, SSL connections, allocator injection, and better Windows support! :tada: Announcing Hiredis v1.0.0, which adds support for RESP3, SSL connections, allocator injection, and better Windows support! :tada:
......
CMAKE_MINIMUM_REQUIRED(VERSION 3.4.0) CMAKE_MINIMUM_REQUIRED(VERSION 3.4.0)
INCLUDE(GNUInstallDirs)
PROJECT(hiredis)
OPTION(ENABLE_SSL "Build hiredis_ssl for SSL support" OFF) OPTION(ENABLE_SSL "Build hiredis_ssl for SSL support" OFF)
OPTION(DISABLE_TESTS "If tests should be compiled or not" OFF) OPTION(DISABLE_TESTS "If tests should be compiled or not" OFF)
OPTION(ENABLE_SSL_TESTS, "Should we test SSL connections" OFF) OPTION(ENABLE_SSL_TESTS "Should we test SSL connections" OFF)
OPTION(ENABLE_ASYNC_TESTS "Should we run all asynchronous API tests" OFF)
MACRO(getVersionBit name) MACRO(getVersionBit name)
SET(VERSION_REGEX "^#define ${name} (.+)$") SET(VERSION_REGEX "^#define ${name} (.+)$")
...@@ -20,7 +19,13 @@ getVersionBit(HIREDIS_SONAME) ...@@ -20,7 +19,13 @@ getVersionBit(HIREDIS_SONAME)
SET(VERSION "${HIREDIS_MAJOR}.${HIREDIS_MINOR}.${HIREDIS_PATCH}") SET(VERSION "${HIREDIS_MAJOR}.${HIREDIS_MINOR}.${HIREDIS_PATCH}")
MESSAGE("Detected version: ${VERSION}") MESSAGE("Detected version: ${VERSION}")
PROJECT(hiredis VERSION "${VERSION}") PROJECT(hiredis LANGUAGES "C" VERSION "${VERSION}")
INCLUDE(GNUInstallDirs)
# Hiredis requires C99
SET(CMAKE_C_STANDARD 99)
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
SET(CMAKE_DEBUG_POSTFIX d)
SET(ENABLE_EXAMPLES OFF CACHE BOOL "Enable building hiredis examples") SET(ENABLE_EXAMPLES OFF CACHE BOOL "Enable building hiredis examples")
...@@ -41,30 +46,84 @@ IF(WIN32) ...@@ -41,30 +46,84 @@ IF(WIN32)
ENDIF() ENDIF()
ADD_LIBRARY(hiredis SHARED ${hiredis_sources}) ADD_LIBRARY(hiredis SHARED ${hiredis_sources})
ADD_LIBRARY(hiredis_static STATIC ${hiredis_sources})
ADD_LIBRARY(hiredis::hiredis ALIAS hiredis)
ADD_LIBRARY(hiredis::hiredis_static ALIAS hiredis_static)
SET_TARGET_PROPERTIES(hiredis SET_TARGET_PROPERTIES(hiredis
PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE
VERSION "${HIREDIS_SONAME}") VERSION "${HIREDIS_SONAME}")
SET_TARGET_PROPERTIES(hiredis_static
PROPERTIES COMPILE_PDB_NAME hiredis_static)
SET_TARGET_PROPERTIES(hiredis_static
PROPERTIES COMPILE_PDB_NAME_DEBUG hiredis_static${CMAKE_DEBUG_POSTFIX})
IF(WIN32 OR MINGW) IF(WIN32 OR MINGW)
TARGET_LINK_LIBRARIES(hiredis PRIVATE ws2_32) TARGET_LINK_LIBRARIES(hiredis PUBLIC ws2_32 crypt32)
TARGET_LINK_LIBRARIES(hiredis_static PUBLIC ws2_32 crypt32)
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
TARGET_LINK_LIBRARIES(hiredis PUBLIC m)
TARGET_LINK_LIBRARIES(hiredis_static PUBLIC m)
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "SunOS")
TARGET_LINK_LIBRARIES(hiredis PUBLIC socket)
TARGET_LINK_LIBRARIES(hiredis_static PUBLIC socket)
ENDIF() ENDIF()
TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC $<INSTALL_INTERFACE:.> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>) TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC $<INSTALL_INTERFACE:include> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
TARGET_INCLUDE_DIRECTORIES(hiredis_static PUBLIC $<INSTALL_INTERFACE:include> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
CONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY) CONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY)
INSTALL(TARGETS hiredis set(CPACK_PACKAGE_VENDOR "Redis")
set(CPACK_PACKAGE_DESCRIPTION "\
Hiredis is a minimalistic C client library for the Redis database.
It is minimalistic because it just adds minimal support for the protocol, \
but at the same time it uses a high level printf-alike API in order to make \
it much higher level than otherwise suggested by its minimal code base and the \
lack of explicit bindings for every Redis command.
Apart from supporting sending commands and receiving replies, it comes with a \
reply parser that is decoupled from the I/O layer. It is a stream parser designed \
for easy reusability, which can for instance be used in higher level language bindings \
for efficient reply parsing.
Hiredis only supports the binary-safe Redis protocol, so you can use it with any Redis \
version >= 1.2.0.
The library comes with multiple APIs. There is the synchronous API, the asynchronous API \
and the reply parsing API.")
set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/redis/hiredis")
set(CPACK_PACKAGE_CONTACT "michael dot grunder at gmail dot com")
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_RPM_PACKAGE_AUTOREQPROV ON)
include(CPack)
INSTALL(TARGETS hiredis hiredis_static
EXPORT hiredis-targets EXPORT hiredis-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
if (MSVC)
INSTALL(FILES $<TARGET_PDB_FILE:hiredis>
DESTINATION ${CMAKE_INSTALL_BINDIR}
CONFIGURATIONS Debug RelWithDebInfo)
INSTALL(FILES $<TARGET_FILE_DIR:hiredis_static>/$<TARGET_FILE_BASE_NAME:hiredis_static>.pdb
DESTINATION ${CMAKE_INSTALL_LIBDIR}
CONFIGURATIONS Debug RelWithDebInfo)
endif()
# For NuGet packages
INSTALL(FILES hiredis.targets
DESTINATION build/native)
INSTALL(FILES hiredis.h read.h sds.h async.h alloc.h INSTALL(FILES hiredis.h read.h sds.h async.h alloc.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis) DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
INSTALL(DIRECTORY adapters INSTALL(DIRECTORY adapters
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis) DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis.pc INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
...@@ -95,10 +154,12 @@ IF(ENABLE_SSL) ...@@ -95,10 +154,12 @@ IF(ENABLE_SSL)
ENDIF() ENDIF()
ENDIF() ENDIF()
FIND_PACKAGE(OpenSSL REQUIRED) FIND_PACKAGE(OpenSSL REQUIRED)
SET(hiredis_ssl_sources SET(hiredis_ssl_sources
ssl.c) ssl.c)
ADD_LIBRARY(hiredis_ssl SHARED ADD_LIBRARY(hiredis_ssl SHARED
${hiredis_ssl_sources}) ${hiredis_ssl_sources})
ADD_LIBRARY(hiredis_ssl_static STATIC
${hiredis_ssl_sources})
IF (APPLE) IF (APPLE)
SET_PROPERTY(TARGET hiredis_ssl PROPERTY LINK_FLAGS "-Wl,-undefined -Wl,dynamic_lookup") SET_PROPERTY(TARGET hiredis_ssl PROPERTY LINK_FLAGS "-Wl,-undefined -Wl,dynamic_lookup")
...@@ -108,23 +169,39 @@ IF(ENABLE_SSL) ...@@ -108,23 +169,39 @@ IF(ENABLE_SSL)
PROPERTIES PROPERTIES
WINDOWS_EXPORT_ALL_SYMBOLS TRUE WINDOWS_EXPORT_ALL_SYMBOLS TRUE
VERSION "${HIREDIS_SONAME}") VERSION "${HIREDIS_SONAME}")
SET_TARGET_PROPERTIES(hiredis_ssl_static
PROPERTIES COMPILE_PDB_NAME hiredis_ssl_static)
SET_TARGET_PROPERTIES(hiredis_ssl_static
PROPERTIES COMPILE_PDB_NAME_DEBUG hiredis_ssl_static${CMAKE_DEBUG_POSTFIX})
TARGET_INCLUDE_DIRECTORIES(hiredis_ssl PRIVATE "${OPENSSL_INCLUDE_DIR}") TARGET_INCLUDE_DIRECTORIES(hiredis_ssl PRIVATE "${OPENSSL_INCLUDE_DIR}")
TARGET_INCLUDE_DIRECTORIES(hiredis_ssl_static PRIVATE "${OPENSSL_INCLUDE_DIR}")
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE ${OPENSSL_LIBRARIES}) TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE ${OPENSSL_LIBRARIES})
IF (WIN32 OR MINGW) IF (WIN32 OR MINGW)
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE hiredis) TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE hiredis)
TARGET_LINK_LIBRARIES(hiredis_ssl_static PUBLIC hiredis_static)
ENDIF() ENDIF()
CONFIGURE_FILE(hiredis_ssl.pc.in hiredis_ssl.pc @ONLY) CONFIGURE_FILE(hiredis_ssl.pc.in hiredis_ssl.pc @ONLY)
INSTALL(TARGETS hiredis_ssl INSTALL(TARGETS hiredis_ssl hiredis_ssl_static
EXPORT hiredis_ssl-targets EXPORT hiredis_ssl-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
if (MSVC)
INSTALL(FILES $<TARGET_PDB_FILE:hiredis_ssl>
DESTINATION ${CMAKE_INSTALL_BINDIR}
CONFIGURATIONS Debug RelWithDebInfo)
INSTALL(FILES $<TARGET_FILE_DIR:hiredis_ssl_static>/$<TARGET_FILE_BASE_NAME:hiredis_ssl_static>.pdb
DESTINATION ${CMAKE_INSTALL_LIBDIR}
CONFIGURATIONS Debug RelWithDebInfo)
endif()
INSTALL(FILES hiredis_ssl.h INSTALL(FILES hiredis_ssl.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis) DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl.pc INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
...@@ -149,11 +226,14 @@ ENDIF() ...@@ -149,11 +226,14 @@ ENDIF()
IF(NOT DISABLE_TESTS) IF(NOT DISABLE_TESTS)
ENABLE_TESTING() ENABLE_TESTING()
ADD_EXECUTABLE(hiredis-test test.c) ADD_EXECUTABLE(hiredis-test test.c)
TARGET_LINK_LIBRARIES(hiredis-test hiredis)
IF(ENABLE_SSL_TESTS) IF(ENABLE_SSL_TESTS)
ADD_DEFINITIONS(-DHIREDIS_TEST_SSL=1) ADD_DEFINITIONS(-DHIREDIS_TEST_SSL=1)
TARGET_LINK_LIBRARIES(hiredis-test hiredis hiredis_ssl) TARGET_LINK_LIBRARIES(hiredis-test hiredis_ssl)
ELSE() ENDIF()
TARGET_LINK_LIBRARIES(hiredis-test hiredis) IF(ENABLE_ASYNC_TESTS)
ADD_DEFINITIONS(-DHIREDIS_TEST_ASYNC=1)
TARGET_LINK_LIBRARIES(hiredis-test event)
ENDIF() ENDIF()
ADD_TEST(NAME hiredis-test ADD_TEST(NAME hiredis-test
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh) COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh)
......
...@@ -4,16 +4,10 @@ ...@@ -4,16 +4,10 @@
# This file is released under the BSD license, see the COPYING file # This file is released under the BSD license, see the COPYING file
OBJ=alloc.o net.o hiredis.o sds.o async.o read.o sockcompat.o OBJ=alloc.o net.o hiredis.o sds.o async.o read.o sockcompat.o
SSL_OBJ=ssl.o
EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib hiredis-example-push EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib hiredis-example-push
ifeq ($(USE_SSL),1)
EXAMPLES+=hiredis-example-ssl hiredis-example-libevent-ssl
endif
TESTS=hiredis-test TESTS=hiredis-test
LIBNAME=libhiredis LIBNAME=libhiredis
PKGCONFNAME=hiredis.pc PKGCONFNAME=hiredis.pc
SSL_LIBNAME=libhiredis_ssl
SSL_PKGCONFNAME=hiredis_ssl.pc
HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}') HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}')
HIREDIS_MINOR=$(shell grep HIREDIS_MINOR hiredis.h | awk '{print $$3}') HIREDIS_MINOR=$(shell grep HIREDIS_MINOR hiredis.h | awk '{print $$3}')
...@@ -60,28 +54,66 @@ DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) ...@@ -60,28 +54,66 @@ DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME)
STLIBNAME=$(LIBNAME).$(STLIBSUFFIX) STLIBNAME=$(LIBNAME).$(STLIBSUFFIX)
STLIB_MAKE_CMD=$(AR) rcs STLIB_MAKE_CMD=$(AR) rcs
#################### SSL variables start ####################
SSL_OBJ=ssl.o
SSL_LIBNAME=libhiredis_ssl
SSL_PKGCONFNAME=hiredis_ssl.pc
SSL_INSTALLNAME=install-ssl
SSL_DYLIB_MINOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME) SSL_DYLIB_MINOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME)
SSL_DYLIB_MAJOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR) SSL_DYLIB_MAJOR_NAME=$(SSL_LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
SSL_DYLIBNAME=$(SSL_LIBNAME).$(DYLIBSUFFIX) SSL_DYLIBNAME=$(SSL_LIBNAME).$(DYLIBSUFFIX)
SSL_STLIBNAME=$(SSL_LIBNAME).$(STLIBSUFFIX) SSL_STLIBNAME=$(SSL_LIBNAME).$(STLIBSUFFIX)
SSL_DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(SSL_DYLIB_MINOR_NAME) SSL_DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(SSL_DYLIB_MINOR_NAME)
USE_SSL?=0
ifeq ($(USE_SSL),1)
# This is required for test.c only
CFLAGS+=-DHIREDIS_TEST_SSL
EXAMPLES+=hiredis-example-ssl hiredis-example-libevent-ssl
SSL_STLIB=$(SSL_STLIBNAME)
SSL_DYLIB=$(SSL_DYLIBNAME)
SSL_PKGCONF=$(SSL_PKGCONFNAME)
SSL_INSTALL=$(SSL_INSTALLNAME)
else
SSL_STLIB=
SSL_DYLIB=
SSL_PKGCONF=
SSL_INSTALL=
endif
##################### SSL variables end #####################
# Platform-specific overrides # Platform-specific overrides
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
USE_SSL?=0
# This is required for test.c only # This is required for test.c only
ifeq ($(TEST_ASYNC),1)
export CFLAGS+=-DHIREDIS_TEST_ASYNC
endif
ifeq ($(USE_SSL),1) ifeq ($(USE_SSL),1)
CFLAGS+=-DHIREDIS_TEST_SSL ifeq ($(uname_S),Linux)
ifdef OPENSSL_PREFIX
CFLAGS+=-I$(OPENSSL_PREFIX)/include
SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto
else
SSL_LDFLAGS=-lssl -lcrypto
endif
else
OPENSSL_PREFIX?=/usr/local/opt/openssl
CFLAGS+=-I$(OPENSSL_PREFIX)/include
SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto
endif
endif endif
ifeq ($(uname_S),Linux) ifeq ($(uname_S),FreeBSD)
SSL_LDFLAGS=-lssl -lcrypto LDFLAGS+=-lm
IS_GCC=$(shell sh -c '$(CC) --version 2>/dev/null |egrep -i -c "gcc"')
ifeq ($(IS_GCC),1)
REAL_CFLAGS+=-pedantic
endif
else else
OPENSSL_PREFIX?=/usr/local/opt/openssl REAL_CFLAGS+=-pedantic
CFLAGS+=-I$(OPENSSL_PREFIX)/include
SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto
endif endif
ifeq ($(uname_S),SunOS) ifeq ($(uname_S),SunOS)
...@@ -103,10 +135,13 @@ ifeq ($(uname_S),Darwin) ...@@ -103,10 +135,13 @@ ifeq ($(uname_S),Darwin)
DYLIB_PLUGIN=-Wl,-undefined -Wl,dynamic_lookup DYLIB_PLUGIN=-Wl,-undefined -Wl,dynamic_lookup
endif endif
all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME) all: dynamic static hiredis-test pkgconfig
ifeq ($(USE_SSL),1)
all: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME) dynamic: $(DYLIBNAME) $(SSL_DYLIB)
endif
static: $(STLIBNAME) $(SSL_STLIB)
pkgconfig: $(PKGCONFNAME) $(SSL_PKGCONF)
# Deps (use make dep to generate this) # Deps (use make dep to generate this)
alloc.o: alloc.c fmacros.h alloc.h alloc.o: alloc.c fmacros.h alloc.h
...@@ -117,7 +152,6 @@ net.o: net.c fmacros.h net.h hiredis.h read.h sds.h alloc.h sockcompat.h win32.h ...@@ -117,7 +152,6 @@ net.o: net.c fmacros.h net.h hiredis.h read.h sds.h alloc.h sockcompat.h win32.h
read.o: read.c fmacros.h alloc.h read.h sds.h win32.h read.o: read.c fmacros.h alloc.h read.h sds.h win32.h
sds.o: sds.c sds.h sdsalloc.h alloc.h sds.o: sds.c sds.h sdsalloc.h alloc.h
sockcompat.o: sockcompat.c sockcompat.h sockcompat.o: sockcompat.c sockcompat.h
ssl.o: ssl.c hiredis.h read.h sds.h alloc.h async.h win32.h async_private.h
test.o: test.c fmacros.h hiredis.h read.h sds.h alloc.h net.h sockcompat.h win32.h test.o: test.c fmacros.h hiredis.h read.h sds.h alloc.h net.h sockcompat.h win32.h
$(DYLIBNAME): $(OBJ) $(DYLIBNAME): $(OBJ)
...@@ -126,18 +160,15 @@ $(DYLIBNAME): $(OBJ) ...@@ -126,18 +160,15 @@ $(DYLIBNAME): $(OBJ)
$(STLIBNAME): $(OBJ) $(STLIBNAME): $(OBJ)
$(STLIB_MAKE_CMD) $(STLIBNAME) $(OBJ) $(STLIB_MAKE_CMD) $(STLIBNAME) $(OBJ)
#################### SSL building rules start ####################
$(SSL_DYLIBNAME): $(SSL_OBJ) $(SSL_DYLIBNAME): $(SSL_OBJ)
$(SSL_DYLIB_MAKE_CMD) $(DYLIB_PLUGIN) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(LDFLAGS) $(SSL_LDFLAGS) $(SSL_DYLIB_MAKE_CMD) $(DYLIB_PLUGIN) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(LDFLAGS) $(SSL_LDFLAGS)
$(SSL_STLIBNAME): $(SSL_OBJ) $(SSL_STLIBNAME): $(SSL_OBJ)
$(STLIB_MAKE_CMD) $(SSL_STLIBNAME) $(SSL_OBJ) $(STLIB_MAKE_CMD) $(SSL_STLIBNAME) $(SSL_OBJ)
dynamic: $(DYLIBNAME) $(SSL_OBJ): ssl.c hiredis.h read.h sds.h alloc.h async.h win32.h async_private.h
static: $(STLIBNAME) #################### SSL building rules end ####################
ifeq ($(USE_SSL),1)
dynamic: $(SSL_DYLIBNAME)
static: $(SSL_STLIBNAME)
endif
# Binaries: # Binaries:
hiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME) hiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME)
...@@ -161,7 +192,6 @@ hiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME) ...@@ -161,7 +192,6 @@ hiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME)
hiredis-example-ssl: examples/example-ssl.c $(STLIBNAME) $(SSL_STLIBNAME) hiredis-example-ssl: examples/example-ssl.c $(STLIBNAME) $(SSL_STLIBNAME)
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS) $(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS)
ifndef AE_DIR ifndef AE_DIR
hiredis-example-ae: hiredis-example-ae:
@echo "Please specify AE_DIR (e.g. <redis repository>/src)" @echo "Please specify AE_DIR (e.g. <redis repository>/src)"
...@@ -172,10 +202,11 @@ hiredis-example-ae: examples/example-ae.c adapters/ae.h $(STLIBNAME) ...@@ -172,10 +202,11 @@ hiredis-example-ae: examples/example-ae.c adapters/ae.h $(STLIBNAME)
endif endif
ifndef LIBUV_DIR ifndef LIBUV_DIR
hiredis-example-libuv: # dynamic link libuv.so
@echo "Please specify LIBUV_DIR (e.g. ../libuv/)" hiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME)
@false $(CC) -o examples/$@ $(REAL_CFLAGS) -I. -I$(LIBUV_DIR)/include $< -luv -lpthread -lrt $(STLIBNAME) $(REAL_LDFLAGS)
else else
# use user provided static lib
hiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME) hiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME)
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread -lrt $(STLIBNAME) $(REAL_LDFLAGS) $(CC) -o examples/$@ $(REAL_CFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread -lrt $(STLIBNAME) $(REAL_LDFLAGS)
endif endif
...@@ -201,10 +232,13 @@ hiredis-example-push: examples/example-push.c $(STLIBNAME) ...@@ -201,10 +232,13 @@ hiredis-example-push: examples/example-push.c $(STLIBNAME)
examples: $(EXAMPLES) examples: $(EXAMPLES)
TEST_LIBS = $(STLIBNAME) TEST_LIBS = $(STLIBNAME) $(SSL_STLIB)
TEST_LDFLAGS = $(SSL_LDFLAGS)
ifeq ($(USE_SSL),1) ifeq ($(USE_SSL),1)
TEST_LIBS += $(SSL_STLIBNAME) TEST_LDFLAGS += -pthread
TEST_LDFLAGS = $(SSL_LDFLAGS) -lssl -lcrypto -lpthread endif
ifeq ($(TEST_ASYNC),1)
TEST_LDFLAGS += -levent
endif endif
hiredis-test: test.o $(TEST_LIBS) hiredis-test: test.o $(TEST_LIBS)
...@@ -220,7 +254,7 @@ check: hiredis-test ...@@ -220,7 +254,7 @@ check: hiredis-test
TEST_SSL=$(USE_SSL) ./test.sh TEST_SSL=$(USE_SSL) ./test.sh
.c.o: .c.o:
$(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $< $(CC) -std=c99 -c $(REAL_CFLAGS) $<
clean: clean:
rm -rf $(DYLIBNAME) $(STLIBNAME) $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov rm -rf $(DYLIBNAME) $(STLIBNAME) $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov
...@@ -257,7 +291,7 @@ $(SSL_PKGCONFNAME): hiredis_ssl.h ...@@ -257,7 +291,7 @@ $(SSL_PKGCONFNAME): hiredis_ssl.h
@echo Libs: -L\$${libdir} -lhiredis_ssl >> $@ @echo Libs: -L\$${libdir} -lhiredis_ssl >> $@
@echo Libs.private: -lssl -lcrypto >> $@ @echo Libs.private: -lssl -lcrypto >> $@
install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME) install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME) $(SSL_INSTALL)
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH) mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH)
$(INSTALL) hiredis.h async.h read.h sds.h alloc.h $(INSTALL_INCLUDE_PATH) $(INSTALL) hiredis.h async.h read.h sds.h alloc.h $(INSTALL_INCLUDE_PATH)
$(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters
...@@ -267,9 +301,6 @@ install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME) ...@@ -267,9 +301,6 @@ install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME)
mkdir -p $(INSTALL_PKGCONF_PATH) mkdir -p $(INSTALL_PKGCONF_PATH)
$(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH) $(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH)
ifeq ($(USE_SSL),1)
install: install-ssl
install-ssl: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME) install-ssl: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME)
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH) mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH)
$(INSTALL) hiredis_ssl.h $(INSTALL_INCLUDE_PATH) $(INSTALL) hiredis_ssl.h $(INSTALL_INCLUDE_PATH)
...@@ -278,7 +309,6 @@ install-ssl: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME) ...@@ -278,7 +309,6 @@ install-ssl: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME)
$(INSTALL) $(SSL_STLIBNAME) $(INSTALL_LIBRARY_PATH) $(INSTALL) $(SSL_STLIBNAME) $(INSTALL_LIBRARY_PATH)
mkdir -p $(INSTALL_PKGCONF_PATH) mkdir -p $(INSTALL_PKGCONF_PATH)
$(INSTALL) $(SSL_PKGCONFNAME) $(INSTALL_PKGCONF_PATH) $(INSTALL) $(SSL_PKGCONFNAME) $(INSTALL_PKGCONF_PATH)
endif
32bit: 32bit:
@echo "" @echo ""
...@@ -294,12 +324,12 @@ gprof: ...@@ -294,12 +324,12 @@ gprof:
$(MAKE) CFLAGS="-pg" LDFLAGS="-pg" $(MAKE) CFLAGS="-pg" LDFLAGS="-pg"
gcov: gcov:
$(MAKE) CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs" $(MAKE) CFLAGS+="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs"
coverage: gcov coverage: gcov
make check make check
mkdir -p tmp/lcov mkdir -p tmp/lcov
lcov -d . -c -o tmp/lcov/hiredis.info lcov -d . -c --exclude '/usr*' -o tmp/lcov/hiredis.info
genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info
noopt: noopt:
......
[![Build Status](https://travis-ci.org/redis/hiredis.png)](https://travis-ci.org/redis/hiredis)
[![Build Status](https://github.com/redis/hiredis/actions/workflows/build.yml/badge.svg)](https://github.com/redis/hiredis/actions/workflows/build.yml)
**This Readme reflects the latest changed in the master branch. See [v1.0.0](https://github.com/redis/hiredis/tree/v1.0.0) for the Readme and documentation for the latest release ([API/ABI history](https://abi-laboratory.pro/?view=timeline&l=hiredis)).** **This Readme reflects the latest changed in the master branch. See [v1.0.0](https://github.com/redis/hiredis/tree/v1.0.0) for the Readme and documentation for the latest release ([API/ABI history](https://abi-laboratory.pro/?view=timeline&l=hiredis)).**
# HIREDIS # HIREDIS
Hiredis is a minimalistic C client library for the [Redis](http://redis.io/) database. Hiredis is a minimalistic C client library for the [Redis](https://redis.io/) database.
It is minimalistic because it just adds minimal support for the protocol, but It is minimalistic because it just adds minimal support for the protocol, but
at the same time it uses a high level printf-alike API in order to make it at the same time it uses a high level printf-alike API in order to make it
...@@ -22,6 +23,12 @@ Redis version >= 1.2.0. ...@@ -22,6 +23,12 @@ Redis version >= 1.2.0.
The library comes with multiple APIs. There is the The library comes with multiple APIs. There is the
*synchronous API*, the *asynchronous API* and the *reply parsing API*. *synchronous API*, the *asynchronous API* and the *reply parsing API*.
## Upgrading to `1.0.2`
<span style="color:red">NOTE: v1.0.1 erroneously bumped SONAME, which is why it is skipped here.</span>
Version 1.0.2 is simply 1.0.0 with a fix for [CVE-2021-32765](https://github.com/redis/hiredis/security/advisories/GHSA-hfm9-39pp-55p2). They are otherwise identical.
## Upgrading to `1.0.0` ## Upgrading to `1.0.0`
Version 1.0.0 marks the first stable release of Hiredis. Version 1.0.0 marks the first stable release of Hiredis.
...@@ -169,7 +176,7 @@ Hiredis also supports every new `RESP3` data type which are as follows. For mor ...@@ -169,7 +176,7 @@ Hiredis also supports every new `RESP3` data type which are as follows. For mor
* **`REDIS_REPLY_MAP`**: * **`REDIS_REPLY_MAP`**:
* An array with the added invariant that there will always be an even number of elements. * An array with the added invariant that there will always be an even number of elements.
The MAP is functionally equivelant to `REDIS_REPLY_ARRAY` except for the previously mentioned invariant. The MAP is functionally equivalent to `REDIS_REPLY_ARRAY` except for the previously mentioned invariant.
* **`REDIS_REPLY_SET`**: * **`REDIS_REPLY_SET`**:
* An array response where each entry is unique. * An array response where each entry is unique.
...@@ -189,7 +196,7 @@ Hiredis also supports every new `RESP3` data type which are as follows. For mor ...@@ -189,7 +196,7 @@ Hiredis also supports every new `RESP3` data type which are as follows. For mor
* **`REDIS_REPLY_VERB`**: * **`REDIS_REPLY_VERB`**:
* A verbatim string, intended to be presented to the user without modification. * A verbatim string, intended to be presented to the user without modification.
The string payload is stored in the `str` memeber, and type data is stored in the `vtype` member (e.g. `txt` for raw text or `md` for markdown). The string payload is stored in the `str` member, and type data is stored in the `vtype` member (e.g. `txt` for raw text or `md` for markdown).
Replies should be freed using the `freeReplyObject()` function. Replies should be freed using the `freeReplyObject()` function.
Note that this function will take care of freeing sub-reply objects Note that this function will take care of freeing sub-reply objects
...@@ -261,9 +268,9 @@ a single call to `read(2)`): ...@@ -261,9 +268,9 @@ a single call to `read(2)`):
redisReply *reply; redisReply *reply;
redisAppendCommand(context,"SET foo bar"); redisAppendCommand(context,"SET foo bar");
redisAppendCommand(context,"GET foo"); redisAppendCommand(context,"GET foo");
redisGetReply(context,(void *)&reply); // reply for SET redisGetReply(context,(void**)&reply); // reply for SET
freeReplyObject(reply); freeReplyObject(reply);
redisGetReply(context,(void *)&reply); // reply for GET redisGetReply(context,(void**)&reply); // reply for GET
freeReplyObject(reply); freeReplyObject(reply);
``` ```
This API can also be used to implement a blocking subscriber: This API can also be used to implement a blocking subscriber:
...@@ -517,7 +524,7 @@ initialize OpenSSL and create a context. You can do that in two ways: ...@@ -517,7 +524,7 @@ initialize OpenSSL and create a context. You can do that in two ways:
/* An Hiredis SSL context. It holds SSL configuration and can be reused across /* An Hiredis SSL context. It holds SSL configuration and can be reused across
* many contexts. * many contexts.
*/ */
redisSSLContext *ssl; redisSSLContext *ssl_context;
/* An error variable to indicate what went wrong, if the context fails to /* An error variable to indicate what went wrong, if the context fails to
* initialize. * initialize.
...@@ -532,17 +539,23 @@ redisSSLContextError ssl_error; ...@@ -532,17 +539,23 @@ redisSSLContextError ssl_error;
redisInitOpenSSL(); redisInitOpenSSL();
/* Create SSL context */ /* Create SSL context */
ssl = redisCreateSSLContext( ssl_context = redisCreateSSLContext(
"cacertbundle.crt", /* File name of trusted CA/ca bundle file, optional */ "cacertbundle.crt", /* File name of trusted CA/ca bundle file, optional */
"/path/to/certs", /* Path of trusted certificates, optional */ "/path/to/certs", /* Path of trusted certificates, optional */
"client_cert.pem", /* File name of client certificate file, optional */ "client_cert.pem", /* File name of client certificate file, optional */
"client_key.pem", /* File name of client private key, optional */ "client_key.pem", /* File name of client private key, optional */
"redis.mydomain.com", /* Server name to request (SNI), optional */ "redis.mydomain.com", /* Server name to request (SNI), optional */
&ssl_error &ssl_error);
) != REDIS_OK) {
printf("SSL error: %s\n", redisSSLContextGetError(ssl_error); if(ssl_context == NULL || ssl_error != 0) {
/* Abort... */ /* Handle error and abort... */
} /* e.g.
printf("SSL error: %s\n",
(ssl_error != 0) ?
redisSSLContextGetError(ssl_error) : "Unknown error");
// Abort
*/
}
/* Create Redis context and establish connection */ /* Create Redis context and establish connection */
c = redisConnect("localhost", 6443); c = redisConnect("localhost", 6443);
...@@ -551,7 +564,7 @@ if (c == NULL || c->err) { ...@@ -551,7 +564,7 @@ if (c == NULL || c->err) {
} }
/* Negotiate SSL/TLS */ /* Negotiate SSL/TLS */
if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) { if (redisInitiateSSLWithContext(c, ssl_context) != REDIS_OK) {
/* Handle error, in c->err / c->errstr */ /* Handle error, in c->err / c->errstr */
} }
``` ```
......
...@@ -46,7 +46,7 @@ typedef struct redisLibevEvents { ...@@ -46,7 +46,7 @@ typedef struct redisLibevEvents {
static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) { static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
#if EV_MULTIPLICITY #if EV_MULTIPLICITY
((void)loop); ((void)EV_A);
#endif #endif
((void)revents); ((void)revents);
...@@ -56,7 +56,7 @@ static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) { ...@@ -56,7 +56,7 @@ static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) { static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
#if EV_MULTIPLICITY #if EV_MULTIPLICITY
((void)loop); ((void)EV_A);
#endif #endif
((void)revents); ((void)revents);
...@@ -66,8 +66,9 @@ static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) { ...@@ -66,8 +66,9 @@ static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
static void redisLibevAddRead(void *privdata) { static void redisLibevAddRead(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
#if EV_MULTIPLICITY
struct ev_loop *loop = e->loop; struct ev_loop *loop = e->loop;
((void)loop); #endif
if (!e->reading) { if (!e->reading) {
e->reading = 1; e->reading = 1;
ev_io_start(EV_A_ &e->rev); ev_io_start(EV_A_ &e->rev);
...@@ -76,8 +77,9 @@ static void redisLibevAddRead(void *privdata) { ...@@ -76,8 +77,9 @@ static void redisLibevAddRead(void *privdata) {
static void redisLibevDelRead(void *privdata) { static void redisLibevDelRead(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
#if EV_MULTIPLICITY
struct ev_loop *loop = e->loop; struct ev_loop *loop = e->loop;
((void)loop); #endif
if (e->reading) { if (e->reading) {
e->reading = 0; e->reading = 0;
ev_io_stop(EV_A_ &e->rev); ev_io_stop(EV_A_ &e->rev);
...@@ -86,8 +88,9 @@ static void redisLibevDelRead(void *privdata) { ...@@ -86,8 +88,9 @@ static void redisLibevDelRead(void *privdata) {
static void redisLibevAddWrite(void *privdata) { static void redisLibevAddWrite(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
#if EV_MULTIPLICITY
struct ev_loop *loop = e->loop; struct ev_loop *loop = e->loop;
((void)loop); #endif
if (!e->writing) { if (!e->writing) {
e->writing = 1; e->writing = 1;
ev_io_start(EV_A_ &e->wev); ev_io_start(EV_A_ &e->wev);
...@@ -96,8 +99,9 @@ static void redisLibevAddWrite(void *privdata) { ...@@ -96,8 +99,9 @@ static void redisLibevAddWrite(void *privdata) {
static void redisLibevDelWrite(void *privdata) { static void redisLibevDelWrite(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
#if EV_MULTIPLICITY
struct ev_loop *loop = e->loop; struct ev_loop *loop = e->loop;
((void)loop); #endif
if (e->writing) { if (e->writing) {
e->writing = 0; e->writing = 0;
ev_io_stop(EV_A_ &e->wev); ev_io_stop(EV_A_ &e->wev);
...@@ -106,8 +110,9 @@ static void redisLibevDelWrite(void *privdata) { ...@@ -106,8 +110,9 @@ static void redisLibevDelWrite(void *privdata) {
static void redisLibevStopTimer(void *privdata) { static void redisLibevStopTimer(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
#if EV_MULTIPLICITY
struct ev_loop *loop = e->loop; struct ev_loop *loop = e->loop;
((void)loop); #endif
ev_timer_stop(EV_A_ &e->timer); ev_timer_stop(EV_A_ &e->timer);
} }
...@@ -120,6 +125,9 @@ static void redisLibevCleanup(void *privdata) { ...@@ -120,6 +125,9 @@ static void redisLibevCleanup(void *privdata) {
} }
static void redisLibevTimeout(EV_P_ ev_timer *timer, int revents) { static void redisLibevTimeout(EV_P_ ev_timer *timer, int revents) {
#if EV_MULTIPLICITY
((void)EV_A);
#endif
((void)revents); ((void)revents);
redisLibevEvents *e = (redisLibevEvents*)timer->data; redisLibevEvents *e = (redisLibevEvents*)timer->data;
redisAsyncHandleTimeout(e->context); redisAsyncHandleTimeout(e->context);
...@@ -127,8 +135,9 @@ static void redisLibevTimeout(EV_P_ ev_timer *timer, int revents) { ...@@ -127,8 +135,9 @@ static void redisLibevTimeout(EV_P_ ev_timer *timer, int revents) {
static void redisLibevSetTimeout(void *privdata, struct timeval tv) { static void redisLibevSetTimeout(void *privdata, struct timeval tv) {
redisLibevEvents *e = (redisLibevEvents*)privdata; redisLibevEvents *e = (redisLibevEvents*)privdata;
#if EV_MULTIPLICITY
struct ev_loop *loop = e->loop; struct ev_loop *loop = e->loop;
((void)loop); #endif
if (!ev_is_active(&e->timer)) { if (!ev_is_active(&e->timer)) {
ev_init(&e->timer, redisLibevTimeout); ev_init(&e->timer, redisLibevTimeout);
...@@ -154,7 +163,7 @@ static int redisLibevAttach(EV_P_ redisAsyncContext *ac) { ...@@ -154,7 +163,7 @@ static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
e->context = ac; e->context = ac;
#if EV_MULTIPLICITY #if EV_MULTIPLICITY
e->loop = loop; e->loop = EV_A;
#else #else
e->loop = NULL; e->loop = NULL;
#endif #endif
......
...@@ -50,7 +50,7 @@ static void redisLibeventDestroy(redisLibeventEvents *e) { ...@@ -50,7 +50,7 @@ static void redisLibeventDestroy(redisLibeventEvents *e) {
hi_free(e); hi_free(e);
} }
static void redisLibeventHandler(int fd, short event, void *arg) { static void redisLibeventHandler(evutil_socket_t fd, short event, void *arg) {
((void)fd); ((void)fd);
redisLibeventEvents *e = (redisLibeventEvents*)arg; redisLibeventEvents *e = (redisLibeventEvents*)arg;
e->state |= REDIS_LIBEVENT_ENTERED; e->state |= REDIS_LIBEVENT_ENTERED;
......
...@@ -7,111 +7,157 @@ ...@@ -7,111 +7,157 @@
#include <string.h> #include <string.h>
typedef struct redisLibuvEvents { typedef struct redisLibuvEvents {
redisAsyncContext* context; redisAsyncContext* context;
uv_poll_t handle; uv_poll_t handle;
int events; uv_timer_t timer;
int events;
} redisLibuvEvents; } redisLibuvEvents;
static void redisLibuvPoll(uv_poll_t* handle, int status, int events) { static void redisLibuvPoll(uv_poll_t* handle, int status, int events) {
redisLibuvEvents* p = (redisLibuvEvents*)handle->data; redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
int ev = (status ? p->events : events); int ev = (status ? p->events : events);
if (p->context != NULL && (ev & UV_READABLE)) { if (p->context != NULL && (ev & UV_READABLE)) {
redisAsyncHandleRead(p->context); redisAsyncHandleRead(p->context);
} }
if (p->context != NULL && (ev & UV_WRITABLE)) { if (p->context != NULL && (ev & UV_WRITABLE)) {
redisAsyncHandleWrite(p->context); redisAsyncHandleWrite(p->context);
} }
} }
static void redisLibuvAddRead(void *privdata) { static void redisLibuvAddRead(void *privdata) {
redisLibuvEvents* p = (redisLibuvEvents*)privdata; redisLibuvEvents* p = (redisLibuvEvents*)privdata;
p->events |= UV_READABLE; p->events |= UV_READABLE;
uv_poll_start(&p->handle, p->events, redisLibuvPoll); uv_poll_start(&p->handle, p->events, redisLibuvPoll);
} }
static void redisLibuvDelRead(void *privdata) { static void redisLibuvDelRead(void *privdata) {
redisLibuvEvents* p = (redisLibuvEvents*)privdata; redisLibuvEvents* p = (redisLibuvEvents*)privdata;
p->events &= ~UV_READABLE; p->events &= ~UV_READABLE;
if (p->events) { if (p->events) {
uv_poll_start(&p->handle, p->events, redisLibuvPoll); uv_poll_start(&p->handle, p->events, redisLibuvPoll);
} else { } else {
uv_poll_stop(&p->handle); uv_poll_stop(&p->handle);
} }
} }
static void redisLibuvAddWrite(void *privdata) { static void redisLibuvAddWrite(void *privdata) {
redisLibuvEvents* p = (redisLibuvEvents*)privdata; redisLibuvEvents* p = (redisLibuvEvents*)privdata;
p->events |= UV_WRITABLE; p->events |= UV_WRITABLE;
uv_poll_start(&p->handle, p->events, redisLibuvPoll); uv_poll_start(&p->handle, p->events, redisLibuvPoll);
} }
static void redisLibuvDelWrite(void *privdata) { static void redisLibuvDelWrite(void *privdata) {
redisLibuvEvents* p = (redisLibuvEvents*)privdata; redisLibuvEvents* p = (redisLibuvEvents*)privdata;
p->events &= ~UV_WRITABLE; p->events &= ~UV_WRITABLE;
if (p->events) { if (p->events) {
uv_poll_start(&p->handle, p->events, redisLibuvPoll); uv_poll_start(&p->handle, p->events, redisLibuvPoll);
} else { } else {
uv_poll_stop(&p->handle); uv_poll_stop(&p->handle);
} }
} }
static void on_timer_close(uv_handle_t *handle) {
redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
p->timer.data = NULL;
if (!p->handle.data) {
// both timer and handle are closed
hi_free(p);
}
// else, wait for `on_handle_close`
}
static void on_close(uv_handle_t* handle) { static void on_handle_close(uv_handle_t *handle) {
redisLibuvEvents* p = (redisLibuvEvents*)handle->data; redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
p->handle.data = NULL;
if (!p->timer.data) {
// timer never started, or timer already destroyed
hi_free(p);
}
// else, wait for `on_timer_close`
}
hi_free(p); // libuv removed `status` parameter since v0.11.23
// see: https://github.com/libuv/libuv/blob/v0.11.23/include/uv.h
#if (UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR < 11) || \
(UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR == 11 && UV_VERSION_PATCH < 23)
static void redisLibuvTimeout(uv_timer_t *timer, int status) {
(void)status; // unused
#else
static void redisLibuvTimeout(uv_timer_t *timer) {
#endif
redisLibuvEvents *e = (redisLibuvEvents*)timer->data;
redisAsyncHandleTimeout(e->context);
} }
static void redisLibuvSetTimeout(void *privdata, struct timeval tv) {
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
uint64_t millsec = tv.tv_sec * 1000 + tv.tv_usec / 1000.0;
if (!p->timer.data) {
// timer is uninitialized
if (uv_timer_init(p->handle.loop, &p->timer) != 0) {
return;
}
p->timer.data = p;
}
// updates the timeout if the timer has already started
// or start the timer
uv_timer_start(&p->timer, redisLibuvTimeout, millsec, 0);
}
static void redisLibuvCleanup(void *privdata) { static void redisLibuvCleanup(void *privdata) {
redisLibuvEvents* p = (redisLibuvEvents*)privdata; redisLibuvEvents* p = (redisLibuvEvents*)privdata;
p->context = NULL; // indicate that context might no longer exist p->context = NULL; // indicate that context might no longer exist
uv_close((uv_handle_t*)&p->handle, on_close); if (p->timer.data) {
uv_close((uv_handle_t*)&p->timer, on_timer_close);
}
uv_close((uv_handle_t*)&p->handle, on_handle_close);
} }
static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) { static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) {
redisContext *c = &(ac->c); redisContext *c = &(ac->c);
if (ac->ev.data != NULL) { if (ac->ev.data != NULL) {
return REDIS_ERR; return REDIS_ERR;
} }
ac->ev.addRead = redisLibuvAddRead; ac->ev.addRead = redisLibuvAddRead;
ac->ev.delRead = redisLibuvDelRead; ac->ev.delRead = redisLibuvDelRead;
ac->ev.addWrite = redisLibuvAddWrite; ac->ev.addWrite = redisLibuvAddWrite;
ac->ev.delWrite = redisLibuvDelWrite; ac->ev.delWrite = redisLibuvDelWrite;
ac->ev.cleanup = redisLibuvCleanup; ac->ev.cleanup = redisLibuvCleanup;
ac->ev.scheduleTimer = redisLibuvSetTimeout;
redisLibuvEvents* p = (redisLibuvEvents*)hi_malloc(sizeof(*p)); redisLibuvEvents* p = (redisLibuvEvents*)hi_malloc(sizeof(*p));
if (p == NULL) if (p == NULL)
return REDIS_ERR; return REDIS_ERR;
memset(p, 0, sizeof(*p)); memset(p, 0, sizeof(*p));
if (uv_poll_init_socket(loop, &p->handle, c->fd) != 0) { if (uv_poll_init_socket(loop, &p->handle, c->fd) != 0) {
return REDIS_ERR; return REDIS_ERR;
} }
ac->ev.data = p; ac->ev.data = p;
p->handle.data = p; p->handle.data = p;
p->context = ac; p->context = ac;
return REDIS_OK; return REDIS_OK;
} }
#endif #endif
...@@ -68,6 +68,10 @@ void *hi_malloc(size_t size) { ...@@ -68,6 +68,10 @@ void *hi_malloc(size_t size) {
} }
void *hi_calloc(size_t nmemb, size_t size) { void *hi_calloc(size_t nmemb, size_t size) {
/* Overflow check as the user can specify any arbitrary allocator */
if (SIZE_MAX / size < nmemb)
return NULL;
return hiredisAllocFns.callocFn(nmemb, size); return hiredisAllocFns.callocFn(nmemb, size);
} }
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#define HIREDIS_ALLOC_H #define HIREDIS_ALLOC_H
#include <stddef.h> /* for size_t */ #include <stddef.h> /* for size_t */
#include <stdint.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
...@@ -59,6 +60,10 @@ static inline void *hi_malloc(size_t size) { ...@@ -59,6 +60,10 @@ static inline void *hi_malloc(size_t size) {
} }
static inline void *hi_calloc(size_t nmemb, size_t size) { static inline void *hi_calloc(size_t nmemb, size_t size) {
/* Overflow check as the user can specify any arbitrary allocator */
if (SIZE_MAX / size < nmemb)
return NULL;
return hiredisAllocFns.callocFn(nmemb, size); return hiredisAllocFns.callocFn(nmemb, size);
} }
......
...@@ -47,6 +47,11 @@ ...@@ -47,6 +47,11 @@
#include "async_private.h" #include "async_private.h"
#ifdef NDEBUG
#undef assert
#define assert(e) (void)(e)
#endif
/* Forward declarations of hiredis.c functions */ /* Forward declarations of hiredis.c functions */
int __redisAppendCommand(redisContext *c, const char *cmd, size_t len); int __redisAppendCommand(redisContext *c, const char *cmd, size_t len);
void __redisSetError(redisContext *c, int type, const char *str); void __redisSetError(redisContext *c, int type, const char *str);
...@@ -54,7 +59,7 @@ void __redisSetError(redisContext *c, int type, const char *str); ...@@ -54,7 +59,7 @@ void __redisSetError(redisContext *c, int type, const char *str);
/* Functions managing dictionary of callbacks for pub/sub. */ /* Functions managing dictionary of callbacks for pub/sub. */
static unsigned int callbackHash(const void *key) { static unsigned int callbackHash(const void *key) {
return dictGenHashFunction((const unsigned char *)key, return dictGenHashFunction((const unsigned char *)key,
hi_sdslen((const hisds)key)); sdslen((const sds)key));
} }
static void *callbackValDup(void *privdata, const void *src) { static void *callbackValDup(void *privdata, const void *src) {
...@@ -73,15 +78,15 @@ static int callbackKeyCompare(void *privdata, const void *key1, const void *key2 ...@@ -73,15 +78,15 @@ static int callbackKeyCompare(void *privdata, const void *key1, const void *key2
int l1, l2; int l1, l2;
((void) privdata); ((void) privdata);
l1 = hi_sdslen((const hisds)key1); l1 = sdslen((const sds)key1);
l2 = hi_sdslen((const hisds)key2); l2 = sdslen((const sds)key2);
if (l1 != l2) return 0; if (l1 != l2) return 0;
return memcmp(key1,key2,l1) == 0; return memcmp(key1,key2,l1) == 0;
} }
static void callbackKeyDestructor(void *privdata, void *key) { static void callbackKeyDestructor(void *privdata, void *key) {
((void) privdata); ((void) privdata);
hi_sdsfree((hisds)key); sdsfree((sds)key);
} }
static void callbackValDestructor(void *privdata, void *val) { static void callbackValDestructor(void *privdata, void *val) {
...@@ -139,8 +144,8 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) { ...@@ -139,8 +144,8 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
ac->replies.head = NULL; ac->replies.head = NULL;
ac->replies.tail = NULL; ac->replies.tail = NULL;
ac->sub.invalid.head = NULL; ac->sub.replies.head = NULL;
ac->sub.invalid.tail = NULL; ac->sub.replies.tail = NULL;
ac->sub.channels = channels; ac->sub.channels = channels;
ac->sub.patterns = patterns; ac->sub.patterns = patterns;
...@@ -301,36 +306,28 @@ static void __redisRunPushCallback(redisAsyncContext *ac, redisReply *reply) { ...@@ -301,36 +306,28 @@ static void __redisRunPushCallback(redisAsyncContext *ac, redisReply *reply) {
static void __redisAsyncFree(redisAsyncContext *ac) { static void __redisAsyncFree(redisAsyncContext *ac) {
redisContext *c = &(ac->c); redisContext *c = &(ac->c);
redisCallback cb; redisCallback cb;
dictIterator *it; dictIterator it;
dictEntry *de; dictEntry *de;
/* Execute pending callbacks with NULL reply. */ /* Execute pending callbacks with NULL reply. */
while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK) while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK)
__redisRunCallback(ac,&cb,NULL); __redisRunCallback(ac,&cb,NULL);
while (__redisShiftCallback(&ac->sub.replies,&cb) == REDIS_OK)
/* Execute callbacks for invalid commands */
while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK)
__redisRunCallback(ac,&cb,NULL); __redisRunCallback(ac,&cb,NULL);
/* Run subscription callbacks with NULL reply */ /* Run subscription callbacks with NULL reply */
if (ac->sub.channels) { if (ac->sub.channels) {
it = dictGetIterator(ac->sub.channels); dictInitIterator(&it,ac->sub.channels);
if (it != NULL) { while ((de = dictNext(&it)) != NULL)
while ((de = dictNext(it)) != NULL) __redisRunCallback(ac,dictGetEntryVal(de),NULL);
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
dictReleaseIterator(it);
}
dictRelease(ac->sub.channels); dictRelease(ac->sub.channels);
} }
if (ac->sub.patterns) { if (ac->sub.patterns) {
it = dictGetIterator(ac->sub.patterns); dictInitIterator(&it,ac->sub.patterns);
if (it != NULL) { while ((de = dictNext(&it)) != NULL)
while ((de = dictNext(it)) != NULL) __redisRunCallback(ac,dictGetEntryVal(de),NULL);
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
dictReleaseIterator(it);
}
dictRelease(ac->sub.patterns); dictRelease(ac->sub.patterns);
} }
...@@ -418,12 +415,13 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, ...@@ -418,12 +415,13 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
dictEntry *de; dictEntry *de;
int pvariant; int pvariant;
char *stype; char *stype;
hisds sname; sds sname;
/* Custom reply functions are not supported for pub/sub. This will fail /* Match reply with the expected format of a pushed message.
* very hard when they are used... */ * The type and number of elements (3 to 4) are specified at:
if (reply->type == REDIS_REPLY_ARRAY || reply->type == REDIS_REPLY_PUSH) { * https://redis.io/topics/pubsub#format-of-pushed-messages */
assert(reply->elements >= 2); if ((reply->type == REDIS_REPLY_ARRAY && !(c->flags & REDIS_SUPPORTS_PUSH) && reply->elements >= 3) ||
reply->type == REDIS_REPLY_PUSH) {
assert(reply->element[0]->type == REDIS_REPLY_STRING); assert(reply->element[0]->type == REDIS_REPLY_STRING);
stype = reply->element[0]->str; stype = reply->element[0]->str;
pvariant = (tolower(stype[0]) == 'p') ? 1 : 0; pvariant = (tolower(stype[0]) == 'p') ? 1 : 0;
...@@ -435,7 +433,7 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, ...@@ -435,7 +433,7 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
/* Locate the right callback */ /* Locate the right callback */
assert(reply->element[1]->type == REDIS_REPLY_STRING); assert(reply->element[1]->type == REDIS_REPLY_STRING);
sname = hi_sdsnewlen(reply->element[1]->str,reply->element[1]->len); sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);
if (sname == NULL) if (sname == NULL)
goto oom; goto oom;
...@@ -462,14 +460,21 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, ...@@ -462,14 +460,21 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
/* Unset subscribed flag only when no pipelined pending subscribe. */ /* Unset subscribed flag only when no pipelined pending subscribe. */
if (reply->element[2]->integer == 0 if (reply->element[2]->integer == 0
&& dictSize(ac->sub.channels) == 0 && dictSize(ac->sub.channels) == 0
&& dictSize(ac->sub.patterns) == 0) && dictSize(ac->sub.patterns) == 0) {
c->flags &= ~REDIS_SUBSCRIBED; c->flags &= ~REDIS_SUBSCRIBED;
/* Move ongoing regular command callbacks. */
redisCallback cb;
while (__redisShiftCallback(&ac->sub.replies,&cb) == REDIS_OK) {
__redisPushCallback(&ac->replies,&cb);
}
}
} }
} }
hi_sdsfree(sname); sdsfree(sname);
} else { } else {
/* Shift callback for invalid commands. */ /* Shift callback for pending command in subscribed context. */
__redisShiftCallback(&ac->sub.invalid,dstcb); __redisShiftCallback(&ac->sub.replies,dstcb);
} }
return REDIS_OK; return REDIS_OK;
oom: oom:
...@@ -497,13 +502,12 @@ static int redisIsSubscribeReply(redisReply *reply) { ...@@ -497,13 +502,12 @@ static int redisIsSubscribeReply(redisReply *reply) {
len = reply->element[0]->len - off; len = reply->element[0]->len - off;
return !strncasecmp(str, "subscribe", len) || return !strncasecmp(str, "subscribe", len) ||
!strncasecmp(str, "message", len); !strncasecmp(str, "message", len) ||
!strncasecmp(str, "unsubscribe", len);
} }
void redisProcessCallbacks(redisAsyncContext *ac) { void redisProcessCallbacks(redisAsyncContext *ac) {
redisContext *c = &(ac->c); redisContext *c = &(ac->c);
redisCallback cb = {NULL, NULL, 0, NULL};
void *reply = NULL; void *reply = NULL;
int status; int status;
...@@ -511,22 +515,19 @@ void redisProcessCallbacks(redisAsyncContext *ac) { ...@@ -511,22 +515,19 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
if (reply == NULL) { if (reply == NULL) {
/* When the connection is being disconnected and there are /* When the connection is being disconnected and there are
* no more replies, this is the cue to really disconnect. */ * no more replies, this is the cue to really disconnect. */
if (c->flags & REDIS_DISCONNECTING && hi_sdslen(c->obuf) == 0 if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0
&& ac->replies.head == NULL) { && ac->replies.head == NULL) {
__redisAsyncDisconnect(ac); __redisAsyncDisconnect(ac);
return; return;
} }
/* If monitor mode, repush callback */
if(c->flags & REDIS_MONITORING) {
__redisPushCallback(&ac->replies,&cb);
}
/* When the connection is not being disconnected, simply stop /* When the connection is not being disconnected, simply stop
* trying to get replies and wait for the next loop tick. */ * trying to get replies and wait for the next loop tick. */
break; break;
} }
/* Keep track of push message support for subscribe handling */
if (redisIsPushReply(reply)) c->flags |= REDIS_SUPPORTS_PUSH;
/* Send any non-subscribe related PUSH messages to our PUSH handler /* Send any non-subscribe related PUSH messages to our PUSH handler
* while allowing subscribe related PUSH messages to pass through. * while allowing subscribe related PUSH messages to pass through.
* This allows existing code to be backward compatible and work in * This allows existing code to be backward compatible and work in
...@@ -539,6 +540,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) { ...@@ -539,6 +540,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
/* Even if the context is subscribed, pending regular /* Even if the context is subscribed, pending regular
* callbacks will get a reply before pub/sub messages arrive. */ * callbacks will get a reply before pub/sub messages arrive. */
redisCallback cb = {NULL, NULL, 0, NULL};
if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) { if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {
/* /*
* A spontaneous reply in a not-subscribed context can be the error * A spontaneous reply in a not-subscribed context can be the error
...@@ -562,15 +564,17 @@ void redisProcessCallbacks(redisAsyncContext *ac) { ...@@ -562,15 +564,17 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
__redisAsyncDisconnect(ac); __redisAsyncDisconnect(ac);
return; return;
} }
/* No more regular callbacks and no errors, the context *must* be subscribed or monitoring. */ /* No more regular callbacks and no errors, the context *must* be subscribed. */
assert((c->flags & REDIS_SUBSCRIBED || c->flags & REDIS_MONITORING)); assert(c->flags & REDIS_SUBSCRIBED);
if(c->flags & REDIS_SUBSCRIBED) if (c->flags & REDIS_SUBSCRIBED)
__redisGetSubscribeCallback(ac,reply,&cb); __redisGetSubscribeCallback(ac,reply,&cb);
} }
if (cb.fn != NULL) { if (cb.fn != NULL) {
__redisRunCallback(ac,&cb,reply); __redisRunCallback(ac,&cb,reply);
c->reader->fn->freeObject(reply); if (!(c->flags & REDIS_NO_AUTO_FREE_REPLIES)){
c->reader->fn->freeObject(reply);
}
/* Proceed with free'ing when redisAsyncFree() was called. */ /* Proceed with free'ing when redisAsyncFree() was called. */
if (c->flags & REDIS_FREEING) { if (c->flags & REDIS_FREEING) {
...@@ -584,6 +588,11 @@ void redisProcessCallbacks(redisAsyncContext *ac) { ...@@ -584,6 +588,11 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
* doesn't know what the server will spit out over the wire. */ * doesn't know what the server will spit out over the wire. */
c->reader->fn->freeObject(reply); c->reader->fn->freeObject(reply);
} }
/* If in monitor mode, repush the callback */
if (c->flags & REDIS_MONITORING) {
__redisPushCallback(&ac->replies,&cb);
}
} }
/* Disconnect when there was an error reading the reply */ /* Disconnect when there was an error reading the reply */
...@@ -605,7 +614,8 @@ static int __redisAsyncHandleConnect(redisAsyncContext *ac) { ...@@ -605,7 +614,8 @@ static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
if (redisCheckConnectDone(c, &completed) == REDIS_ERR) { if (redisCheckConnectDone(c, &completed) == REDIS_ERR) {
/* Error! */ /* Error! */
redisCheckSocketError(c); if (redisCheckSocketError(c) == REDIS_ERR)
__redisAsyncCopyError(ac);
__redisAsyncHandleConnectFailure(ac); __redisAsyncHandleConnectFailure(ac);
return REDIS_ERR; return REDIS_ERR;
} else if (completed == 1) { } else if (completed == 1) {
...@@ -691,13 +701,22 @@ void redisAsyncHandleTimeout(redisAsyncContext *ac) { ...@@ -691,13 +701,22 @@ void redisAsyncHandleTimeout(redisAsyncContext *ac) {
redisContext *c = &(ac->c); redisContext *c = &(ac->c);
redisCallback cb; redisCallback cb;
if ((c->flags & REDIS_CONNECTED) && ac->replies.head == NULL) { if ((c->flags & REDIS_CONNECTED)) {
/* Nothing to do - just an idle timeout */ if (ac->replies.head == NULL && ac->sub.replies.head == NULL) {
return; /* Nothing to do - just an idle timeout */
return;
}
if (!ac->c.command_timeout ||
(!ac->c.command_timeout->tv_sec && !ac->c.command_timeout->tv_usec)) {
/* A belated connect timeout arriving, ignore */
return;
}
} }
if (!c->err) { if (!c->err) {
__redisSetError(c, REDIS_ERR_TIMEOUT, "Timeout"); __redisSetError(c, REDIS_ERR_TIMEOUT, "Timeout");
__redisAsyncCopyError(ac);
} }
if (!(c->flags & REDIS_CONNECTED) && ac->onConnect) { if (!(c->flags & REDIS_CONNECTED) && ac->onConnect) {
...@@ -744,7 +763,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void ...@@ -744,7 +763,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
const char *cstr, *astr; const char *cstr, *astr;
size_t clen, alen; size_t clen, alen;
const char *p; const char *p;
hisds sname; sds sname;
int ret; int ret;
/* Don't accept new commands when the connection is about to be closed. */ /* Don't accept new commands when the connection is about to be closed. */
...@@ -768,7 +787,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void ...@@ -768,7 +787,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
/* Add every channel/pattern to the list of subscription callbacks. */ /* Add every channel/pattern to the list of subscription callbacks. */
while ((p = nextArgument(p,&astr,&alen)) != NULL) { while ((p = nextArgument(p,&astr,&alen)) != NULL) {
sname = hi_sdsnewlen(astr,alen); sname = sdsnewlen(astr,alen);
if (sname == NULL) if (sname == NULL)
goto oom; goto oom;
...@@ -786,7 +805,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void ...@@ -786,7 +805,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
ret = dictReplace(cbdict,sname,&cb); ret = dictReplace(cbdict,sname,&cb);
if (ret == 0) hi_sdsfree(sname); if (ret == 0) sdsfree(sname);
} }
} else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) { } else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) {
/* It is only useful to call (P)UNSUBSCRIBE when the context is /* It is only useful to call (P)UNSUBSCRIBE when the context is
...@@ -796,17 +815,19 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void ...@@ -796,17 +815,19 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
/* (P)UNSUBSCRIBE does not have its own response: every channel or /* (P)UNSUBSCRIBE does not have its own response: every channel or
* pattern that is unsubscribed will receive a message. This means we * pattern that is unsubscribed will receive a message. This means we
* should not append a callback function for this command. */ * should not append a callback function for this command. */
} else if(strncasecmp(cstr,"monitor\r\n",9) == 0) { } else if (strncasecmp(cstr,"monitor\r\n",9) == 0) {
/* Set monitor flag and push callback */ /* Set monitor flag and push callback */
c->flags |= REDIS_MONITORING; c->flags |= REDIS_MONITORING;
__redisPushCallback(&ac->replies,&cb); if (__redisPushCallback(&ac->replies,&cb) != REDIS_OK)
goto oom;
} else { } else {
if (c->flags & REDIS_SUBSCRIBED) if (c->flags & REDIS_SUBSCRIBED) {
/* This will likely result in an error reply, but it needs to be if (__redisPushCallback(&ac->sub.replies,&cb) != REDIS_OK)
* received and passed to the callback. */ goto oom;
__redisPushCallback(&ac->sub.invalid,&cb); } else {
else if (__redisPushCallback(&ac->replies,&cb) != REDIS_OK)
__redisPushCallback(&ac->replies,&cb); goto oom;
}
} }
__redisAppendCommand(c,cmd,len); __redisAppendCommand(c,cmd,len);
...@@ -817,6 +838,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void ...@@ -817,6 +838,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
return REDIS_OK; return REDIS_OK;
oom: oom:
__redisSetError(&(ac->c), REDIS_ERR_OOM, "Out of memory"); __redisSetError(&(ac->c), REDIS_ERR_OOM, "Out of memory");
__redisAsyncCopyError(ac);
return REDIS_ERR; return REDIS_ERR;
} }
...@@ -845,14 +867,14 @@ int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata ...@@ -845,14 +867,14 @@ int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata
} }
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) { int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) {
hisds cmd; sds cmd;
int len; long long len;
int status; int status;
len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen); len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
if (len < 0) if (len < 0)
return REDIS_ERR; return REDIS_ERR;
status = __redisAsyncCommand(ac,fn,privdata,cmd,len); status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
hi_sdsfree(cmd); sdsfree(cmd);
return status; return status;
} }
......
...@@ -102,7 +102,7 @@ typedef struct redisAsyncContext { ...@@ -102,7 +102,7 @@ typedef struct redisAsyncContext {
/* Subscription callbacks */ /* Subscription callbacks */
struct { struct {
redisCallbackList invalid; redisCallbackList replies;
struct dict *channels; struct dict *channels;
struct dict *patterns; struct dict *patterns;
} sub; } sub;
......
...@@ -267,16 +267,11 @@ static dictEntry *dictFind(dict *ht, const void *key) { ...@@ -267,16 +267,11 @@ static dictEntry *dictFind(dict *ht, const void *key) {
return NULL; return NULL;
} }
static dictIterator *dictGetIterator(dict *ht) { static void dictInitIterator(dictIterator *iter, dict *ht) {
dictIterator *iter = hi_malloc(sizeof(*iter));
if (iter == NULL)
return NULL;
iter->ht = ht; iter->ht = ht;
iter->index = -1; iter->index = -1;
iter->entry = NULL; iter->entry = NULL;
iter->nextEntry = NULL; iter->nextEntry = NULL;
return iter;
} }
static dictEntry *dictNext(dictIterator *iter) { static dictEntry *dictNext(dictIterator *iter) {
...@@ -299,10 +294,6 @@ static dictEntry *dictNext(dictIterator *iter) { ...@@ -299,10 +294,6 @@ static dictEntry *dictNext(dictIterator *iter) {
return NULL; return NULL;
} }
static void dictReleaseIterator(dictIterator *iter) {
hi_free(iter);
}
/* ------------------------- private functions ------------------------------ */ /* ------------------------- private functions ------------------------------ */
/* Expand the hash table if needed */ /* Expand the hash table if needed */
......
...@@ -119,8 +119,7 @@ static int dictReplace(dict *ht, void *key, void *val); ...@@ -119,8 +119,7 @@ static int dictReplace(dict *ht, void *key, void *val);
static int dictDelete(dict *ht, const void *key); static int dictDelete(dict *ht, const void *key);
static void dictRelease(dict *ht); static void dictRelease(dict *ht);
static dictEntry * dictFind(dict *ht, const void *key); static dictEntry * dictFind(dict *ht, const void *key);
static dictIterator *dictGetIterator(dict *ht); static void dictInitIterator(dictIterator *iter, dict *ht);
static dictEntry *dictNext(dictIterator *iter); static dictEntry *dictNext(dictIterator *iter);
static void dictReleaseIterator(dictIterator *iter);
#endif /* __DICT_H */ #endif /* __DICT_H */
...@@ -21,7 +21,7 @@ ENDIF() ...@@ -21,7 +21,7 @@ ENDIF()
FIND_PATH(LIBEVENT event.h) FIND_PATH(LIBEVENT event.h)
if (LIBEVENT) if (LIBEVENT)
ADD_EXECUTABLE(example-libevent example-libevent) ADD_EXECUTABLE(example-libevent example-libevent.c)
TARGET_LINK_LIBRARIES(example-libevent hiredis event) TARGET_LINK_LIBRARIES(example-libevent hiredis event)
ENDIF() ENDIF()
......
...@@ -7,18 +7,33 @@ ...@@ -7,18 +7,33 @@
#include <async.h> #include <async.h>
#include <adapters/libuv.h> #include <adapters/libuv.h>
void debugCallback(redisAsyncContext *c, void *r, void *privdata) {
(void)privdata; //unused
redisReply *reply = r;
if (reply == NULL) {
/* The DEBUG SLEEP command will almost always fail, because we have set a 1 second timeout */
printf("`DEBUG SLEEP` error: %s\n", c->errstr ? c->errstr : "unknown error");
return;
}
/* Disconnect after receiving the reply of DEBUG SLEEP (which will not)*/
redisAsyncDisconnect(c);
}
void getCallback(redisAsyncContext *c, void *r, void *privdata) { void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r; redisReply *reply = r;
if (reply == NULL) return; if (reply == NULL) {
printf("argv[%s]: %s\n", (char*)privdata, reply->str); printf("`GET key` error: %s\n", c->errstr ? c->errstr : "unknown error");
return;
}
printf("`GET key` result: argv[%s]: %s\n", (char*)privdata, reply->str);
/* Disconnect after receiving the reply to GET */ /* start another request that demonstrate timeout */
redisAsyncDisconnect(c); redisAsyncCommand(c, debugCallback, NULL, "DEBUG SLEEP %f", 1.5);
} }
void connectCallback(const redisAsyncContext *c, int status) { void connectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) { if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr); printf("connect error: %s\n", c->errstr);
return; return;
} }
printf("Connected...\n"); printf("Connected...\n");
...@@ -26,7 +41,7 @@ void connectCallback(const redisAsyncContext *c, int status) { ...@@ -26,7 +41,7 @@ void connectCallback(const redisAsyncContext *c, int status) {
void disconnectCallback(const redisAsyncContext *c, int status) { void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) { if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr); printf("disconnect because of error: %s\n", c->errstr);
return; return;
} }
printf("Disconnected...\n"); printf("Disconnected...\n");
...@@ -49,8 +64,18 @@ int main (int argc, char **argv) { ...@@ -49,8 +64,18 @@ int main (int argc, char **argv) {
redisLibuvAttach(c,loop); redisLibuvAttach(c,loop);
redisAsyncSetConnectCallback(c,connectCallback); redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback); redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncSetTimeout(c, (struct timeval){ .tv_sec = 1, .tv_usec = 0});
/*
In this demo, we first `set key`, then `get key` to demonstrate the basic usage of libuv adapter.
Then in `getCallback`, we start a `debug sleep` command to create 1.5 second long request.
Because we have set a 1 second timeout to the connection, the command will always fail with a
timeout error, which is shown in the `debugCallback`.
*/
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
uv_run(loop, UV_RUN_DEFAULT); uv_run(loop, UV_RUN_DEFAULT);
return 0; return 0;
} }
...@@ -31,7 +31,6 @@ ...@@ -31,7 +31,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <hiredis.h> #include <hiredis.h>
#include <win32.h>
#define KEY_COUNT 5 #define KEY_COUNT 5
......
...@@ -4,7 +4,10 @@ ...@@ -4,7 +4,10 @@
#include <hiredis.h> #include <hiredis.h>
#include <hiredis_ssl.h> #include <hiredis_ssl.h>
#include <win32.h>
#ifdef _MSC_VER
#include <winsock2.h> /* For struct timeval */
#endif
int main(int argc, char **argv) { int main(int argc, char **argv) {
unsigned int j; unsigned int j;
......
...@@ -2,7 +2,10 @@ ...@@ -2,7 +2,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <hiredis.h> #include <hiredis.h>
#include <win32.h>
#ifdef _MSC_VER
#include <winsock2.h> /* For struct timeval */
#endif
int main(int argc, char **argv) { int main(int argc, char **argv) {
unsigned int j, isunix = 0; unsigned int j, isunix = 0;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment