Commit 886a04c2 authored by Yossi Gottlieb's avatar Yossi Gottlieb
Browse files

Merge commit '418de21d' into refresh-hiredis

parents e6478cfd 418de21d
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,33 +54,66 @@ DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) ...@@ -60,33 +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 ($(USE_SSL),1) ifeq ($(TEST_ASYNC),1)
CFLAGS+=-DHIREDIS_TEST_SSL export CFLAGS+=-DHIREDIS_TEST_ASYNC
endif endif
ifeq ($(uname_S),Linux) ifeq ($(USE_SSL),1)
ifdef OPENSSL_PREFIX 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 CFLAGS+=-I$(OPENSSL_PREFIX)/include
SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto
else endif
SSL_LDFLAGS=-lssl -lcrypto endif
ifeq ($(uname_S),FreeBSD)
LDFLAGS+=-lm
IS_GCC=$(shell sh -c '$(CC) --version 2>/dev/null |egrep -i -c "gcc"')
ifeq ($(IS_GCC),1)
REAL_CFLAGS+=-pedantic
endif 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)
...@@ -108,10 +135,13 @@ ifeq ($(uname_S),Darwin) ...@@ -108,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
...@@ -122,7 +152,6 @@ net.o: net.c fmacros.h net.h hiredis.h read.h sds.h alloc.h sockcompat.h win32.h ...@@ -122,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)
...@@ -131,18 +160,15 @@ $(DYLIBNAME): $(OBJ) ...@@ -131,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)
...@@ -166,7 +192,6 @@ hiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME) ...@@ -166,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)"
...@@ -177,10 +202,11 @@ hiredis-example-ae: examples/example-ae.c adapters/ae.h $(STLIBNAME) ...@@ -177,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
...@@ -206,10 +232,13 @@ hiredis-example-push: examples/example-push.c $(STLIBNAME) ...@@ -206,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)
...@@ -225,7 +254,7 @@ check: hiredis-test ...@@ -225,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
...@@ -262,7 +291,7 @@ $(SSL_PKGCONFNAME): hiredis_ssl.h ...@@ -262,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
...@@ -272,9 +301,6 @@ install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME) ...@@ -272,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)
...@@ -283,7 +309,6 @@ install-ssl: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME) ...@@ -283,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 ""
...@@ -299,12 +324,12 @@ gprof: ...@@ -299,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
...@@ -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;
......
#ifndef __HIREDIS_FMACRO_H #ifndef __HIREDIS_FMACRO_H
#define __HIREDIS_FMACRO_H #define __HIREDIS_FMACRO_H
#ifndef _AIX
#define _XOPEN_SOURCE 600 #define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200112L #define _POSIX_C_SOURCE 200112L
#endif
#if defined(__APPLE__) && defined(__MACH__) #if defined(__APPLE__) && defined(__MACH__)
/* Enable TCP_KEEPALIVE */ /* Enable TCP_KEEPALIVE */
......
/* /*
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com> * Copyright (c) 2020, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2020, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* Copyright (c) 2020, Matt Stancliff <matt at genges dot com>,
* Jan-Erik Rediger <janerik at fnordig dot com>
* *
* All rights reserved. * All rights reserved.
* *
...@@ -28,67 +31,27 @@ ...@@ -28,67 +31,27 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
/* #include <stdlib.h>
* SDS compatibility header. #include <string.h>
* #include "hiredis.h"
* This simple file maps sds types and calls to their unique hiredis symbol names.
* It's useful when we build Hiredis as a dependency of Redis and want to call int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
* Hiredis' sds symbols rather than the ones built into Redis, as the libraries char *new_str, *cmd;
* have slightly diverged and could cause hard to track down ABI incompatibility
* bugs.
*
*/
#ifndef HIREDIS_SDS_COMPAT if (size < 3)
#define HIREDIS_SDS_COMPAT return 0;
#define sds hisds new_str = malloc(size+1);
if (new_str == NULL)
return 0;
#define sdslen hi_sdslen memcpy(new_str, data, size);
#define sdsavail hi_sdsavail new_str[size] = '\0';
#define sdssetlen hi_sdssetlen
#define sdsinclen hi_sdsinclen
#define sdsalloc hi_sdsalloc
#define sdssetalloc hi_sdssetalloc
#define sdsAllocPtr hi_sdsAllocPtr redisFormatCommand(&cmd, new_str);
#define sdsAllocSize hi_sdsAllocSize
#define sdscat hi_sdscat
#define sdscatfmt hi_sdscatfmt
#define sdscatlen hi_sdscatlen
#define sdscatprintf hi_sdscatprintf
#define sdscatrepr hi_sdscatrepr
#define sdscatsds hi_sdscatsds
#define sdscatvprintf hi_sdscatvprintf
#define sdsclear hi_sdsclear
#define sdscmp hi_sdscmp
#define sdscpy hi_sdscpy
#define sdscpylen hi_sdscpylen
#define sdsdup hi_sdsdup
#define sdsempty hi_sdsempty
#define sds_free hi_sds_free
#define sdsfree hi_sdsfree
#define sdsfreesplitres hi_sdsfreesplitres
#define sdsfromlonglong hi_sdsfromlonglong
#define sdsgrowzero hi_sdsgrowzero
#define sdsIncrLen hi_sdsIncrLen
#define sdsjoin hi_sdsjoin
#define sdsjoinsds hi_sdsjoinsds
#define sdsll2str hi_sdsll2str
#define sdsMakeRoomFor hi_sdsMakeRoomFor
#define sds_malloc hi_sds_malloc
#define sdsmapchars hi_sdsmapchars
#define sdsnew hi_sdsnew
#define sdsnewlen hi_sdsnewlen
#define sdsrange hi_sdsrange
#define sds_realloc hi_sds_realloc
#define sdsRemoveFreeSpace hi_sdsRemoveFreeSpace
#define sdssplitargs hi_sdssplitargs
#define sdssplitlen hi_sdssplitlen
#define sdstolower hi_sdstolower
#define sdstoupper hi_sdstoupper
#define sdstrim hi_sdstrim
#define sdsull2str hi_sdsull2str
#define sdsupdatelen hi_sdsupdatelen
#endif /* HIREDIS_SDS_COMPAT */ if (cmd != NULL)
hi_free(cmd);
free(new_str);
return 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