Merge branch 'master' of github.com:redis/hiredis

This commit is contained in:
Alessio M 2020-09-04 09:31:47 +01:00
commit d7b1d21e80
16 changed files with 461 additions and 124 deletions

View File

@ -7,6 +7,8 @@ os:
- linux - linux
- osx - osx
dist: bionic
branches: branches:
only: only:
- staging - staging
@ -14,6 +16,13 @@ branches:
- master - master
- /^release\/.*$/ - /^release\/.*$/
install:
- if [ "$TRAVIS_COMPILER" != "mingw" ]; then
wget https://github.com/redis/redis/archive/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;
fi;
before_script: before_script:
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then - if [ "$TRAVIS_OS_NAME" == "osx" ]; then
curl -O https://distfiles.macports.org/MacPorts/MacPorts-2.6.2-10.13-HighSierra.pkg; curl -O https://distfiles.macports.org/MacPorts/MacPorts-2.6.2-10.13-HighSierra.pkg;
@ -24,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
@ -37,14 +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 [ "$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";
@ -79,7 +85,11 @@ script:
- mkdir build/ && cd build/ - mkdir build/ && cd build/
- cmake .. ${EXTRA_CMAKE_OPTS} - cmake .. ${EXTRA_CMAKE_OPTS}
- make VERBOSE=1 - make VERBOSE=1
- SKIPS_AS_FAILS=1 ctest -V - if [ "$BITS" == "64" ]; then
TEST_SSL=1 SKIPS_AS_FAILS=1 ctest -V;
else
SKIPS_AS_FAILS=1 ctest -V;
fi;
jobs: jobs:
include: include:

View File

@ -1,28 +1,175 @@
### 1.0.0 (unreleased) ## [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:
_A big thanks to everyone who helped with this release. The following list includes everyone who contributed at least five lines, sorted by lines contributed._ :sparkling_heart:
[Michael Grunder](https://github.com/michael-grunder), [Yossi Gottlieb](https://github.com/yossigo),
[Mark Nunberg](https://github.com/mnunberg), [Marcus Geelnard](https://github.com/mbitsnbites),
[Justin Brewer](https://github.com/justinbrewer), [Valentino Geron](https://github.com/valentinogeron),
[Minun Dragonation](https://github.com/dragonation), [Omri Steiner](https://github.com/OmriSteiner),
[Sangmoon Yi](https://github.com/jman-krafton), [Jinjiazh](https://github.com/jinjiazhang),
[Odin Hultgren Van Der Horst](https://github.com/Miniwoffer), [Muhammad Zahalqa](https://github.com/tryfinally),
[Nick Rivera](https://github.com/heronr), [Qi Yang](https://github.com/movebean),
[kevin1018](https://github.com/kevin1018)
[Full Changelog](https://github.com/redis/hiredis/compare/v0.14.1...v1.0.0)
**BREAKING CHANGES**: **BREAKING CHANGES**:
* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now * `redisOptions` now has two timeout fields. One for connecting, and one for commands. If you're presently using `options->timeout` you will need to change it to use `options->connect_timeout`. (See [example](https://github.com/redis/hiredis/commit/38b5ae543f5c99eb4ccabbe277770fc6bc81226f#diff-86ba39d37aa829c8c82624cce4f049fbL36))
protocol errors. This is consistent with the RESP specification. On 32-bit
platforms, the upper bound is lowered to `SIZE_MAX`.
* Change `redisReply.len` to `size_t`, as it denotes the the size of a string * Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now protocol errors. This is consistent
with the RESP specification. On 32-bit platforms, the upper bound is lowered to `SIZE_MAX`.
User code should compare this to `size_t` values as well. If it was used to
compare to other values, casting might be necessary or can be removed, if
casting was applied before.
### 0.x.x (unreleased)
**BREAKING CHANGES**:
* Change `redisReply.len` to `size_t`, as it denotes the the size of a string
User code should compare this to `size_t` values as well.
If it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before.
* `redisReplyObjectFunctions.createArray` now takes `size_t` for its length parameter. * `redisReplyObjectFunctions.createArray` now takes `size_t` for its length parameter.
**New features:**
- Support for RESP3
[\#697](https://github.com/redis/hiredis/pull/697),
[\#805](https://github.com/redis/hiredis/pull/805),
[\#819](https://github.com/redis/hiredis/pull/819),
[\#841](https://github.com/redis/hiredis/pull/841)
([Yossi Gottlieb](https://github.com/yossigo), [Michael Grunder](https://github.com/michael-grunder))
- Support for SSL connections
[\#645](https://github.com/redis/hiredis/pull/645),
[\#699](https://github.com/redis/hiredis/pull/699),
[\#702](https://github.com/redis/hiredis/pull/702),
[\#708](https://github.com/redis/hiredis/pull/708),
[\#711](https://github.com/redis/hiredis/pull/711),
[\#821](https://github.com/redis/hiredis/pull/821),
[more](https://github.com/redis/hiredis/pulls?q=is%3Apr+is%3Amerged+SSL)
([Mark Nunberg](https://github.com/mnunberg), [Yossi Gottlieb](https://github.com/yossigo))
- Run-time allocator injection
[\#800](https://github.com/redis/hiredis/pull/800)
([Michael Grunder](https://github.com/michael-grunder))
- Improved Windows support (including MinGW and Windows CI)
[\#652](https://github.com/redis/hiredis/pull/652),
[\#663](https://github.com/redis/hiredis/pull/663)
([Marcus Geelnard](https://www.bitsnbites.eu/author/m/))
- Adds support for distinct connect and command timeouts
[\#839](https://github.com/redis/hiredis/pull/839),
[\#829](https://github.com/redis/hiredis/pull/829)
([Valentino Geron](https://github.com/valentinogeron))
- Add generic pointer and destructor to `redisContext` that users can use for context.
[\#855](https://github.com/redis/hiredis/pull/855)
([Michael Grunder](https://github.com/michael-grunder))
**Closed issues (that involved code changes):**
- Makefile does not install TLS libraries [\#809](https://github.com/redis/hiredis/issues/809)
- redisConnectWithOptions should not set command timeout [\#722](https://github.com/redis/hiredis/issues/722), [\#829](https://github.com/redis/hiredis/pull/829) ([valentinogeron](https://github.com/valentinogeron))
- Fix integer overflow in `sdsrange` [\#827](https://github.com/redis/hiredis/issues/827)
- INFO & CLUSTER commands failed when using RESP3 [\#802](https://github.com/redis/hiredis/issues/802)
- Windows compatibility patches [\#687](https://github.com/redis/hiredis/issues/687), [\#838](https://github.com/redis/hiredis/issues/838), [\#842](https://github.com/redis/hiredis/issues/842)
- RESP3 PUSH messages incorrectly use pending callback [\#825](https://github.com/redis/hiredis/issues/825)
- Asynchronous PSUBSCRIBE command fails when using RESP3 [\#815](https://github.com/redis/hiredis/issues/815)
- New SSL API [\#804](https://github.com/redis/hiredis/issues/804), [\#813](https://github.com/redis/hiredis/issues/813)
- Hard-coded limit of nested reply depth [\#794](https://github.com/redis/hiredis/issues/794)
- Fix TCP_NODELAY in Windows/OSX [\#679](https://github.com/redis/hiredis/issues/679), [\#690](https://github.com/redis/hiredis/issues/690), [\#779](https://github.com/redis/hiredis/issues/779), [\#785](https://github.com/redis/hiredis/issues/785),
- Added timers to libev adapter. [\#778](https://github.com/redis/hiredis/issues/778), [\#795](https://github.com/redis/hiredis/pull/795)
- Initialization discards const qualifier [\#777](https://github.com/redis/hiredis/issues/777)
- \[BUG\]\[MinGW64\] Error setting socket timeout [\#775](https://github.com/redis/hiredis/issues/775)
- undefined reference to hi_malloc [\#769](https://github.com/redis/hiredis/issues/769)
- hiredis pkg-config file incorrectly ignores multiarch libdir spec'n [\#767](https://github.com/redis/hiredis/issues/767)
- Don't use -G to build shared object on Solaris [\#757](https://github.com/redis/hiredis/issues/757)
- error when make USE\_SSL=1 [\#748](https://github.com/redis/hiredis/issues/748)
- Allow to change SSL Mode [\#646](https://github.com/redis/hiredis/issues/646)
- hiredis/adapters/libevent.h memleak [\#618](https://github.com/redis/hiredis/issues/618)
- redisLibuvPoll crash when server closes the connetion [\#545](https://github.com/redis/hiredis/issues/545)
- about redisAsyncDisconnect question [\#518](https://github.com/redis/hiredis/issues/518)
- hiredis adapters libuv error for help [\#508](https://github.com/redis/hiredis/issues/508)
- API/ABI changes analysis [\#506](https://github.com/redis/hiredis/issues/506)
- Memory leak patch in Redis [\#502](https://github.com/redis/hiredis/issues/502)
- Remove the depth limitation [\#421](https://github.com/redis/hiredis/issues/421)
**Merged pull requests:**
- Move SSL management to a distinct private pointer [\#855](https://github.com/redis/hiredis/pull/855) ([michael-grunder](https://github.com/michael-grunder))
- Move include to sockcompat.h to maintain style [\#850](https://github.com/redis/hiredis/pull/850) ([michael-grunder](https://github.com/michael-grunder))
- Remove erroneous tag and add license to push example [\#849](https://github.com/redis/hiredis/pull/849) ([michael-grunder](https://github.com/michael-grunder))
- fix windows compiling with mingw [\#848](https://github.com/redis/hiredis/pull/848) ([rmalizia44](https://github.com/rmalizia44))
- Some Windows quality of life improvements. [\#846](https://github.com/redis/hiredis/pull/846) ([michael-grunder](https://github.com/michael-grunder))
- Use \_WIN32 define instead of WIN32 [\#845](https://github.com/redis/hiredis/pull/845) ([michael-grunder](https://github.com/michael-grunder))
- Non Linux CI fixes [\#844](https://github.com/redis/hiredis/pull/844) ([michael-grunder](https://github.com/michael-grunder))
- Resp3 oob push support [\#841](https://github.com/redis/hiredis/pull/841) ([michael-grunder](https://github.com/michael-grunder))
- fix \#785: defer TCP\_NODELAY in async tcp connections [\#836](https://github.com/redis/hiredis/pull/836) ([OmriSteiner](https://github.com/OmriSteiner))
- sdsrange overflow fix [\#830](https://github.com/redis/hiredis/pull/830) ([michael-grunder](https://github.com/michael-grunder))
- Use explicit pointer casting for c++ compatibility [\#826](https://github.com/redis/hiredis/pull/826) ([aureus1](https://github.com/aureus1))
- Document allocator injection and completeness fix in test.c [\#824](https://github.com/redis/hiredis/pull/824) ([michael-grunder](https://github.com/michael-grunder))
- Use unique names for allocator struct members [\#823](https://github.com/redis/hiredis/pull/823) ([michael-grunder](https://github.com/michael-grunder))
- New SSL API to replace redisSecureConnection\(\). [\#821](https://github.com/redis/hiredis/pull/821) ([yossigo](https://github.com/yossigo))
- Add logic to handle RESP3 push messages [\#819](https://github.com/redis/hiredis/pull/819) ([michael-grunder](https://github.com/michael-grunder))
- Use standrad isxdigit instead of custom helper function. [\#814](https://github.com/redis/hiredis/pull/814) ([tryfinally](https://github.com/tryfinally))
- Fix missing SSL build/install options. [\#812](https://github.com/redis/hiredis/pull/812) ([yossigo](https://github.com/yossigo))
- Add link to ABI tracker [\#808](https://github.com/redis/hiredis/pull/808) ([michael-grunder](https://github.com/michael-grunder))
- Resp3 verbatim string support [\#805](https://github.com/redis/hiredis/pull/805) ([michael-grunder](https://github.com/michael-grunder))
- Allow users to replace allocator and handle OOM everywhere. [\#800](https://github.com/redis/hiredis/pull/800) ([michael-grunder](https://github.com/michael-grunder))
- Remove nested depth limitation. [\#797](https://github.com/redis/hiredis/pull/797) ([michael-grunder](https://github.com/michael-grunder))
- Attempt to fix compilation on Solaris [\#796](https://github.com/redis/hiredis/pull/796) ([michael-grunder](https://github.com/michael-grunder))
- Support timeouts in libev adapater [\#795](https://github.com/redis/hiredis/pull/795) ([michael-grunder](https://github.com/michael-grunder))
- Fix pkgconfig when installing to a custom lib dir [\#793](https://github.com/redis/hiredis/pull/793) ([michael-grunder](https://github.com/michael-grunder))
- Fix USE\_SSL=1 make/cmake on OSX and CMake tests [\#789](https://github.com/redis/hiredis/pull/789) ([michael-grunder](https://github.com/michael-grunder))
- Use correct libuv call on Windows [\#784](https://github.com/redis/hiredis/pull/784) ([michael-grunder](https://github.com/michael-grunder))
- Added CMake package config and fixed hiredis\_ssl on Windows [\#783](https://github.com/redis/hiredis/pull/783) ([michael-grunder](https://github.com/michael-grunder))
- CMake: Set hiredis\_ssl shared object version. [\#780](https://github.com/redis/hiredis/pull/780) ([yossigo](https://github.com/yossigo))
- Win32 tests and timeout fix [\#776](https://github.com/redis/hiredis/pull/776) ([michael-grunder](https://github.com/michael-grunder))
- Provides an optional cleanup callback for async data. [\#768](https://github.com/redis/hiredis/pull/768) ([heronr](https://github.com/heronr))
- Housekeeping fixes [\#764](https://github.com/redis/hiredis/pull/764) ([michael-grunder](https://github.com/michael-grunder))
- install alloc.h [\#756](https://github.com/redis/hiredis/pull/756) ([ch1aki](https://github.com/ch1aki))
- fix spelling mistakes [\#746](https://github.com/redis/hiredis/pull/746) ([ShooterIT](https://github.com/ShooterIT))
- Free the reply in redisGetReply when passed NULL [\#741](https://github.com/redis/hiredis/pull/741) ([michael-grunder](https://github.com/michael-grunder))
- Fix dead code in sslLogCallback relating to should\_log variable. [\#737](https://github.com/redis/hiredis/pull/737) ([natoscott](https://github.com/natoscott))
- Fix typo in dict.c. [\#731](https://github.com/redis/hiredis/pull/731) ([Kevin-Xi](https://github.com/Kevin-Xi))
- Adding an option to DISABLE\_TESTS [\#727](https://github.com/redis/hiredis/pull/727) ([pbotros](https://github.com/pbotros))
- Update README with SSL support. [\#720](https://github.com/redis/hiredis/pull/720) ([yossigo](https://github.com/yossigo))
- Fixes leaks in unit tests [\#715](https://github.com/redis/hiredis/pull/715) ([michael-grunder](https://github.com/michael-grunder))
- SSL Tests [\#711](https://github.com/redis/hiredis/pull/711) ([yossigo](https://github.com/yossigo))
- SSL Reorganization [\#708](https://github.com/redis/hiredis/pull/708) ([yossigo](https://github.com/yossigo))
- Fix MSVC build. [\#706](https://github.com/redis/hiredis/pull/706) ([yossigo](https://github.com/yossigo))
- SSL: Properly report SSL\_connect\(\) errors. [\#702](https://github.com/redis/hiredis/pull/702) ([yossigo](https://github.com/yossigo))
- Silent SSL trace to stdout by default. [\#699](https://github.com/redis/hiredis/pull/699) ([yossigo](https://github.com/yossigo))
- Port RESP3 support from Redis. [\#697](https://github.com/redis/hiredis/pull/697) ([yossigo](https://github.com/yossigo))
- Removed whitespace before newline [\#691](https://github.com/redis/hiredis/pull/691) ([Miniwoffer](https://github.com/Miniwoffer))
- Add install adapters header files [\#688](https://github.com/redis/hiredis/pull/688) ([kevin1018](https://github.com/kevin1018))
- Remove unnecessary null check before free [\#684](https://github.com/redis/hiredis/pull/684) ([qlyoung](https://github.com/qlyoung))
- redisReaderGetReply leak memory [\#671](https://github.com/redis/hiredis/pull/671) ([movebean](https://github.com/movebean))
- fix timeout code in windows [\#670](https://github.com/redis/hiredis/pull/670) ([jman-krafton](https://github.com/jman-krafton))
- test: fix errstr matching for musl libc [\#665](https://github.com/redis/hiredis/pull/665) ([ghost](https://github.com/ghost))
- Windows: MinGW fixes and Windows Travis builders [\#663](https://github.com/redis/hiredis/pull/663) ([mbitsnbites](https://github.com/mbitsnbites))
- The setsockopt and getsockopt API diffs from BSD socket and WSA one [\#662](https://github.com/redis/hiredis/pull/662) ([dragonation](https://github.com/dragonation))
- Fix Compile Error On Windows \(Visual Studio\) [\#658](https://github.com/redis/hiredis/pull/658) ([jinjiazhang](https://github.com/jinjiazhang))
- Fix NXDOMAIN test case [\#653](https://github.com/redis/hiredis/pull/653) ([michael-grunder](https://github.com/michael-grunder))
- Add MinGW support [\#652](https://github.com/redis/hiredis/pull/652) ([mbitsnbites](https://github.com/mbitsnbites))
- SSL Support [\#645](https://github.com/redis/hiredis/pull/645) ([mnunberg](https://github.com/mnunberg))
- Fix Invalid argument after redisAsyncConnectUnix [\#644](https://github.com/redis/hiredis/pull/644) ([codehz](https://github.com/codehz))
- Makefile: use predefined AR [\#632](https://github.com/redis/hiredis/pull/632) ([Mic92](https://github.com/Mic92))
- FreeBSD build fix [\#628](https://github.com/redis/hiredis/pull/628) ([devnexen](https://github.com/devnexen))
- Fix errors not propagating properly with libuv.h. [\#624](https://github.com/redis/hiredis/pull/624) ([yossigo](https://github.com/yossigo))
- Update README.md [\#621](https://github.com/redis/hiredis/pull/621) ([Crunsher](https://github.com/Crunsher))
- Fix redisBufferRead documentation [\#620](https://github.com/redis/hiredis/pull/620) ([hacst](https://github.com/hacst))
- Add CPPFLAGS to REAL\_CFLAGS [\#614](https://github.com/redis/hiredis/pull/614) ([thomaslee](https://github.com/thomaslee))
- Update createArray to take size\_t [\#597](https://github.com/redis/hiredis/pull/597) ([justinbrewer](https://github.com/justinbrewer))
- fix common realloc mistake and add null check more [\#580](https://github.com/redis/hiredis/pull/580) ([charsyam](https://github.com/charsyam))
- Proper error reporting for connect failures [\#578](https://github.com/redis/hiredis/pull/578) ([mnunberg](https://github.com/mnunberg))
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
## [1.0.0-rc1](https://github.com/redis/hiredis/tree/v1.0.0-rc1) - (2020-07-29)
_Note: There were no changes to code between v1.0.0-rc1 and v1.0.0 so see v1.0.0 for changelog_
### 0.14.1 (2020-03-13)
* Adds safe allocation wrappers (CVE-2020-7105, #747, #752) (Michael Grunder)
### 0.14.0 (2018-09-25) ### 0.14.0 (2018-09-25)
**BREAKING CHANGES**:
* Change `redisReply.len` to `size_t`, as it denotes the the size of a string
User code should compare this to `size_t` values as well.
If it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before.
* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b]) * Make string2ll static to fix conflict with Redis (Tom Lee [c3188b])
* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537]) * Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537])
@ -196,4 +343,3 @@ The parser, standalone since v0.12.0, can now be compiled on Windows
### 0.10.0 ### 0.10.0
* See commit log. * See commit log.

View File

@ -4,6 +4,7 @@ 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)
MACRO(getVersionBit name) MACRO(getVersionBit name)
SET(VERSION_REGEX "^#define ${name} (.+)$") SET(VERSION_REGEX "^#define ${name} (.+)$")
@ -21,6 +22,9 @@ MESSAGE("Detected version: ${VERSION}")
PROJECT(hiredis VERSION "${VERSION}") PROJECT(hiredis VERSION "${VERSION}")
# Hiredis requires C99
SET(CMAKE_C_STANDARD 99)
SET(ENABLE_EXAMPLES OFF CACHE BOOL "Enable building hiredis examples") SET(ENABLE_EXAMPLES OFF CACHE BOOL "Enable building hiredis examples")
SET(hiredis_sources SET(hiredis_sources
@ -92,10 +96,10 @@ INSTALL(FILES hiredis.targets
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)
@ -126,7 +130,7 @@ 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})
@ -159,7 +163,7 @@ IF(ENABLE_SSL)
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)
@ -184,7 +188,12 @@ 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)
ADD_DEFINITIONS(-DHIREDIS_TEST_SSL=1)
TARGET_LINK_LIBRARIES(hiredis-test hiredis hiredis_ssl)
ELSE()
TARGET_LINK_LIBRARIES(hiredis-test hiredis)
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)
ENDIF() ENDIF()

View File

@ -1,6 +1,6 @@
[![Build Status](https://travis-ci.org/redis/hiredis.png)](https://travis-ci.org/redis/hiredis) [![Build Status](https://travis-ci.org/redis/hiredis.png)](https://travis-ci.org/redis/hiredis)
**This Readme reflects the latest changed in the master branch. See [v0.14.1](https://github.com/redis/hiredis/tree/v0.14.1) 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
@ -24,12 +24,31 @@ The library comes with multiple APIs. There is the
## Upgrading to `1.0.0` ## Upgrading to `1.0.0`
Version 1.0.0 marks a stable release of hiredis. Version 1.0.0 marks the first stable release of Hiredis.
It includes some minor breaking changes, mostly to make the exposed API more uniform and self-explanatory. It includes some minor breaking changes, mostly to make the exposed API more uniform and self-explanatory.
It also bundles the updated `sds` library, to sync up with upstream and Redis. It also bundles the updated `sds` library, to sync up with upstream and Redis.
For most applications a recompile against the new hiredis should be enough.
For code changes see the [Changelog](CHANGELOG.md). For code changes see the [Changelog](CHANGELOG.md).
_Note: As described below, a few member names have been changed but most applications should be able to upgrade with minor code changes and recompiling._
## IMPORTANT: Breaking changes from `0.14.1` -> `1.0.0`
* `redisContext` has two additional members (`free_privdata`, and `privctx`).
* `redisOptions.timeout` has been renamed to `redisOptions.connect_timeout`, and we've added `redisOptions.command_timeout`.
* `redisReplyObjectFunctions.createArray` now takes `size_t` instead of `int` for its length parameter.
## IMPORTANT: Breaking changes when upgrading from 0.13.x -> 0.14.x
Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now
protocol errors. This is consistent with the RESP specification. On 32-bit
platforms, the upper bound is lowered to `SIZE_MAX`.
Change `redisReply.len` to `size_t`, as it denotes the the size of a string
User code should compare this to `size_t` values as well. If it was used to
compare to other values, casting might be necessary or can be removed, if
casting was applied before.
## Upgrading from `<0.9.0` ## Upgrading from `<0.9.0`
Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing
@ -110,6 +129,8 @@ The standard replies that `redisCommand` are of the type `redisReply`. The
`type` field in the `redisReply` should be used to test what kind of reply `type` field in the `redisReply` should be used to test what kind of reply
was received: was received:
### RESP2
* **`REDIS_REPLY_STATUS`**: * **`REDIS_REPLY_STATUS`**:
* The command replied with a status reply. The status string can be accessed using `reply->str`. * The command replied with a status reply. The status string can be accessed using `reply->str`.
The length of this string can be accessed using `reply->len`. The length of this string can be accessed using `reply->len`.
@ -134,16 +155,51 @@ was received:
and can be accessed via `reply->element[..index..]`. and can be accessed via `reply->element[..index..]`.
Redis may reply with nested arrays but this is fully supported. Redis may reply with nested arrays but this is fully supported.
### RESP3
Hiredis also supports every new `RESP3` data type which are as follows. For more information about the protocol see the `RESP3` [specification.](https://github.com/antirez/RESP3/blob/master/spec.md)
* **`REDIS_REPLY_DOUBLE`**:
* The command replied with a double-precision floating point number.
The value is stored as a string in the `str` member, and can be converted with `strtod` or similar.
* **`REDIS_REPLY_BOOL`**:
* A boolean true/false reply.
The value is stored in the `integer` member and will be either `0` or `1`.
* **`REDIS_REPLY_MAP`**:
* 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.
* **`REDIS_REPLY_SET`**:
* An array response where each entry is unique.
Like the MAP type, the data is identical to an array response except there are no duplicate values.
* **`REDIS_REPLY_PUSH`**:
* An array that can be generated spontaneously by Redis.
This array response will always contain at least two subelements. The first contains the type of `PUSH` message (e.g. `message`, or `invalidate`), and the second being a sub-array with the `PUSH` payload itself.
* **`REDIS_REPLY_ATTR`**:
* An array structurally identical to a `MAP` but intended as meta-data about a reply.
_As of Redis 6.0.6 this reply type is not used in Redis_
* **`REDIS_REPLY_BIGNUM`**:
* A string representing an arbitrarily large signed or unsigned integer value.
The number will be encoded as a string in the `str` member of `redisReply`.
* **`REDIS_REPLY_VERB`**:
* 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).
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
contained in arrays and nested arrays, so there is no need for the user to contained in arrays and nested arrays, so there is no need for the user to
free the sub replies (it is actually harmful and will corrupt the memory). free the sub replies (it is actually harmful and will corrupt the memory).
**Important:** the current version of hiredis (0.10.0) frees replies when the **Important:** the current version of hiredis (1.0.0) frees replies when the
asynchronous API is used. This means you should not call `freeReplyObject` when asynchronous API is used. This means you should not call `freeReplyObject` when
you use this API. The reply is cleaned up by hiredis _after_ the callback you use this API. The reply is cleaned up by hiredis _after_ the callback
returns. This behavior will probably change in future releases, so make sure to returns. We may introduce a flag to make this configurable in future versions of the library.
keep an eye on the changelog when upgrading (see issue #39).
### Cleaning up ### Cleaning up
@ -595,6 +651,8 @@ hiredisResetAllocators();
## AUTHORS ## AUTHORS
Hiredis was written by Salvatore Sanfilippo (antirez at gmail), Salvatore Sanfilippo (antirez at gmail),\
Pieter Noordhuis (pcnoordhuis at gmail), and Michael Grunder Pieter Noordhuis (pcnoordhuis at gmail)\
(michael dot grunder at gmail) and is released under the BSD license. Michael Grunder (michael dot grunder at gmail)
_Hiredis is released under the BSD license._

View File

@ -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) {
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);
@ -154,7 +154,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

17
async.c
View File

@ -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);
@ -868,19 +873,19 @@ redisAsyncPushFn *redisAsyncSetPushCallback(redisAsyncContext *ac, redisAsyncPus
} }
int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) { int redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) {
if (!ac->c.timeout) { if (!ac->c.command_timeout) {
ac->c.timeout = hi_calloc(1, sizeof(tv)); ac->c.command_timeout = hi_calloc(1, sizeof(tv));
if (ac->c.timeout == NULL) { if (ac->c.command_timeout == NULL) {
__redisSetError(&ac->c, REDIS_ERR_OOM, "Out of memory"); __redisSetError(&ac->c, REDIS_ERR_OOM, "Out of memory");
__redisAsyncCopyError(ac); __redisAsyncCopyError(ac);
return REDIS_ERR; return REDIS_ERR;
} }
} }
if (tv.tv_sec != ac->c.timeout->tv_sec || if (tv.tv_sec != ac->c.command_timeout->tv_sec ||
tv.tv_usec != ac->c.timeout->tv_usec) tv.tv_usec != ac->c.command_timeout->tv_usec)
{ {
*ac->c.timeout = tv; *ac->c.command_timeout = tv;
} }
return REDIS_OK; return REDIS_OK;

View File

@ -54,15 +54,18 @@
} while(0); } while(0);
static inline void refreshTimeout(redisAsyncContext *ctx) { static inline void refreshTimeout(redisAsyncContext *ctx) {
if (ctx->c.timeout && ctx->ev.scheduleTimer && #define REDIS_TIMER_ISSET(tvp) \
(ctx->c.timeout->tv_sec || ctx->c.timeout->tv_usec)) { (tvp && ((tvp)->tv_sec || (tvp)->tv_usec))
ctx->ev.scheduleTimer(ctx->ev.data, *ctx->c.timeout);
// } else { #define REDIS_EL_TIMER(ac, tvp) \
// printf("Not scheduling timer.. (tmo=%p)\n", ctx->c.timeout); if ((ac)->ev.scheduleTimer && REDIS_TIMER_ISSET(tvp)) { \
// if (ctx->c.timeout){ (ac)->ev.scheduleTimer((ac)->ev.data, *(tvp)); \
// printf("tv_sec: %u. tv_usec: %u\n", ctx->c.timeout->tv_sec, }
// ctx->c.timeout->tv_usec);
// } if (ctx->c.flags & REDIS_CONNECTED) {
REDIS_EL_TIMER(ctx, ctx->c.command_timeout);
} else {
REDIS_EL_TIMER(ctx, ctx->c.connect_timeout);
} }
} }

View File

@ -47,7 +47,7 @@ int main (int argc, char **argv) {
REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379); REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
struct timeval tv = {0}; struct timeval tv = {0};
tv.tv_sec = 1; tv.tv_sec = 1;
options.timeout = &tv; options.connect_timeout = &tv;
redisAsyncContext *c = redisAsyncConnectWithOptions(&options); redisAsyncContext *c = redisAsyncConnectWithOptions(&options);

View File

@ -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
@ -97,6 +96,13 @@ void pushReplyHandler(void *privdata, void *r) {
freeReplyObject(reply); freeReplyObject(reply);
} }
/* We aren't actually freeing anything here, but it is included to show that we can
* have hiredis call our data destructor when freeing the context */
void privdata_dtor(void *privdata) {
unsigned int *icount = privdata;
printf("privdata_dtor(): In context privdata dtor (invalidations: %u)\n", *icount);
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
unsigned int j, invalidations = 0; unsigned int j, invalidations = 0;
redisContext *c; redisContext *c;
@ -108,6 +114,16 @@ int main(int argc, char **argv) {
redisOptions o = {0}; redisOptions o = {0};
REDIS_OPTIONS_SET_TCP(&o, hostname, port); REDIS_OPTIONS_SET_TCP(&o, hostname, port);
/* Set our context privdata to the address of our invalidation counter. Each
* time our PUSH handler is called, hiredis will pass the privdata for context.
*
* This could also be done after we create the context like so:
*
* c->privdata = &invalidations;
* c->free_privdata = privdata_dtor;
*/
REDIS_OPTIONS_SET_PRIVDATA(&o, &invalidations, privdata_dtor);
/* Set our custom PUSH message handler */ /* Set our custom PUSH message handler */
o.push_cb = pushReplyHandler; o.push_cb = pushReplyHandler;
@ -118,10 +134,6 @@ int main(int argc, char **argv) {
/* Enable RESP3 and turn on client tracking */ /* Enable RESP3 and turn on client tracking */
enableClientTracking(c); enableClientTracking(c);
/* Set our context privdata to the address of our invalidation counter. Each
* time our PUSH handler is called, hiredis will pass the privdata for context */
c->privdata = &invalidations;
/* Set some keys and then read them back. Once we do that, Redis will deliver /* Set some keys and then read them back. Once we do that, Redis will deliver
* invalidation push messages whenever the key is modified */ * invalidation push messages whenever the key is modified */
for (j = 0; j < KEY_COUNT; j++) { for (j = 0; j < KEY_COUNT; j++) {

View File

@ -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;
@ -33,7 +36,7 @@ int main(int argc, char **argv) {
struct timeval tv = { 1, 500000 }; // 1.5 seconds struct timeval tv = { 1, 500000 }; // 1.5 seconds
redisOptions options = {0}; redisOptions options = {0};
REDIS_OPTIONS_SET_TCP(&options, hostname, port); REDIS_OPTIONS_SET_TCP(&options, hostname, port);
options.timeout = &tv; options.connect_timeout = &tv;
c = redisConnectWithOptions(&options); c = redisConnectWithOptions(&options);
if (c == NULL || c->err) { if (c == NULL || c->err) {

View File

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

View File

@ -44,8 +44,11 @@
#include "async.h" #include "async.h"
#include "win32.h" #include "win32.h"
extern int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout);
extern int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout);
static redisContextFuncs redisContextDefaultFuncs = { static redisContextFuncs redisContextDefaultFuncs = {
.free_privdata = NULL, .free_privctx = NULL,
.async_read = redisAsyncRead, .async_read = redisAsyncRead,
.async_write = redisAsyncWrite, .async_write = redisAsyncWrite,
.read = redisNetRead, .read = redisNetRead,
@ -685,7 +688,7 @@ static void redisPushAutoFree(void *privdata, void *reply) {
freeReplyObject(reply); freeReplyObject(reply);
} }
static redisContext *redisContextInit(const redisOptions *options) { static redisContext *redisContextInit(void) {
redisContext *c; redisContext *c;
c = hi_calloc(1, sizeof(*c)); c = hi_calloc(1, sizeof(*c));
@ -694,13 +697,6 @@ static redisContext *redisContextInit(const redisOptions *options) {
c->funcs = &redisContextDefaultFuncs; c->funcs = &redisContextDefaultFuncs;
/* Set any user supplied RESP3 PUSH handler or use freeReplyObject
* as a default unless specifically flagged that we don't want one. */
if (options->push_cb != NULL)
redisSetPushCallback(c, options->push_cb);
else if (!(options->options & REDIS_OPT_NO_PUSH_AUTOFREE))
redisSetPushCallback(c, redisPushAutoFree);
c->obuf = sdsempty(); c->obuf = sdsempty();
c->reader = redisReaderCreate(); c->reader = redisReaderCreate();
c->fd = REDIS_INVALID_FD; c->fd = REDIS_INVALID_FD;
@ -709,7 +705,7 @@ static redisContext *redisContextInit(const redisOptions *options) {
redisFree(c); redisFree(c);
return NULL; return NULL;
} }
(void)options; /* options are used in other functions */
return c; return c;
} }
@ -723,11 +719,16 @@ void redisFree(redisContext *c) {
hi_free(c->tcp.host); hi_free(c->tcp.host);
hi_free(c->tcp.source_addr); hi_free(c->tcp.source_addr);
hi_free(c->unix_sock.path); hi_free(c->unix_sock.path);
hi_free(c->timeout); hi_free(c->connect_timeout);
hi_free(c->command_timeout);
hi_free(c->saddr); hi_free(c->saddr);
if (c->funcs->free_privdata) {
c->funcs->free_privdata(c->privdata); if (c->privdata && c->free_privdata)
} c->free_privdata(c->privdata);
if (c->funcs->free_privctx)
c->funcs->free_privctx(c->privctx);
memset(c, 0xff, sizeof(*c)); memset(c, 0xff, sizeof(*c));
hi_free(c); hi_free(c);
} }
@ -743,9 +744,9 @@ int redisReconnect(redisContext *c) {
c->err = 0; c->err = 0;
memset(c->errstr, '\0', strlen(c->errstr)); memset(c->errstr, '\0', strlen(c->errstr));
if (c->privdata && c->funcs->free_privdata) { if (c->privctx && c->funcs->free_privctx) {
c->funcs->free_privdata(c->privdata); c->funcs->free_privctx(c->privctx);
c->privdata = NULL; c->privctx = NULL;
} }
redisNetClose(c); redisNetClose(c);
@ -761,22 +762,28 @@ int redisReconnect(redisContext *c) {
return REDIS_ERR; return REDIS_ERR;
} }
int ret = REDIS_ERR;
if (c->connection_type == REDIS_CONN_TCP) { if (c->connection_type == REDIS_CONN_TCP) {
return redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port, ret = redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port,
c->timeout, c->tcp.source_addr); c->connect_timeout, c->tcp.source_addr);
} else if (c->connection_type == REDIS_CONN_UNIX) { } else if (c->connection_type == REDIS_CONN_UNIX) {
return redisContextConnectUnix(c, c->unix_sock.path, c->timeout); ret = redisContextConnectUnix(c, c->unix_sock.path, c->connect_timeout);
} else { } else {
/* Something bad happened here and shouldn't have. There isn't /* Something bad happened here and shouldn't have. There isn't
enough information in the context to reconnect. */ enough information in the context to reconnect. */
__redisSetError(c,REDIS_ERR_OTHER,"Not enough information to reconnect"); __redisSetError(c,REDIS_ERR_OTHER,"Not enough information to reconnect");
ret = REDIS_ERR;
} }
return REDIS_ERR; if (c->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
redisContextSetTimeout(c, *c->command_timeout);
}
return ret;
} }
redisContext *redisConnectWithOptions(const redisOptions *options) { redisContext *redisConnectWithOptions(const redisOptions *options) {
redisContext *c = redisContextInit(options); redisContext *c = redisContextInit();
if (c == NULL) { if (c == NULL) {
return NULL; return NULL;
} }
@ -790,13 +797,29 @@ redisContext *redisConnectWithOptions(const redisOptions *options) {
c->flags |= REDIS_NO_AUTO_FREE; c->flags |= REDIS_NO_AUTO_FREE;
} }
/* Set any user supplied RESP3 PUSH handler or use freeReplyObject
* as a default unless specifically flagged that we don't want one. */
if (options->push_cb != NULL)
redisSetPushCallback(c, options->push_cb);
else if (!(options->options & REDIS_OPT_NO_PUSH_AUTOFREE))
redisSetPushCallback(c, redisPushAutoFree);
c->privdata = options->privdata;
c->free_privdata = options->free_privdata;
if (redisContextUpdateConnectTimeout(c, options->connect_timeout) != REDIS_OK ||
redisContextUpdateCommandTimeout(c, options->command_timeout) != REDIS_OK) {
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
return c;
}
if (options->type == REDIS_CONN_TCP) { if (options->type == REDIS_CONN_TCP) {
redisContextConnectBindTcp(c, options->endpoint.tcp.ip, redisContextConnectBindTcp(c, options->endpoint.tcp.ip,
options->endpoint.tcp.port, options->timeout, options->endpoint.tcp.port, options->connect_timeout,
options->endpoint.tcp.source_addr); options->endpoint.tcp.source_addr);
} else if (options->type == REDIS_CONN_UNIX) { } else if (options->type == REDIS_CONN_UNIX) {
redisContextConnectUnix(c, options->endpoint.unix_socket, redisContextConnectUnix(c, options->endpoint.unix_socket,
options->timeout); options->connect_timeout);
} else if (options->type == REDIS_CONN_USERFD) { } else if (options->type == REDIS_CONN_USERFD) {
c->fd = options->endpoint.fd; c->fd = options->endpoint.fd;
c->flags |= REDIS_CONNECTED; c->flags |= REDIS_CONNECTED;
@ -805,6 +828,10 @@ redisContext *redisConnectWithOptions(const redisOptions *options) {
return NULL; return NULL;
} }
if (options->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
redisContextSetTimeout(c, *options->command_timeout);
}
return c; return c;
} }
@ -820,7 +847,7 @@ redisContext *redisConnect(const char *ip, int port) {
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) { redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) {
redisOptions options = {0}; redisOptions options = {0};
REDIS_OPTIONS_SET_TCP(&options, ip, port); REDIS_OPTIONS_SET_TCP(&options, ip, port);
options.timeout = &tv; options.connect_timeout = &tv;
return redisConnectWithOptions(&options); return redisConnectWithOptions(&options);
} }
@ -858,7 +885,7 @@ redisContext *redisConnectUnix(const char *path) {
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) { redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) {
redisOptions options = {0}; redisOptions options = {0};
REDIS_OPTIONS_SET_UNIX(&options, path); REDIS_OPTIONS_SET_UNIX(&options, path);
options.timeout = &tv; options.connect_timeout = &tv;
return redisConnectWithOptions(&options); return redisConnectWithOptions(&options);
} }

View File

@ -45,10 +45,10 @@ typedef long long ssize_t;
#include "sds.h" /* for sds */ #include "sds.h" /* for sds */
#include "alloc.h" /* for allocation wrappers */ #include "alloc.h" /* for allocation wrappers */
#define HIREDIS_MAJOR 0 #define HIREDIS_MAJOR 1
#define HIREDIS_MINOR 15 #define HIREDIS_MINOR 0
#define HIREDIS_PATCH 0 #define HIREDIS_PATCH 1
#define HIREDIS_SONAME 0.15 #define HIREDIS_SONAME 1.0.1-dev
/* Connection type can be blocking or non-blocking and is set in the /* Connection type can be blocking or non-blocking and is set in the
* least significant bit of the flags field in redisContext. */ * least significant bit of the flags field in redisContext. */
@ -176,8 +176,11 @@ typedef struct {
int type; int type;
/* bit field of REDIS_OPT_xxx */ /* bit field of REDIS_OPT_xxx */
int options; int options;
/* timeout value. if NULL, no timeout is used */ /* timeout value for connect operation. If NULL, no timeout is used */
const struct timeval *timeout; const struct timeval *connect_timeout;
/* timeout value for commands. If NULL, no timeout is used. This can be
* updated at runtime with redisSetTimeout/redisAsyncSetTimeout. */
const struct timeval *command_timeout;
union { union {
/** use this field for tcp/ip connections */ /** use this field for tcp/ip connections */
struct { struct {
@ -193,6 +196,10 @@ typedef struct {
redisFD fd; redisFD fd;
} endpoint; } endpoint;
/* Optional user defined data/destructor */
void *privdata;
void (*free_privdata)(void *);
/* A user defined PUSH message callback */ /* A user defined PUSH message callback */
redisPushFn *push_cb; redisPushFn *push_cb;
redisAsyncPushFn *async_push_cb; redisAsyncPushFn *async_push_cb;
@ -210,8 +217,12 @@ typedef struct {
(opts)->type = REDIS_CONN_UNIX; \ (opts)->type = REDIS_CONN_UNIX; \
(opts)->endpoint.unix_socket = path; (opts)->endpoint.unix_socket = path;
#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) \
(opts)->privdata = data; \
(opts)->free_privdata = dtor; \
typedef struct redisContextFuncs { typedef struct redisContextFuncs {
void (*free_privdata)(void *); void (*free_privctx)(void *);
void (*async_read)(struct redisAsyncContext *); void (*async_read)(struct redisAsyncContext *);
void (*async_write)(struct redisAsyncContext *); void (*async_write)(struct redisAsyncContext *);
ssize_t (*read)(struct redisContext *, char *, size_t); ssize_t (*read)(struct redisContext *, char *, size_t);
@ -230,7 +241,8 @@ typedef struct redisContext {
redisReader *reader; /* Protocol reader */ redisReader *reader; /* Protocol reader */
enum redisConnectionType connection_type; enum redisConnectionType connection_type;
struct timeval *timeout; struct timeval *connect_timeout;
struct timeval *command_timeout;
struct { struct {
char *host; char *host;
@ -243,11 +255,17 @@ typedef struct redisContext {
} unix_sock; } unix_sock;
/* For non-blocking connect */ /* For non-blocking connect */
struct sockadr *saddr; struct sockaddr *saddr;
size_t addrlen; size_t addrlen;
/* Additional private data for hiredis addons such as SSL */ /* Optional data and corresponding destructor users can use to provide
* context to a given redisContext. Not used by hiredis. */
void *privdata; void *privdata;
void (*free_privdata)(void *);
/* Internal context pointer presently used by hiredis to manage
* SSL connections. */
void *privctx;
/* An optional RESP3 PUSH handler */ /* An optional RESP3 PUSH handler */
redisPushFn *push_cb; redisPushFn *push_cb;

42
net.c
View File

@ -217,7 +217,7 @@ int redisSetTcpNoDelay(redisContext *c) {
static int redisContextTimeoutMsec(redisContext *c, long *result) static int redisContextTimeoutMsec(redisContext *c, long *result)
{ {
const struct timeval *timeout = c->timeout; const struct timeval *timeout = c->connect_timeout;
long msec = -1; long msec = -1;
/* Only use timeout when not NULL. */ /* Only use timeout when not NULL. */
@ -328,19 +328,35 @@ int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
return REDIS_OK; return REDIS_OK;
} }
static int _redisContextUpdateTimeout(redisContext *c, const struct timeval *timeout) { int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout) {
/* Same timeval struct, short circuit */ /* Same timeval struct, short circuit */
if (c->timeout == timeout) if (c->connect_timeout == timeout)
return REDIS_OK; return REDIS_OK;
/* Allocate context timeval if we need to */ /* Allocate context timeval if we need to */
if (c->timeout == NULL) { if (c->connect_timeout == NULL) {
c->timeout = hi_malloc(sizeof(*c->timeout)); c->connect_timeout = hi_malloc(sizeof(*c->connect_timeout));
if (c->timeout == NULL) if (c->connect_timeout == NULL)
return REDIS_ERR; return REDIS_ERR;
} }
memcpy(c->timeout, timeout, sizeof(*c->timeout)); memcpy(c->connect_timeout, timeout, sizeof(*c->connect_timeout));
return REDIS_OK;
}
int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout) {
/* Same timeval struct, short circuit */
if (c->command_timeout == timeout)
return REDIS_OK;
/* Allocate context timeval if we need to */
if (c->command_timeout == NULL) {
c->command_timeout = hi_malloc(sizeof(*c->command_timeout));
if (c->command_timeout == NULL)
return REDIS_ERR;
}
memcpy(c->command_timeout, timeout, sizeof(*c->command_timeout));
return REDIS_OK; return REDIS_OK;
} }
@ -376,11 +392,11 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
} }
if (timeout) { if (timeout) {
if (_redisContextUpdateTimeout(c, timeout) == REDIS_ERR) if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)
goto oom; goto oom;
} else { } else {
hi_free(c->timeout); hi_free(c->connect_timeout);
c->timeout = NULL; c->connect_timeout = NULL;
} }
if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) { if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
@ -549,11 +565,11 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time
} }
if (timeout) { if (timeout) {
if (_redisContextUpdateTimeout(c, timeout) == REDIS_ERR) if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR)
goto oom; goto oom;
} else { } else {
hi_free(c->timeout); hi_free(c->connect_timeout);
c->timeout = NULL; c->connect_timeout = NULL;
} }
if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK) if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)

22
ssl.c
View File

@ -267,7 +267,7 @@ error:
static int redisSSLConnect(redisContext *c, SSL *ssl) { static int redisSSLConnect(redisContext *c, SSL *ssl) {
if (c->privdata) { if (c->privctx) {
__redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated"); __redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated");
return REDIS_ERR; return REDIS_ERR;
} }
@ -288,14 +288,14 @@ static int redisSSLConnect(redisContext *c, SSL *ssl) {
ERR_clear_error(); ERR_clear_error();
int rv = SSL_connect(rssl->ssl); int rv = SSL_connect(rssl->ssl);
if (rv == 1) { if (rv == 1) {
c->privdata = rssl; c->privctx = rssl;
return REDIS_OK; return REDIS_OK;
} }
rv = SSL_get_error(rssl->ssl, rv); rv = SSL_get_error(rssl->ssl, rv);
if (((c->flags & REDIS_BLOCK) == 0) && if (((c->flags & REDIS_BLOCK) == 0) &&
(rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) { (rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {
c->privdata = rssl; c->privctx = rssl;
return REDIS_OK; return REDIS_OK;
} }
@ -337,7 +337,7 @@ int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx)
/* We want to verify that redisSSLConnect() won't fail on this, as it will /* We want to verify that redisSSLConnect() won't fail on this, as it will
* not own the SSL object in that case and we'll end up leaking. * not own the SSL object in that case and we'll end up leaking.
*/ */
if (c->privdata) if (c->privctx)
return REDIS_ERR; return REDIS_ERR;
SSL *ssl = SSL_new(redis_ssl_ctx->ssl_ctx); SSL *ssl = SSL_new(redis_ssl_ctx->ssl_ctx);
@ -381,8 +381,8 @@ static int maybeCheckWant(redisSSL *rssl, int rv) {
* Implementation of redisContextFuncs for SSL connections. * Implementation of redisContextFuncs for SSL connections.
*/ */
static void redisSSLFree(void *privdata){ static void redisSSLFree(void *privctx){
redisSSL *rsc = privdata; redisSSL *rsc = privctx;
if (!rsc) return; if (!rsc) return;
if (rsc->ssl) { if (rsc->ssl) {
@ -393,7 +393,7 @@ static void redisSSLFree(void *privdata){
} }
static ssize_t redisSSLRead(redisContext *c, char *buf, size_t bufcap) { static ssize_t redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
redisSSL *rssl = c->privdata; redisSSL *rssl = c->privctx;
int nread = SSL_read(rssl->ssl, buf, bufcap); int nread = SSL_read(rssl->ssl, buf, bufcap);
if (nread > 0) { if (nread > 0) {
@ -435,7 +435,7 @@ static ssize_t redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
} }
static ssize_t redisSSLWrite(redisContext *c) { static ssize_t redisSSLWrite(redisContext *c) {
redisSSL *rssl = c->privdata; redisSSL *rssl = c->privctx;
size_t len = rssl->lastLen ? rssl->lastLen : sdslen(c->obuf); size_t len = rssl->lastLen ? rssl->lastLen : sdslen(c->obuf);
int rv = SSL_write(rssl->ssl, c->obuf, len); int rv = SSL_write(rssl->ssl, c->obuf, len);
@ -458,7 +458,7 @@ static ssize_t redisSSLWrite(redisContext *c) {
static void redisSSLAsyncRead(redisAsyncContext *ac) { static void redisSSLAsyncRead(redisAsyncContext *ac) {
int rv; int rv;
redisSSL *rssl = ac->c.privdata; redisSSL *rssl = ac->c.privctx;
redisContext *c = &ac->c; redisContext *c = &ac->c;
rssl->wantRead = 0; rssl->wantRead = 0;
@ -488,7 +488,7 @@ static void redisSSLAsyncRead(redisAsyncContext *ac) {
static void redisSSLAsyncWrite(redisAsyncContext *ac) { static void redisSSLAsyncWrite(redisAsyncContext *ac) {
int rv, done = 0; int rv, done = 0;
redisSSL *rssl = ac->c.privdata; redisSSL *rssl = ac->c.privctx;
redisContext *c = &ac->c; redisContext *c = &ac->c;
rssl->pendingWrite = 0; rssl->pendingWrite = 0;
@ -517,7 +517,7 @@ static void redisSSLAsyncWrite(redisAsyncContext *ac) {
} }
redisContextFuncs redisContextSSLFuncs = { redisContextFuncs redisContextSSLFuncs = {
.free_privdata = redisSSLFree, .free_privctx = redisSSLFree,
.async_read = redisSSLAsyncRead, .async_read = redisSSLAsyncRead,
.async_write = redisSSLAsyncWrite, .async_write = redisSSLAsyncWrite,
.read = redisSSLRead, .read = redisSSLRead,

27
test.c
View File

@ -49,6 +49,10 @@ struct config {
} ssl; } ssl;
}; };
struct privdata {
int dtor_counter;
};
#ifdef HIREDIS_TEST_SSL #ifdef HIREDIS_TEST_SSL
redisSSLContext *_ssl_ctx = NULL; redisSSLContext *_ssl_ctx = NULL;
#endif #endif
@ -786,6 +790,27 @@ static void test_resp3_push_options(struct config config) {
redisAsyncFree(ac); redisAsyncFree(ac);
} }
void free_privdata(void *privdata) {
struct privdata *data = privdata;
data->dtor_counter++;
}
static void test_privdata_hooks(struct config config) {
struct privdata data = {0};
redisOptions options;
redisContext *c;
test("We can use redisOptions to set privdata: ");
options = get_redis_tcp_options(config);
REDIS_OPTIONS_SET_PRIVDATA(&options, &data, free_privdata);
assert((c = redisConnectWithOptions(&options)) != NULL);
test_cond(c->privdata == &data);
test("Our privdata destructor fires when we free the context: ");
redisFree(c);
test_cond(data.dtor_counter == 1);
}
static void test_blocking_connection(struct config config) { static void test_blocking_connection(struct config config) {
redisContext *c; redisContext *c;
redisReply *reply; redisReply *reply;
@ -871,6 +896,8 @@ static void test_blocking_connection(struct config config) {
if (major >= 6) test_resp3_push_handler(c); if (major >= 6) test_resp3_push_handler(c);
test_resp3_push_options(config); test_resp3_push_options(config);
test_privdata_hooks(config);
disconnect(c, 0); disconnect(c, 0);
} }