Remove hiredis so we can add it as a subtree
This commit is contained in:
parent
cad93ed273
commit
b110dc9bbb
8
deps/hiredis/.gitignore
vendored
8
deps/hiredis/.gitignore
vendored
@ -1,8 +0,0 @@
|
|||||||
/hiredis-test
|
|
||||||
/examples/hiredis-example*
|
|
||||||
/*.o
|
|
||||||
/*.so
|
|
||||||
/*.dylib
|
|
||||||
/*.a
|
|
||||||
/*.pc
|
|
||||||
*.dSYM
|
|
97
deps/hiredis/.travis.yml
vendored
97
deps/hiredis/.travis.yml
vendored
@ -1,97 +0,0 @@
|
|||||||
language: c
|
|
||||||
sudo: false
|
|
||||||
compiler:
|
|
||||||
- gcc
|
|
||||||
- clang
|
|
||||||
|
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
- osx
|
|
||||||
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- staging
|
|
||||||
- trying
|
|
||||||
- master
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- if [ "$TRAVIS_OS_NAME" == "osx" ] ; then brew update; brew install redis; fi
|
|
||||||
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- libc6-dbg
|
|
||||||
- libc6-dev
|
|
||||||
- libc6:i386
|
|
||||||
- libc6-dev-i386
|
|
||||||
- libc6-dbg:i386
|
|
||||||
- gcc-multilib
|
|
||||||
- g++-multilib
|
|
||||||
- valgrind
|
|
||||||
|
|
||||||
env:
|
|
||||||
- BITS="32"
|
|
||||||
- BITS="64"
|
|
||||||
|
|
||||||
script:
|
|
||||||
- EXTRA_CMAKE_OPTS="-DENABLE_EXAMPLES:BOOL=ON -DHIREDIS_SSL:BOOL=ON";
|
|
||||||
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
|
||||||
if [ "$BITS" == "32" ]; then
|
|
||||||
CFLAGS="-m32 -Werror";
|
|
||||||
CXXFLAGS="-m32 -Werror";
|
|
||||||
LDFLAGS="-m32";
|
|
||||||
EXTRA_CMAKE_OPTS=;
|
|
||||||
else
|
|
||||||
CFLAGS="-Werror";
|
|
||||||
CXXFLAGS="-Werror";
|
|
||||||
fi;
|
|
||||||
else
|
|
||||||
TEST_PREFIX="valgrind --track-origins=yes --leak-check=full";
|
|
||||||
if [ "$BITS" == "32" ]; then
|
|
||||||
CFLAGS="-m32 -Werror";
|
|
||||||
CXXFLAGS="-m32 -Werror";
|
|
||||||
LDFLAGS="-m32";
|
|
||||||
EXTRA_CMAKE_OPTS=;
|
|
||||||
else
|
|
||||||
CFLAGS="-Werror";
|
|
||||||
CXXFLAGS="-Werror";
|
|
||||||
fi;
|
|
||||||
fi;
|
|
||||||
export CFLAGS CXXFLAGS LDFLAGS TEST_PREFIX EXTRA_CMAKE_OPTS
|
|
||||||
- mkdir build/ && cd build/
|
|
||||||
- cmake .. ${EXTRA_CMAKE_OPTS}
|
|
||||||
- make VERBOSE=1
|
|
||||||
- ctest -V
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# Windows MinGW cross compile on Linux
|
|
||||||
- os: linux
|
|
||||||
dist: xenial
|
|
||||||
compiler: mingw
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- ninja-build
|
|
||||||
- gcc-mingw-w64-x86-64
|
|
||||||
- g++-mingw-w64-x86-64
|
|
||||||
script:
|
|
||||||
- mkdir build && cd build
|
|
||||||
- CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_BUILD_WITH_INSTALL_RPATH=on
|
|
||||||
- ninja -v
|
|
||||||
|
|
||||||
# Windows MSVC 2017
|
|
||||||
- os: windows
|
|
||||||
compiler: msvc
|
|
||||||
env:
|
|
||||||
- MATRIX_EVAL="CC=cl.exe && CXX=cl.exe"
|
|
||||||
before_install:
|
|
||||||
- eval "${MATRIX_EVAL}"
|
|
||||||
install:
|
|
||||||
- choco install ninja
|
|
||||||
script:
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmd.exe /C '"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" amd64 &&
|
|
||||||
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release &&
|
|
||||||
ninja -v'
|
|
||||||
- ctest -V
|
|
199
deps/hiredis/CHANGELOG.md
vendored
199
deps/hiredis/CHANGELOG.md
vendored
@ -1,199 +0,0 @@
|
|||||||
### 1.0.0 (unreleased)
|
|
||||||
|
|
||||||
**BREAKING CHANGES**:
|
|
||||||
|
|
||||||
* 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.
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
### 0.14.0 (2018-09-25)
|
|
||||||
|
|
||||||
* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b])
|
|
||||||
* Use -dynamiclib instead of -shared for OSX (Ryan Schmidt [a65537])
|
|
||||||
* Use string2ll from Redis w/added tests (Michael Grunder [7bef04, 60f622])
|
|
||||||
* Makefile - OSX compilation fixes (Ryan Schmidt [881fcb, 0e9af8])
|
|
||||||
* Remove redundant NULL checks (Justin Brewer [54acc8, 58e6b8])
|
|
||||||
* Fix bulk and multi-bulk length truncation (Justin Brewer [109197])
|
|
||||||
* Fix SIGSEGV in OpenBSD by checking for NULL before calling freeaddrinfo (Justin Brewer [546d94])
|
|
||||||
* Several POSIX compatibility fixes (Justin Brewer [bbeab8, 49bbaa, d1c1b6])
|
|
||||||
* Makefile - Compatibility fixes (Dimitri Vorobiev [3238cf, 12a9d1])
|
|
||||||
* Makefile - Fix make install on FreeBSD (Zach Shipko [a2ef2b])
|
|
||||||
* Makefile - don't assume $(INSTALL) is cp (Igor Gnatenko [725a96])
|
|
||||||
* Separate side-effect causing function from assert and small cleanup (amallia [b46413, 3c3234])
|
|
||||||
* Don't send negative values to `__redisAsyncCommand` (Frederik Deweerdt [706129])
|
|
||||||
* Fix leak if setsockopt fails (Frederik Deweerdt [e21c9c])
|
|
||||||
* Fix libevent leak (zfz [515228])
|
|
||||||
* Clean up GCC warning (Ichito Nagata [2ec774])
|
|
||||||
* Keep track of errno in `__redisSetErrorFromErrno()` as snprintf may use it (Jin Qing [25cd88])
|
|
||||||
* Solaris compilation fix (Donald Whyte [41b07d])
|
|
||||||
* Reorder linker arguments when building examples (Tustfarm-heart [06eedd])
|
|
||||||
* Keep track of subscriptions in case of rapid subscribe/unsubscribe (Hyungjin Kim [073dc8, be76c5, d46999])
|
|
||||||
* libuv use after free fix (Paul Scott [cbb956])
|
|
||||||
* Properly close socket fd on reconnect attempt (WSL [64d1ec])
|
|
||||||
* Skip valgrind in OSX tests (Jan-Erik Rediger [9deb78])
|
|
||||||
* Various updates for Travis testing OSX (Ted Nyman [fa3774, 16a459, bc0ea5])
|
|
||||||
* Update libevent (Chris Xin [386802])
|
|
||||||
* Change sds.h for building in C++ projects (Ali Volkan ATLI [f5b32e])
|
|
||||||
* Use proper format specifier in redisFormatSdsCommandArgv (Paulino Huerta, Jan-Erik Rediger [360a06, 8655a6])
|
|
||||||
* Better handling of NULL reply in example code (Jan-Erik Rediger [1b8ed3])
|
|
||||||
* Prevent overflow when formatting an error (Jan-Erik Rediger [0335cb])
|
|
||||||
* Compatibility fix for strerror_r (Tom Lee [bb1747])
|
|
||||||
* Properly detect integer parse/overflow errors (Justin Brewer [93421f])
|
|
||||||
* Adds CI for Windows and cygwin fixes (owent, [6c53d6, 6c3e40])
|
|
||||||
* Catch a buffer overflow when formatting the error message
|
|
||||||
* Import latest upstream sds. This breaks applications that are linked against the old hiredis v0.13
|
|
||||||
* Fix warnings, when compiled with -Wshadow
|
|
||||||
* Make hiredis compile in Cygwin on Windows, now CI-tested
|
|
||||||
* 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`.
|
|
||||||
|
|
||||||
* Remove backwards compatibility macro's
|
|
||||||
|
|
||||||
This removes the following old function aliases, use the new name now:
|
|
||||||
|
|
||||||
| Old | New |
|
|
||||||
| --------------------------- | ---------------------- |
|
|
||||||
| redisReplyReaderCreate | redisReaderCreate |
|
|
||||||
| redisReplyReaderCreate | redisReaderCreate |
|
|
||||||
| redisReplyReaderFree | redisReaderFree |
|
|
||||||
| redisReplyReaderFeed | redisReaderFeed |
|
|
||||||
| redisReplyReaderGetReply | redisReaderGetReply |
|
|
||||||
| redisReplyReaderSetPrivdata | redisReaderSetPrivdata |
|
|
||||||
| redisReplyReaderGetObject | redisReaderGetObject |
|
|
||||||
| redisReplyReaderGetError | redisReaderGetError |
|
|
||||||
|
|
||||||
* The `DEBUG` variable in the Makefile was renamed to `DEBUG_FLAGS`
|
|
||||||
|
|
||||||
Previously it broke some builds for people that had `DEBUG` set to some arbitrary value,
|
|
||||||
due to debugging other software.
|
|
||||||
By renaming we avoid unintentional name clashes.
|
|
||||||
|
|
||||||
Simply rename `DEBUG` to `DEBUG_FLAGS` in your environment to make it working again.
|
|
||||||
|
|
||||||
### 0.13.3 (2015-09-16)
|
|
||||||
|
|
||||||
* Revert "Clear `REDIS_CONNECTED` flag when connection is closed".
|
|
||||||
* Make tests pass on FreeBSD (Thanks, Giacomo Olgeni)
|
|
||||||
|
|
||||||
|
|
||||||
If the `REDIS_CONNECTED` flag is cleared,
|
|
||||||
the async onDisconnect callback function will never be called.
|
|
||||||
This causes problems as the disconnect is never reported back to the user.
|
|
||||||
|
|
||||||
### 0.13.2 (2015-08-25)
|
|
||||||
|
|
||||||
* Prevent crash on pending replies in async code (Thanks, @switch-st)
|
|
||||||
* Clear `REDIS_CONNECTED` flag when connection is closed (Thanks, Jerry Jacobs)
|
|
||||||
* Add MacOS X addapter (Thanks, @dizzus)
|
|
||||||
* Add Qt adapter (Thanks, Pietro Cerutti)
|
|
||||||
* Add Ivykis adapter (Thanks, Gergely Nagy)
|
|
||||||
|
|
||||||
All adapters are provided as is and are only tested where possible.
|
|
||||||
|
|
||||||
### 0.13.1 (2015-05-03)
|
|
||||||
|
|
||||||
This is a bug fix release.
|
|
||||||
The new `reconnect` method introduced new struct members, which clashed with pre-defined names in pre-C99 code.
|
|
||||||
Another commit forced C99 compilation just to make it work, but of course this is not desirable for outside projects.
|
|
||||||
Other non-C99 code can now use hiredis as usual again.
|
|
||||||
Sorry for the inconvenience.
|
|
||||||
|
|
||||||
* Fix memory leak in async reply handling (Salvatore Sanfilippo)
|
|
||||||
* Rename struct member to avoid name clash with pre-c99 code (Alex Balashov, ncopa)
|
|
||||||
|
|
||||||
### 0.13.0 (2015-04-16)
|
|
||||||
|
|
||||||
This release adds a minimal Windows compatibility layer.
|
|
||||||
The parser, standalone since v0.12.0, can now be compiled on Windows
|
|
||||||
(and thus used in other client libraries as well)
|
|
||||||
|
|
||||||
* Windows compatibility layer for parser code (tzickel)
|
|
||||||
* Properly escape data printed to PKGCONF file (Dan Skorupski)
|
|
||||||
* Fix tests when assert() undefined (Keith Bennett, Matt Stancliff)
|
|
||||||
* Implement a reconnect method for the client context, this changes the structure of `redisContext` (Aaron Bedra)
|
|
||||||
|
|
||||||
### 0.12.1 (2015-01-26)
|
|
||||||
|
|
||||||
* Fix `make install`: DESTDIR support, install all required files, install PKGCONF in proper location
|
|
||||||
* Fix `make test` as 32 bit build on 64 bit platform
|
|
||||||
|
|
||||||
### 0.12.0 (2015-01-22)
|
|
||||||
|
|
||||||
* Add optional KeepAlive support
|
|
||||||
|
|
||||||
* Try again on EINTR errors
|
|
||||||
|
|
||||||
* Add libuv adapter
|
|
||||||
|
|
||||||
* Add IPv6 support
|
|
||||||
|
|
||||||
* Remove possibility of multiple close on same fd
|
|
||||||
|
|
||||||
* Add ability to bind source address on connect
|
|
||||||
|
|
||||||
* Add redisConnectFd() and redisFreeKeepFd()
|
|
||||||
|
|
||||||
* Fix getaddrinfo() memory leak
|
|
||||||
|
|
||||||
* Free string if it is unused (fixes memory leak)
|
|
||||||
|
|
||||||
* Improve redisAppendCommandArgv performance 2.5x
|
|
||||||
|
|
||||||
* Add support for SO_REUSEADDR
|
|
||||||
|
|
||||||
* Fix redisvFormatCommand format parsing
|
|
||||||
|
|
||||||
* Add GLib 2.0 adapter
|
|
||||||
|
|
||||||
* Refactor reading code into read.c
|
|
||||||
|
|
||||||
* Fix errno error buffers to not clobber errors
|
|
||||||
|
|
||||||
* Generate pkgconf during build
|
|
||||||
|
|
||||||
* Silence _BSD_SOURCE warnings
|
|
||||||
|
|
||||||
* Improve digit counting for multibulk creation
|
|
||||||
|
|
||||||
|
|
||||||
### 0.11.0
|
|
||||||
|
|
||||||
* Increase the maximum multi-bulk reply depth to 7.
|
|
||||||
|
|
||||||
* Increase the read buffer size from 2k to 16k.
|
|
||||||
|
|
||||||
* Use poll(2) instead of select(2) to support large fds (>= 1024).
|
|
||||||
|
|
||||||
### 0.10.1
|
|
||||||
|
|
||||||
* Makefile overhaul. Important to check out if you override one or more
|
|
||||||
variables using environment variables or via arguments to the "make" tool.
|
|
||||||
|
|
||||||
* Issue #45: Fix potential memory leak for a multi bulk reply with 0 elements
|
|
||||||
being created by the default reply object functions.
|
|
||||||
|
|
||||||
* Issue #43: Don't crash in an asynchronous context when Redis returns an error
|
|
||||||
reply after the connection has been made (this happens when the maximum
|
|
||||||
number of connections is reached).
|
|
||||||
|
|
||||||
### 0.10.0
|
|
||||||
|
|
||||||
* See commit log.
|
|
||||||
|
|
90
deps/hiredis/CMakeLists.txt
vendored
90
deps/hiredis/CMakeLists.txt
vendored
@ -1,90 +0,0 @@
|
|||||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.4.0)
|
|
||||||
INCLUDE(GNUInstallDirs)
|
|
||||||
PROJECT(hiredis)
|
|
||||||
|
|
||||||
OPTION(ENABLE_SSL "Build hiredis_ssl for SSL support" OFF)
|
|
||||||
|
|
||||||
MACRO(getVersionBit name)
|
|
||||||
SET(VERSION_REGEX "^#define ${name} (.+)$")
|
|
||||||
FILE(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/hiredis.h"
|
|
||||||
VERSION_BIT REGEX ${VERSION_REGEX})
|
|
||||||
STRING(REGEX REPLACE ${VERSION_REGEX} "\\1" ${name} "${VERSION_BIT}")
|
|
||||||
ENDMACRO(getVersionBit)
|
|
||||||
|
|
||||||
getVersionBit(HIREDIS_MAJOR)
|
|
||||||
getVersionBit(HIREDIS_MINOR)
|
|
||||||
getVersionBit(HIREDIS_PATCH)
|
|
||||||
getVersionBit(HIREDIS_SONAME)
|
|
||||||
SET(VERSION "${HIREDIS_MAJOR}.${HIREDIS_MINOR}.${HIREDIS_PATCH}")
|
|
||||||
MESSAGE("Detected version: ${VERSION}")
|
|
||||||
|
|
||||||
PROJECT(hiredis VERSION "${VERSION}")
|
|
||||||
|
|
||||||
SET(ENABLE_EXAMPLES OFF CACHE BOOL "Enable building hiredis examples")
|
|
||||||
|
|
||||||
ADD_LIBRARY(hiredis SHARED
|
|
||||||
async.c
|
|
||||||
dict.c
|
|
||||||
hiredis.c
|
|
||||||
net.c
|
|
||||||
read.c
|
|
||||||
sds.c
|
|
||||||
sockcompat.c)
|
|
||||||
|
|
||||||
SET_TARGET_PROPERTIES(hiredis
|
|
||||||
PROPERTIES
|
|
||||||
VERSION "${HIREDIS_SONAME}")
|
|
||||||
IF(WIN32 OR MINGW)
|
|
||||||
TARGET_LINK_LIBRARIES(hiredis PRIVATE ws2_32)
|
|
||||||
ENDIF()
|
|
||||||
TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC .)
|
|
||||||
|
|
||||||
CONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY)
|
|
||||||
|
|
||||||
INSTALL(TARGETS hiredis
|
|
||||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}")
|
|
||||||
|
|
||||||
INSTALL(FILES hiredis.h read.h sds.h async.h
|
|
||||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
|
|
||||||
|
|
||||||
INSTALL(DIRECTORY adapters
|
|
||||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
|
|
||||||
|
|
||||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis.pc
|
|
||||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
|
||||||
|
|
||||||
IF(ENABLE_SSL)
|
|
||||||
IF (NOT OPENSSL_ROOT_DIR)
|
|
||||||
IF (APPLE)
|
|
||||||
SET(OPENSSL_ROOT_DIR "/usr/local/opt/openssl")
|
|
||||||
ENDIF()
|
|
||||||
ENDIF()
|
|
||||||
FIND_PACKAGE(OpenSSL REQUIRED)
|
|
||||||
ADD_LIBRARY(hiredis_ssl SHARED
|
|
||||||
ssl.c)
|
|
||||||
TARGET_INCLUDE_DIRECTORIES(hiredis_ssl PRIVATE "${OPENSSL_INCLUDE_DIR}")
|
|
||||||
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE ${OPENSSL_LIBRARIES})
|
|
||||||
CONFIGURE_FILE(hiredis_ssl.pc.in hiredis_ssl.pc @ONLY)
|
|
||||||
|
|
||||||
INSTALL(TARGETS hiredis_ssl
|
|
||||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}")
|
|
||||||
|
|
||||||
INSTALL(FILES hiredis_ssl.h
|
|
||||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
|
|
||||||
|
|
||||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl.pc
|
|
||||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
IF(NOT (WIN32 OR MINGW))
|
|
||||||
ENABLE_TESTING()
|
|
||||||
ADD_EXECUTABLE(hiredis-test test.c)
|
|
||||||
TARGET_LINK_LIBRARIES(hiredis-test hiredis)
|
|
||||||
ADD_TEST(NAME hiredis-test
|
|
||||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh)
|
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
# Add examples
|
|
||||||
IF(ENABLE_EXAMPLES)
|
|
||||||
ADD_SUBDIRECTORY(examples)
|
|
||||||
ENDIF(ENABLE_EXAMPLES)
|
|
29
deps/hiredis/COPYING
vendored
29
deps/hiredis/COPYING
vendored
@ -1,29 +0,0 @@
|
|||||||
Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
to endorse or promote products derived from this software without specific
|
|
||||||
prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
273
deps/hiredis/Makefile
vendored
273
deps/hiredis/Makefile
vendored
@ -1,273 +0,0 @@
|
|||||||
# Hiredis Makefile
|
|
||||||
# Copyright (C) 2010-2011 Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
# Copyright (C) 2010-2011 Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
# This file is released under the BSD license, see the COPYING file
|
|
||||||
|
|
||||||
OBJ=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
|
|
||||||
ifeq ($(USE_SSL),1)
|
|
||||||
EXAMPLES+=hiredis-example-ssl hiredis-example-libevent-ssl
|
|
||||||
endif
|
|
||||||
TESTS=hiredis-test
|
|
||||||
LIBNAME=libhiredis
|
|
||||||
SSL_LIBNAME=libhiredis_ssl
|
|
||||||
PKGCONFNAME=hiredis.pc
|
|
||||||
SSL_PKGCONFNAME=hiredis_ssl.pc
|
|
||||||
|
|
||||||
HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}')
|
|
||||||
HIREDIS_MINOR=$(shell grep HIREDIS_MINOR hiredis.h | awk '{print $$3}')
|
|
||||||
HIREDIS_PATCH=$(shell grep HIREDIS_PATCH hiredis.h | awk '{print $$3}')
|
|
||||||
HIREDIS_SONAME=$(shell grep HIREDIS_SONAME hiredis.h | awk '{print $$3}')
|
|
||||||
|
|
||||||
# Installation related variables and target
|
|
||||||
PREFIX?=/usr/local
|
|
||||||
INCLUDE_PATH?=include/hiredis
|
|
||||||
LIBRARY_PATH?=lib
|
|
||||||
PKGCONF_PATH?=pkgconfig
|
|
||||||
INSTALL_INCLUDE_PATH= $(DESTDIR)$(PREFIX)/$(INCLUDE_PATH)
|
|
||||||
INSTALL_LIBRARY_PATH= $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH)
|
|
||||||
INSTALL_PKGCONF_PATH= $(INSTALL_LIBRARY_PATH)/$(PKGCONF_PATH)
|
|
||||||
|
|
||||||
# redis-server configuration used for testing
|
|
||||||
REDIS_PORT=56379
|
|
||||||
REDIS_SERVER=redis-server
|
|
||||||
define REDIS_TEST_CONFIG
|
|
||||||
daemonize yes
|
|
||||||
pidfile /tmp/hiredis-test-redis.pid
|
|
||||||
port $(REDIS_PORT)
|
|
||||||
bind 127.0.0.1
|
|
||||||
unixsocket /tmp/hiredis-test-redis.sock
|
|
||||||
endef
|
|
||||||
export REDIS_TEST_CONFIG
|
|
||||||
|
|
||||||
# Fallback to gcc when $CC is not in $PATH.
|
|
||||||
CC:=$(shell sh -c 'type $${CC%% *} >/dev/null 2>/dev/null && echo $(CC) || echo gcc')
|
|
||||||
CXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++')
|
|
||||||
OPTIMIZATION?=-O3
|
|
||||||
WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings -Wno-missing-field-initializers
|
|
||||||
DEBUG_FLAGS?= -g -ggdb
|
|
||||||
REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CPPFLAGS) $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS)
|
|
||||||
REAL_LDFLAGS=$(LDFLAGS)
|
|
||||||
|
|
||||||
DYLIBSUFFIX=so
|
|
||||||
STLIBSUFFIX=a
|
|
||||||
DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME)
|
|
||||||
DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
|
|
||||||
DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)
|
|
||||||
SSL_DYLIBNAME=$(SSL_LIBNAME).$(DYLIBSUFFIX)
|
|
||||||
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME)
|
|
||||||
STLIBNAME=$(LIBNAME).$(STLIBSUFFIX)
|
|
||||||
SSL_STLIBNAME=$(SSL_LIBNAME).$(STLIBSUFFIX)
|
|
||||||
STLIB_MAKE_CMD=$(AR) rcs
|
|
||||||
|
|
||||||
# Platform-specific overrides
|
|
||||||
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
|
||||||
|
|
||||||
USE_SSL?=0
|
|
||||||
|
|
||||||
# This is required for test.c only
|
|
||||||
ifeq ($(USE_SSL),1)
|
|
||||||
CFLAGS+=-DHIREDIS_TEST_SSL
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(uname_S),Linux)
|
|
||||||
SSL_LDFLAGS=-lssl -lcrypto
|
|
||||||
else
|
|
||||||
OPENSSL_PREFIX?=/usr/local/opt/openssl
|
|
||||||
CFLAGS+=-I$(OPENSSL_PREFIX)/include
|
|
||||||
SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(uname_S),SunOS)
|
|
||||||
REAL_LDFLAGS+= -ldl -lnsl -lsocket
|
|
||||||
DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)
|
|
||||||
endif
|
|
||||||
ifeq ($(uname_S),Darwin)
|
|
||||||
DYLIBSUFFIX=dylib
|
|
||||||
DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX)
|
|
||||||
DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
|
|
||||||
endif
|
|
||||||
|
|
||||||
all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME)
|
|
||||||
ifeq ($(USE_SSL),1)
|
|
||||||
all: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME)
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Deps (use make dep to generate this)
|
|
||||||
async.o: async.c fmacros.h async.h hiredis.h read.h sds.h net.h dict.c dict.h
|
|
||||||
dict.o: dict.c fmacros.h dict.h
|
|
||||||
hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h net.h win32.h
|
|
||||||
net.o: net.c fmacros.h net.h hiredis.h read.h sds.h sockcompat.h win32.h
|
|
||||||
read.o: read.c fmacros.h read.h sds.h
|
|
||||||
sds.o: sds.c sds.h
|
|
||||||
sockcompat.o: sockcompat.c sockcompat.h
|
|
||||||
ssl.o: ssl.c hiredis.h
|
|
||||||
test.o: test.c fmacros.h hiredis.h read.h sds.h
|
|
||||||
|
|
||||||
$(DYLIBNAME): $(OBJ)
|
|
||||||
$(DYLIB_MAKE_CMD) -o $(DYLIBNAME) $(OBJ) $(REAL_LDFLAGS)
|
|
||||||
|
|
||||||
$(STLIBNAME): $(OBJ)
|
|
||||||
$(STLIB_MAKE_CMD) $(STLIBNAME) $(OBJ)
|
|
||||||
|
|
||||||
$(SSL_DYLIBNAME): $(SSL_OBJ)
|
|
||||||
$(DYLIB_MAKE_CMD) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(SSL_LDFLAGS)
|
|
||||||
|
|
||||||
$(SSL_STLIBNAME): $(SSL_OBJ)
|
|
||||||
$(STLIB_MAKE_CMD) $(SSL_STLIBNAME) $(SSL_OBJ)
|
|
||||||
|
|
||||||
dynamic: $(DYLIBNAME)
|
|
||||||
static: $(STLIBNAME)
|
|
||||||
ifeq ($(USE_SSL),1)
|
|
||||||
dynamic: $(SSL_DYLIBNAME)
|
|
||||||
static: $(SSL_STLIBNAME)
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Binaries:
|
|
||||||
hiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME)
|
|
||||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(REAL_LDFLAGS)
|
|
||||||
|
|
||||||
hiredis-example-libevent-ssl: examples/example-libevent-ssl.c adapters/libevent.h $(STLIBNAME) $(SSL_STLIBNAME)
|
|
||||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS)
|
|
||||||
|
|
||||||
hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME)
|
|
||||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -lev $(STLIBNAME) $(REAL_LDFLAGS)
|
|
||||||
|
|
||||||
hiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME)
|
|
||||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME) $(REAL_LDFLAGS)
|
|
||||||
|
|
||||||
hiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME)
|
|
||||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -livykis $(STLIBNAME) $(REAL_LDFLAGS)
|
|
||||||
|
|
||||||
hiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME)
|
|
||||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -framework CoreFoundation $(STLIBNAME) $(REAL_LDFLAGS)
|
|
||||||
|
|
||||||
hiredis-example-ssl: examples/example-ssl.c $(STLIBNAME) $(SSL_STLIBNAME)
|
|
||||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS)
|
|
||||||
|
|
||||||
ifndef AE_DIR
|
|
||||||
hiredis-example-ae:
|
|
||||||
@echo "Please specify AE_DIR (e.g. <redis repository>/src)"
|
|
||||||
@false
|
|
||||||
else
|
|
||||||
hiredis-example-ae: examples/example-ae.c adapters/ae.h $(STLIBNAME)
|
|
||||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(AE_DIR) $< $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o $(AE_DIR)/../deps/jemalloc/lib/libjemalloc.a -pthread $(STLIBNAME)
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifndef LIBUV_DIR
|
|
||||||
hiredis-example-libuv:
|
|
||||||
@echo "Please specify LIBUV_DIR (e.g. ../libuv/)"
|
|
||||||
@false
|
|
||||||
else
|
|
||||||
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)
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(and $(QT_MOC),$(QT_INCLUDE_DIR),$(QT_LIBRARY_DIR)),)
|
|
||||||
hiredis-example-qt:
|
|
||||||
@echo "Please specify QT_MOC, QT_INCLUDE_DIR AND QT_LIBRARY_DIR"
|
|
||||||
@false
|
|
||||||
else
|
|
||||||
hiredis-example-qt: examples/example-qt.cpp adapters/qt.h $(STLIBNAME)
|
|
||||||
$(QT_MOC) adapters/qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \
|
|
||||||
$(CXX) -x c++ -o qt-adapter-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore
|
|
||||||
$(QT_MOC) examples/example-qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \
|
|
||||||
$(CXX) -x c++ -o qt-example-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore
|
|
||||||
$(CXX) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore -L$(QT_LIBRARY_DIR) qt-adapter-moc.o qt-example-moc.o $< -pthread $(STLIBNAME) -lQtCore
|
|
||||||
endif
|
|
||||||
|
|
||||||
hiredis-example: examples/example.c $(STLIBNAME)
|
|
||||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS)
|
|
||||||
|
|
||||||
examples: $(EXAMPLES)
|
|
||||||
|
|
||||||
TEST_LIBS = $(STLIBNAME)
|
|
||||||
ifeq ($(USE_SSL),1)
|
|
||||||
TEST_LIBS += $(SSL_STLIBNAME) -lssl -lcrypto -lpthread
|
|
||||||
endif
|
|
||||||
hiredis-test: test.o $(TEST_LIBS)
|
|
||||||
|
|
||||||
hiredis-%: %.o $(STLIBNAME)
|
|
||||||
$(CC) $(REAL_CFLAGS) -o $@ $< $(TEST_LIBS) $(REAL_LDFLAGS)
|
|
||||||
|
|
||||||
test: hiredis-test
|
|
||||||
./hiredis-test
|
|
||||||
|
|
||||||
check: hiredis-test
|
|
||||||
TEST_SSL=$(USE_SSL) ./test.sh
|
|
||||||
|
|
||||||
.c.o:
|
|
||||||
$(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $<
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf $(DYLIBNAME) $(STLIBNAME) $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov
|
|
||||||
|
|
||||||
dep:
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) -MM *.c
|
|
||||||
|
|
||||||
INSTALL?= cp -pPR
|
|
||||||
|
|
||||||
$(PKGCONFNAME): hiredis.h
|
|
||||||
@echo "Generating $@ for pkgconfig..."
|
|
||||||
@echo prefix=$(PREFIX) > $@
|
|
||||||
@echo exec_prefix=\$${prefix} >> $@
|
|
||||||
@echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@
|
|
||||||
@echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@
|
|
||||||
@echo >> $@
|
|
||||||
@echo Name: hiredis >> $@
|
|
||||||
@echo Description: Minimalistic C client library for Redis. >> $@
|
|
||||||
@echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@
|
|
||||||
@echo Libs: -L\$${libdir} -lhiredis >> $@
|
|
||||||
@echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@
|
|
||||||
|
|
||||||
$(SSL_PKGCONFNAME): hiredis.h
|
|
||||||
@echo "Generating $@ for pkgconfig..."
|
|
||||||
@echo prefix=$(PREFIX) > $@
|
|
||||||
@echo exec_prefix=\$${prefix} >> $@
|
|
||||||
@echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@
|
|
||||||
@echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@
|
|
||||||
@echo >> $@
|
|
||||||
@echo Name: hiredis_ssl >> $@
|
|
||||||
@echo Description: SSL Support for hiredis. >> $@
|
|
||||||
@echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@
|
|
||||||
@echo Requires: hiredis >> $@
|
|
||||||
@echo Libs: -L\$${libdir} -lhiredis_ssl >> $@
|
|
||||||
@echo Libs.private: -lssl -lcrypto >> $@
|
|
||||||
|
|
||||||
install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME)
|
|
||||||
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH)
|
|
||||||
$(INSTALL) hiredis.h async.h read.h sds.h $(INSTALL_INCLUDE_PATH)
|
|
||||||
$(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters
|
|
||||||
$(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)
|
|
||||||
cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME)
|
|
||||||
$(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)
|
|
||||||
mkdir -p $(INSTALL_PKGCONF_PATH)
|
|
||||||
$(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH)
|
|
||||||
|
|
||||||
32bit:
|
|
||||||
@echo ""
|
|
||||||
@echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386"
|
|
||||||
@echo ""
|
|
||||||
$(MAKE) CFLAGS="-m32" LDFLAGS="-m32"
|
|
||||||
|
|
||||||
32bit-vars:
|
|
||||||
$(eval CFLAGS=-m32)
|
|
||||||
$(eval LDFLAGS=-m32)
|
|
||||||
|
|
||||||
gprof:
|
|
||||||
$(MAKE) CFLAGS="-pg" LDFLAGS="-pg"
|
|
||||||
|
|
||||||
gcov:
|
|
||||||
$(MAKE) CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs"
|
|
||||||
|
|
||||||
coverage: gcov
|
|
||||||
make check
|
|
||||||
mkdir -p tmp/lcov
|
|
||||||
lcov -d . -c -o tmp/lcov/hiredis.info
|
|
||||||
genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info
|
|
||||||
|
|
||||||
noopt:
|
|
||||||
$(MAKE) OPTIMIZATION=""
|
|
||||||
|
|
||||||
.PHONY: all test check clean dep install 32bit 32bit-vars gprof gcov noopt
|
|
412
deps/hiredis/README.md
vendored
412
deps/hiredis/README.md
vendored
@ -1,412 +0,0 @@
|
|||||||
[](https://travis-ci.org/redis/hiredis)
|
|
||||||
|
|
||||||
**This Readme reflects the latest changed in the master branch. See [v0.13.3](https://github.com/redis/hiredis/tree/v0.13.3) for the Readme and documentation for the latest release.**
|
|
||||||
|
|
||||||
# HIREDIS
|
|
||||||
|
|
||||||
Hiredis is a minimalistic C client library for the [Redis](http://redis.io/) 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*.
|
|
||||||
|
|
||||||
## Upgrading to `1.0.0`
|
|
||||||
|
|
||||||
Version 1.0.0 marks a stable release of hiredis.
|
|
||||||
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.
|
|
||||||
For most applications a recompile against the new hiredis should be enough.
|
|
||||||
For code changes see the [Changelog](CHANGELOG.md).
|
|
||||||
|
|
||||||
## Upgrading from `<0.9.0`
|
|
||||||
|
|
||||||
Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing
|
|
||||||
code using hiredis should not be a big pain. The key thing to keep in mind when
|
|
||||||
upgrading is that hiredis >= 0.9.0 uses a `redisContext*` to keep state, in contrast to
|
|
||||||
the stateless 0.0.1 that only has a file descriptor to work with.
|
|
||||||
|
|
||||||
## Synchronous API
|
|
||||||
|
|
||||||
To consume the synchronous API, there are only a few function calls that need to be introduced:
|
|
||||||
|
|
||||||
```c
|
|
||||||
redisContext *redisConnect(const char *ip, int port);
|
|
||||||
void *redisCommand(redisContext *c, const char *format, ...);
|
|
||||||
void freeReplyObject(void *reply);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Connecting
|
|
||||||
|
|
||||||
The function `redisConnect` is used to create a so-called `redisContext`. The
|
|
||||||
context is where Hiredis holds state for a connection. The `redisContext`
|
|
||||||
struct has an integer `err` field that is non-zero when the connection is in
|
|
||||||
an error state. The field `errstr` will contain a string with a description of
|
|
||||||
the error. More information on errors can be found in the **Errors** section.
|
|
||||||
After trying to connect to Redis using `redisConnect` you should
|
|
||||||
check the `err` field to see if establishing the connection was successful:
|
|
||||||
```c
|
|
||||||
redisContext *c = redisConnect("127.0.0.1", 6379);
|
|
||||||
if (c == NULL || c->err) {
|
|
||||||
if (c) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
// handle error
|
|
||||||
} else {
|
|
||||||
printf("Can't allocate redis context\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
*Note: A `redisContext` is not thread-safe.*
|
|
||||||
|
|
||||||
### Sending commands
|
|
||||||
|
|
||||||
There are several ways to issue commands to Redis. The first that will be introduced is
|
|
||||||
`redisCommand`. This function takes a format similar to printf. In the simplest form,
|
|
||||||
it is used like this:
|
|
||||||
```c
|
|
||||||
reply = redisCommand(context, "SET foo bar");
|
|
||||||
```
|
|
||||||
|
|
||||||
The specifier `%s` interpolates a string in the command, and uses `strlen` to
|
|
||||||
determine the length of the string:
|
|
||||||
```c
|
|
||||||
reply = redisCommand(context, "SET foo %s", value);
|
|
||||||
```
|
|
||||||
When you need to pass binary safe strings in a command, the `%b` specifier can be
|
|
||||||
used. Together with a pointer to the string, it requires a `size_t` length argument
|
|
||||||
of the string:
|
|
||||||
```c
|
|
||||||
reply = redisCommand(context, "SET foo %b", value, (size_t) valuelen);
|
|
||||||
```
|
|
||||||
Internally, Hiredis splits the command in different arguments and will
|
|
||||||
convert it to the protocol used to communicate with Redis.
|
|
||||||
One or more spaces separates arguments, so you can use the specifiers
|
|
||||||
anywhere in an argument:
|
|
||||||
```c
|
|
||||||
reply = redisCommand(context, "SET key:%s %s", myid, value);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using replies
|
|
||||||
|
|
||||||
The return value of `redisCommand` holds a reply when the command was
|
|
||||||
successfully executed. When an error occurs, the return value is `NULL` and
|
|
||||||
the `err` field in the context will be set (see section on **Errors**).
|
|
||||||
Once an error is returned the context cannot be reused and you should set up
|
|
||||||
a new connection.
|
|
||||||
|
|
||||||
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
|
|
||||||
was received:
|
|
||||||
|
|
||||||
* **`REDIS_REPLY_STATUS`**:
|
|
||||||
* 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`.
|
|
||||||
|
|
||||||
* **`REDIS_REPLY_ERROR`**:
|
|
||||||
* The command replied with an error. The error string can be accessed identical to `REDIS_REPLY_STATUS`.
|
|
||||||
|
|
||||||
* **`REDIS_REPLY_INTEGER`**:
|
|
||||||
* The command replied with an integer. The integer value can be accessed using the
|
|
||||||
`reply->integer` field of type `long long`.
|
|
||||||
|
|
||||||
* **`REDIS_REPLY_NIL`**:
|
|
||||||
* The command replied with a **nil** object. There is no data to access.
|
|
||||||
|
|
||||||
* **`REDIS_REPLY_STRING`**:
|
|
||||||
* A bulk (string) reply. The value of the reply can be accessed using `reply->str`.
|
|
||||||
The length of this string can be accessed using `reply->len`.
|
|
||||||
|
|
||||||
* **`REDIS_REPLY_ARRAY`**:
|
|
||||||
* A multi bulk reply. The number of elements in the multi bulk reply is stored in
|
|
||||||
`reply->elements`. Every element in the multi bulk reply is a `redisReply` object as well
|
|
||||||
and can be accessed via `reply->element[..index..]`.
|
|
||||||
Redis may reply with nested arrays but this is fully supported.
|
|
||||||
|
|
||||||
Replies should be freed using the `freeReplyObject()` function.
|
|
||||||
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
|
|
||||||
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
|
|
||||||
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
|
|
||||||
returns. This behavior will probably change in future releases, so make sure to
|
|
||||||
keep an eye on the changelog when upgrading (see issue #39).
|
|
||||||
|
|
||||||
### Cleaning up
|
|
||||||
|
|
||||||
To disconnect and free the context the following function can be used:
|
|
||||||
```c
|
|
||||||
void redisFree(redisContext *c);
|
|
||||||
```
|
|
||||||
This function immediately closes the socket and then frees the allocations done in
|
|
||||||
creating the context.
|
|
||||||
|
|
||||||
### Sending commands (cont'd)
|
|
||||||
|
|
||||||
Together with `redisCommand`, the function `redisCommandArgv` can be used to issue commands.
|
|
||||||
It has the following prototype:
|
|
||||||
```c
|
|
||||||
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
|
|
||||||
```
|
|
||||||
It takes the number of arguments `argc`, an array of strings `argv` and the lengths of the
|
|
||||||
arguments `argvlen`. For convenience, `argvlen` may be set to `NULL` and the function will
|
|
||||||
use `strlen(3)` on every argument to determine its length. Obviously, when any of the arguments
|
|
||||||
need to be binary safe, the entire array of lengths `argvlen` should be provided.
|
|
||||||
|
|
||||||
The return value has the same semantic as `redisCommand`.
|
|
||||||
|
|
||||||
### Pipelining
|
|
||||||
|
|
||||||
To explain how Hiredis supports pipelining in a blocking connection, there needs to be
|
|
||||||
understanding of the internal execution flow.
|
|
||||||
|
|
||||||
When any of the functions in the `redisCommand` family is called, Hiredis first formats the
|
|
||||||
command according to the Redis protocol. The formatted command is then put in the output buffer
|
|
||||||
of the context. This output buffer is dynamic, so it can hold any number of commands.
|
|
||||||
After the command is put in the output buffer, `redisGetReply` is called. This function has the
|
|
||||||
following two execution paths:
|
|
||||||
|
|
||||||
1. The input buffer is non-empty:
|
|
||||||
* Try to parse a single reply from the input buffer and return it
|
|
||||||
* If no reply could be parsed, continue at *2*
|
|
||||||
2. The input buffer is empty:
|
|
||||||
* Write the **entire** output buffer to the socket
|
|
||||||
* Read from the socket until a single reply could be parsed
|
|
||||||
|
|
||||||
The function `redisGetReply` is exported as part of the Hiredis API and can be used when a reply
|
|
||||||
is expected on the socket. To pipeline commands, the only things that needs to be done is
|
|
||||||
filling up the output buffer. For this cause, two commands can be used that are identical
|
|
||||||
to the `redisCommand` family, apart from not returning a reply:
|
|
||||||
```c
|
|
||||||
void redisAppendCommand(redisContext *c, const char *format, ...);
|
|
||||||
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
|
|
||||||
```
|
|
||||||
After calling either function one or more times, `redisGetReply` can be used to receive the
|
|
||||||
subsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where
|
|
||||||
the latter means an error occurred while reading a reply. Just as with the other commands,
|
|
||||||
the `err` field in the context can be used to find out what the cause of this error is.
|
|
||||||
|
|
||||||
The following examples shows a simple pipeline (resulting in only a single call to `write(2)` and
|
|
||||||
a single call to `read(2)`):
|
|
||||||
```c
|
|
||||||
redisReply *reply;
|
|
||||||
redisAppendCommand(context,"SET foo bar");
|
|
||||||
redisAppendCommand(context,"GET foo");
|
|
||||||
redisGetReply(context,&reply); // reply for SET
|
|
||||||
freeReplyObject(reply);
|
|
||||||
redisGetReply(context,&reply); // reply for GET
|
|
||||||
freeReplyObject(reply);
|
|
||||||
```
|
|
||||||
This API can also be used to implement a blocking subscriber:
|
|
||||||
```c
|
|
||||||
reply = redisCommand(context,"SUBSCRIBE foo");
|
|
||||||
freeReplyObject(reply);
|
|
||||||
while(redisGetReply(context,&reply) == REDIS_OK) {
|
|
||||||
// consume message
|
|
||||||
freeReplyObject(reply);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
### Errors
|
|
||||||
|
|
||||||
When a function call is not successful, depending on the function either `NULL` or `REDIS_ERR` is
|
|
||||||
returned. The `err` field inside the context will be non-zero and set to one of the
|
|
||||||
following constants:
|
|
||||||
|
|
||||||
* **`REDIS_ERR_IO`**:
|
|
||||||
There was an I/O error while creating the connection, trying to write
|
|
||||||
to the socket or read from the socket. If you included `errno.h` in your
|
|
||||||
application, you can use the global `errno` variable to find out what is
|
|
||||||
wrong.
|
|
||||||
|
|
||||||
* **`REDIS_ERR_EOF`**:
|
|
||||||
The server closed the connection which resulted in an empty read.
|
|
||||||
|
|
||||||
* **`REDIS_ERR_PROTOCOL`**:
|
|
||||||
There was an error while parsing the protocol.
|
|
||||||
|
|
||||||
* **`REDIS_ERR_OTHER`**:
|
|
||||||
Any other error. Currently, it is only used when a specified hostname to connect
|
|
||||||
to cannot be resolved.
|
|
||||||
|
|
||||||
In every case, the `errstr` field in the context will be set to hold a string representation
|
|
||||||
of the error.
|
|
||||||
|
|
||||||
## Asynchronous API
|
|
||||||
|
|
||||||
Hiredis comes with an asynchronous API that works easily with any event library.
|
|
||||||
Examples are bundled that show using Hiredis with [libev](http://software.schmorp.de/pkg/libev.html)
|
|
||||||
and [libevent](http://monkey.org/~provos/libevent/).
|
|
||||||
|
|
||||||
### Connecting
|
|
||||||
|
|
||||||
The function `redisAsyncConnect` can be used to establish a non-blocking connection to
|
|
||||||
Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field
|
|
||||||
should be checked after creation to see if there were errors creating the connection.
|
|
||||||
Because the connection that will be created is non-blocking, the kernel is not able to
|
|
||||||
instantly return if the specified host and port is able to accept a connection.
|
|
||||||
|
|
||||||
*Note: A `redisAsyncContext` is not thread-safe.*
|
|
||||||
|
|
||||||
```c
|
|
||||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
|
||||||
if (c->err) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
// handle error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The asynchronous context can hold a disconnect callback function that is called when the
|
|
||||||
connection is disconnected (either because of an error or per user request). This function should
|
|
||||||
have the following prototype:
|
|
||||||
```c
|
|
||||||
void(const redisAsyncContext *c, int status);
|
|
||||||
```
|
|
||||||
On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the
|
|
||||||
user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err`
|
|
||||||
field in the context can be accessed to find out the cause of the error.
|
|
||||||
|
|
||||||
The context object is always freed after the disconnect callback fired. When a reconnect is needed,
|
|
||||||
the disconnect callback is a good point to do so.
|
|
||||||
|
|
||||||
Setting the disconnect callback can only be done once per context. For subsequent calls it will
|
|
||||||
return `REDIS_ERR`. The function to set the disconnect callback has the following prototype:
|
|
||||||
```c
|
|
||||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
|
|
||||||
```
|
|
||||||
`ac->data` may be used to pass user data to this callback, the same can be done for redisConnectCallback.
|
|
||||||
### Sending commands and their callbacks
|
|
||||||
|
|
||||||
In an asynchronous context, commands are automatically pipelined due to the nature of an event loop.
|
|
||||||
Therefore, unlike the synchronous API, there is only a single way to send commands.
|
|
||||||
Because commands are sent to Redis asynchronously, issuing a command requires a callback function
|
|
||||||
that is called when the reply is received. Reply callbacks should have the following prototype:
|
|
||||||
```c
|
|
||||||
void(redisAsyncContext *c, void *reply, void *privdata);
|
|
||||||
```
|
|
||||||
The `privdata` argument can be used to curry arbitrary data to the callback from the point where
|
|
||||||
the command is initially queued for execution.
|
|
||||||
|
|
||||||
The functions that can be used to issue commands in an asynchronous context are:
|
|
||||||
```c
|
|
||||||
int redisAsyncCommand(
|
|
||||||
redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
|
|
||||||
const char *format, ...);
|
|
||||||
int redisAsyncCommandArgv(
|
|
||||||
redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
|
|
||||||
int argc, const char **argv, const size_t *argvlen);
|
|
||||||
```
|
|
||||||
Both functions work like their blocking counterparts. The return value is `REDIS_OK` when the command
|
|
||||||
was successfully added to the output buffer and `REDIS_ERR` otherwise. Example: when the connection
|
|
||||||
is being disconnected per user-request, no new commands may be added to the output buffer and `REDIS_ERR` is
|
|
||||||
returned on calls to the `redisAsyncCommand` family.
|
|
||||||
|
|
||||||
If the reply for a command with a `NULL` callback is read, it is immediately freed. When the callback
|
|
||||||
for a command is non-`NULL`, the memory is freed immediately following the callback: the reply is only
|
|
||||||
valid for the duration of the callback.
|
|
||||||
|
|
||||||
All pending callbacks are called with a `NULL` reply when the context encountered an error.
|
|
||||||
|
|
||||||
### Disconnecting
|
|
||||||
|
|
||||||
An asynchronous connection can be terminated using:
|
|
||||||
```c
|
|
||||||
void redisAsyncDisconnect(redisAsyncContext *ac);
|
|
||||||
```
|
|
||||||
When this function is called, the connection is **not** immediately terminated. Instead, new
|
|
||||||
commands are no longer accepted and the connection is only terminated when all pending commands
|
|
||||||
have been written to the socket, their respective replies have been read and their respective
|
|
||||||
callbacks have been executed. After this, the disconnection callback is executed with the
|
|
||||||
`REDIS_OK` status and the context object is freed.
|
|
||||||
|
|
||||||
### Hooking it up to event library *X*
|
|
||||||
|
|
||||||
There are a few hooks that need to be set on the context object after it is created.
|
|
||||||
See the `adapters/` directory for bindings to *libev* and *libevent*.
|
|
||||||
|
|
||||||
## Reply parsing API
|
|
||||||
|
|
||||||
Hiredis comes with a reply parsing API that makes it easy for writing higher
|
|
||||||
level language bindings.
|
|
||||||
|
|
||||||
The reply parsing API consists of the following functions:
|
|
||||||
```c
|
|
||||||
redisReader *redisReaderCreate(void);
|
|
||||||
void redisReaderFree(redisReader *reader);
|
|
||||||
int redisReaderFeed(redisReader *reader, const char *buf, size_t len);
|
|
||||||
int redisReaderGetReply(redisReader *reader, void **reply);
|
|
||||||
```
|
|
||||||
The same set of functions are used internally by hiredis when creating a
|
|
||||||
normal Redis context, the above API just exposes it to the user for a direct
|
|
||||||
usage.
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
The function `redisReaderCreate` creates a `redisReader` structure that holds a
|
|
||||||
buffer with unparsed data and state for the protocol parser.
|
|
||||||
|
|
||||||
Incoming data -- most likely from a socket -- can be placed in the internal
|
|
||||||
buffer of the `redisReader` using `redisReaderFeed`. This function will make a
|
|
||||||
copy of the buffer pointed to by `buf` for `len` bytes. This data is parsed
|
|
||||||
when `redisReaderGetReply` is called. This function returns an integer status
|
|
||||||
and a reply object (as described above) via `void **reply`. The returned status
|
|
||||||
can be either `REDIS_OK` or `REDIS_ERR`, where the latter means something went
|
|
||||||
wrong (either a protocol error, or an out of memory error).
|
|
||||||
|
|
||||||
The parser limits the level of nesting for multi bulk payloads to 7. If the
|
|
||||||
multi bulk nesting level is higher than this, the parser returns an error.
|
|
||||||
|
|
||||||
### Customizing replies
|
|
||||||
|
|
||||||
The function `redisReaderGetReply` creates `redisReply` and makes the function
|
|
||||||
argument `reply` point to the created `redisReply` variable. For instance, if
|
|
||||||
the response of type `REDIS_REPLY_STATUS` then the `str` field of `redisReply`
|
|
||||||
will hold the status as a vanilla C string. However, the functions that are
|
|
||||||
responsible for creating instances of the `redisReply` can be customized by
|
|
||||||
setting the `fn` field on the `redisReader` struct. This should be done
|
|
||||||
immediately after creating the `redisReader`.
|
|
||||||
|
|
||||||
For example, [hiredis-rb](https://github.com/pietern/hiredis-rb/blob/master/ext/hiredis_ext/reader.c)
|
|
||||||
uses customized reply object functions to create Ruby objects.
|
|
||||||
|
|
||||||
### Reader max buffer
|
|
||||||
|
|
||||||
Both when using the Reader API directly or when using it indirectly via a
|
|
||||||
normal Redis context, the redisReader structure uses a buffer in order to
|
|
||||||
accumulate data from the server.
|
|
||||||
Usually this buffer is destroyed when it is empty and is larger than 16
|
|
||||||
KiB in order to avoid wasting memory in unused buffers
|
|
||||||
|
|
||||||
However when working with very big payloads destroying the buffer may slow
|
|
||||||
down performances considerably, so it is possible to modify the max size of
|
|
||||||
an idle buffer changing the value of the `maxbuf` field of the reader structure
|
|
||||||
to the desired value. The special value of 0 means that there is no maximum
|
|
||||||
value for an idle buffer, so the buffer will never get freed.
|
|
||||||
|
|
||||||
For instance if you have a normal Redis context you can set the maximum idle
|
|
||||||
buffer to zero (unlimited) just with:
|
|
||||||
```c
|
|
||||||
context->reader->maxbuf = 0;
|
|
||||||
```
|
|
||||||
This should be done only in order to maximize performances when working with
|
|
||||||
large payloads. The context should be set back to `REDIS_READER_MAX_BUF` again
|
|
||||||
as soon as possible in order to prevent allocation of useless memory.
|
|
||||||
|
|
||||||
## AUTHORS
|
|
||||||
|
|
||||||
Hiredis was written by Salvatore Sanfilippo (antirez at gmail) and
|
|
||||||
Pieter Noordhuis (pcnoordhuis at gmail) and is released under the BSD license.
|
|
||||||
Hiredis is currently maintained by Matt Stancliff (matt at genges dot com) and
|
|
||||||
Jan-Erik Rediger (janerik at fnordig dot com)
|
|
127
deps/hiredis/adapters/ae.h
vendored
127
deps/hiredis/adapters/ae.h
vendored
@ -1,127 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __HIREDIS_AE_H__
|
|
||||||
#define __HIREDIS_AE_H__
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <ae.h>
|
|
||||||
#include "../hiredis.h"
|
|
||||||
#include "../async.h"
|
|
||||||
|
|
||||||
typedef struct redisAeEvents {
|
|
||||||
redisAsyncContext *context;
|
|
||||||
aeEventLoop *loop;
|
|
||||||
int fd;
|
|
||||||
int reading, writing;
|
|
||||||
} redisAeEvents;
|
|
||||||
|
|
||||||
static void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
|
|
||||||
((void)el); ((void)fd); ((void)mask);
|
|
||||||
|
|
||||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
|
||||||
redisAsyncHandleRead(e->context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
|
|
||||||
((void)el); ((void)fd); ((void)mask);
|
|
||||||
|
|
||||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
|
||||||
redisAsyncHandleWrite(e->context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisAeAddRead(void *privdata) {
|
|
||||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
|
||||||
aeEventLoop *loop = e->loop;
|
|
||||||
if (!e->reading) {
|
|
||||||
e->reading = 1;
|
|
||||||
aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisAeDelRead(void *privdata) {
|
|
||||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
|
||||||
aeEventLoop *loop = e->loop;
|
|
||||||
if (e->reading) {
|
|
||||||
e->reading = 0;
|
|
||||||
aeDeleteFileEvent(loop,e->fd,AE_READABLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisAeAddWrite(void *privdata) {
|
|
||||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
|
||||||
aeEventLoop *loop = e->loop;
|
|
||||||
if (!e->writing) {
|
|
||||||
e->writing = 1;
|
|
||||||
aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisAeDelWrite(void *privdata) {
|
|
||||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
|
||||||
aeEventLoop *loop = e->loop;
|
|
||||||
if (e->writing) {
|
|
||||||
e->writing = 0;
|
|
||||||
aeDeleteFileEvent(loop,e->fd,AE_WRITABLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisAeCleanup(void *privdata) {
|
|
||||||
redisAeEvents *e = (redisAeEvents*)privdata;
|
|
||||||
redisAeDelRead(privdata);
|
|
||||||
redisAeDelWrite(privdata);
|
|
||||||
free(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
redisAeEvents *e;
|
|
||||||
|
|
||||||
/* Nothing should be attached when something is already attached */
|
|
||||||
if (ac->ev.data != NULL)
|
|
||||||
return REDIS_ERR;
|
|
||||||
|
|
||||||
/* Create container for context and r/w events */
|
|
||||||
e = (redisAeEvents*)malloc(sizeof(*e));
|
|
||||||
e->context = ac;
|
|
||||||
e->loop = loop;
|
|
||||||
e->fd = c->fd;
|
|
||||||
e->reading = e->writing = 0;
|
|
||||||
|
|
||||||
/* Register functions to start/stop listening for events */
|
|
||||||
ac->ev.addRead = redisAeAddRead;
|
|
||||||
ac->ev.delRead = redisAeDelRead;
|
|
||||||
ac->ev.addWrite = redisAeAddWrite;
|
|
||||||
ac->ev.delWrite = redisAeDelWrite;
|
|
||||||
ac->ev.cleanup = redisAeCleanup;
|
|
||||||
ac->ev.data = e;
|
|
||||||
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
#endif
|
|
153
deps/hiredis/adapters/glib.h
vendored
153
deps/hiredis/adapters/glib.h
vendored
@ -1,153 +0,0 @@
|
|||||||
#ifndef __HIREDIS_GLIB_H__
|
|
||||||
#define __HIREDIS_GLIB_H__
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
|
|
||||||
#include "../hiredis.h"
|
|
||||||
#include "../async.h"
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
GSource source;
|
|
||||||
redisAsyncContext *ac;
|
|
||||||
GPollFD poll_fd;
|
|
||||||
} RedisSource;
|
|
||||||
|
|
||||||
static void
|
|
||||||
redis_source_add_read (gpointer data)
|
|
||||||
{
|
|
||||||
RedisSource *source = (RedisSource *)data;
|
|
||||||
g_return_if_fail(source);
|
|
||||||
source->poll_fd.events |= G_IO_IN;
|
|
||||||
g_main_context_wakeup(g_source_get_context((GSource *)data));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
redis_source_del_read (gpointer data)
|
|
||||||
{
|
|
||||||
RedisSource *source = (RedisSource *)data;
|
|
||||||
g_return_if_fail(source);
|
|
||||||
source->poll_fd.events &= ~G_IO_IN;
|
|
||||||
g_main_context_wakeup(g_source_get_context((GSource *)data));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
redis_source_add_write (gpointer data)
|
|
||||||
{
|
|
||||||
RedisSource *source = (RedisSource *)data;
|
|
||||||
g_return_if_fail(source);
|
|
||||||
source->poll_fd.events |= G_IO_OUT;
|
|
||||||
g_main_context_wakeup(g_source_get_context((GSource *)data));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
redis_source_del_write (gpointer data)
|
|
||||||
{
|
|
||||||
RedisSource *source = (RedisSource *)data;
|
|
||||||
g_return_if_fail(source);
|
|
||||||
source->poll_fd.events &= ~G_IO_OUT;
|
|
||||||
g_main_context_wakeup(g_source_get_context((GSource *)data));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
redis_source_cleanup (gpointer data)
|
|
||||||
{
|
|
||||||
RedisSource *source = (RedisSource *)data;
|
|
||||||
|
|
||||||
g_return_if_fail(source);
|
|
||||||
|
|
||||||
redis_source_del_read(source);
|
|
||||||
redis_source_del_write(source);
|
|
||||||
/*
|
|
||||||
* It is not our responsibility to remove ourself from the
|
|
||||||
* current main loop. However, we will remove the GPollFD.
|
|
||||||
*/
|
|
||||||
if (source->poll_fd.fd >= 0) {
|
|
||||||
g_source_remove_poll((GSource *)data, &source->poll_fd);
|
|
||||||
source->poll_fd.fd = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
redis_source_prepare (GSource *source,
|
|
||||||
gint *timeout_)
|
|
||||||
{
|
|
||||||
RedisSource *redis = (RedisSource *)source;
|
|
||||||
*timeout_ = -1;
|
|
||||||
return !!(redis->poll_fd.events & redis->poll_fd.revents);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
redis_source_check (GSource *source)
|
|
||||||
{
|
|
||||||
RedisSource *redis = (RedisSource *)source;
|
|
||||||
return !!(redis->poll_fd.events & redis->poll_fd.revents);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
redis_source_dispatch (GSource *source,
|
|
||||||
GSourceFunc callback,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
RedisSource *redis = (RedisSource *)source;
|
|
||||||
|
|
||||||
if ((redis->poll_fd.revents & G_IO_OUT)) {
|
|
||||||
redisAsyncHandleWrite(redis->ac);
|
|
||||||
redis->poll_fd.revents &= ~G_IO_OUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((redis->poll_fd.revents & G_IO_IN)) {
|
|
||||||
redisAsyncHandleRead(redis->ac);
|
|
||||||
redis->poll_fd.revents &= ~G_IO_IN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
return callback(user_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
redis_source_finalize (GSource *source)
|
|
||||||
{
|
|
||||||
RedisSource *redis = (RedisSource *)source;
|
|
||||||
|
|
||||||
if (redis->poll_fd.fd >= 0) {
|
|
||||||
g_source_remove_poll(source, &redis->poll_fd);
|
|
||||||
redis->poll_fd.fd = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static GSource *
|
|
||||||
redis_source_new (redisAsyncContext *ac)
|
|
||||||
{
|
|
||||||
static GSourceFuncs source_funcs = {
|
|
||||||
.prepare = redis_source_prepare,
|
|
||||||
.check = redis_source_check,
|
|
||||||
.dispatch = redis_source_dispatch,
|
|
||||||
.finalize = redis_source_finalize,
|
|
||||||
};
|
|
||||||
redisContext *c = &ac->c;
|
|
||||||
RedisSource *source;
|
|
||||||
|
|
||||||
g_return_val_if_fail(ac != NULL, NULL);
|
|
||||||
|
|
||||||
source = (RedisSource *)g_source_new(&source_funcs, sizeof *source);
|
|
||||||
source->ac = ac;
|
|
||||||
source->poll_fd.fd = c->fd;
|
|
||||||
source->poll_fd.events = 0;
|
|
||||||
source->poll_fd.revents = 0;
|
|
||||||
g_source_add_poll((GSource *)source, &source->poll_fd);
|
|
||||||
|
|
||||||
ac->ev.addRead = redis_source_add_read;
|
|
||||||
ac->ev.delRead = redis_source_del_read;
|
|
||||||
ac->ev.addWrite = redis_source_add_write;
|
|
||||||
ac->ev.delWrite = redis_source_del_write;
|
|
||||||
ac->ev.cleanup = redis_source_cleanup;
|
|
||||||
ac->ev.data = source;
|
|
||||||
|
|
||||||
return (GSource *)source;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* __HIREDIS_GLIB_H__ */
|
|
81
deps/hiredis/adapters/ivykis.h
vendored
81
deps/hiredis/adapters/ivykis.h
vendored
@ -1,81 +0,0 @@
|
|||||||
#ifndef __HIREDIS_IVYKIS_H__
|
|
||||||
#define __HIREDIS_IVYKIS_H__
|
|
||||||
#include <iv.h>
|
|
||||||
#include "../hiredis.h"
|
|
||||||
#include "../async.h"
|
|
||||||
|
|
||||||
typedef struct redisIvykisEvents {
|
|
||||||
redisAsyncContext *context;
|
|
||||||
struct iv_fd fd;
|
|
||||||
} redisIvykisEvents;
|
|
||||||
|
|
||||||
static void redisIvykisReadEvent(void *arg) {
|
|
||||||
redisAsyncContext *context = (redisAsyncContext *)arg;
|
|
||||||
redisAsyncHandleRead(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisIvykisWriteEvent(void *arg) {
|
|
||||||
redisAsyncContext *context = (redisAsyncContext *)arg;
|
|
||||||
redisAsyncHandleWrite(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisIvykisAddRead(void *privdata) {
|
|
||||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
|
||||||
iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisIvykisDelRead(void *privdata) {
|
|
||||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
|
||||||
iv_fd_set_handler_in(&e->fd, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisIvykisAddWrite(void *privdata) {
|
|
||||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
|
||||||
iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisIvykisDelWrite(void *privdata) {
|
|
||||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
|
||||||
iv_fd_set_handler_out(&e->fd, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisIvykisCleanup(void *privdata) {
|
|
||||||
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
|
||||||
|
|
||||||
iv_fd_unregister(&e->fd);
|
|
||||||
free(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int redisIvykisAttach(redisAsyncContext *ac) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
redisIvykisEvents *e;
|
|
||||||
|
|
||||||
/* Nothing should be attached when something is already attached */
|
|
||||||
if (ac->ev.data != NULL)
|
|
||||||
return REDIS_ERR;
|
|
||||||
|
|
||||||
/* Create container for context and r/w events */
|
|
||||||
e = (redisIvykisEvents*)malloc(sizeof(*e));
|
|
||||||
e->context = ac;
|
|
||||||
|
|
||||||
/* Register functions to start/stop listening for events */
|
|
||||||
ac->ev.addRead = redisIvykisAddRead;
|
|
||||||
ac->ev.delRead = redisIvykisDelRead;
|
|
||||||
ac->ev.addWrite = redisIvykisAddWrite;
|
|
||||||
ac->ev.delWrite = redisIvykisDelWrite;
|
|
||||||
ac->ev.cleanup = redisIvykisCleanup;
|
|
||||||
ac->ev.data = e;
|
|
||||||
|
|
||||||
/* Initialize and install read/write events */
|
|
||||||
IV_FD_INIT(&e->fd);
|
|
||||||
e->fd.fd = c->fd;
|
|
||||||
e->fd.handler_in = redisIvykisReadEvent;
|
|
||||||
e->fd.handler_out = redisIvykisWriteEvent;
|
|
||||||
e->fd.handler_err = NULL;
|
|
||||||
e->fd.cookie = e->context;
|
|
||||||
|
|
||||||
iv_fd_register(&e->fd);
|
|
||||||
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
#endif
|
|
147
deps/hiredis/adapters/libev.h
vendored
147
deps/hiredis/adapters/libev.h
vendored
@ -1,147 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __HIREDIS_LIBEV_H__
|
|
||||||
#define __HIREDIS_LIBEV_H__
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <ev.h>
|
|
||||||
#include "../hiredis.h"
|
|
||||||
#include "../async.h"
|
|
||||||
|
|
||||||
typedef struct redisLibevEvents {
|
|
||||||
redisAsyncContext *context;
|
|
||||||
struct ev_loop *loop;
|
|
||||||
int reading, writing;
|
|
||||||
ev_io rev, wev;
|
|
||||||
} redisLibevEvents;
|
|
||||||
|
|
||||||
static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
|
|
||||||
#if EV_MULTIPLICITY
|
|
||||||
((void)loop);
|
|
||||||
#endif
|
|
||||||
((void)revents);
|
|
||||||
|
|
||||||
redisLibevEvents *e = (redisLibevEvents*)watcher->data;
|
|
||||||
redisAsyncHandleRead(e->context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
|
|
||||||
#if EV_MULTIPLICITY
|
|
||||||
((void)loop);
|
|
||||||
#endif
|
|
||||||
((void)revents);
|
|
||||||
|
|
||||||
redisLibevEvents *e = (redisLibevEvents*)watcher->data;
|
|
||||||
redisAsyncHandleWrite(e->context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisLibevAddRead(void *privdata) {
|
|
||||||
redisLibevEvents *e = (redisLibevEvents*)privdata;
|
|
||||||
struct ev_loop *loop = e->loop;
|
|
||||||
((void)loop);
|
|
||||||
if (!e->reading) {
|
|
||||||
e->reading = 1;
|
|
||||||
ev_io_start(EV_A_ &e->rev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisLibevDelRead(void *privdata) {
|
|
||||||
redisLibevEvents *e = (redisLibevEvents*)privdata;
|
|
||||||
struct ev_loop *loop = e->loop;
|
|
||||||
((void)loop);
|
|
||||||
if (e->reading) {
|
|
||||||
e->reading = 0;
|
|
||||||
ev_io_stop(EV_A_ &e->rev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisLibevAddWrite(void *privdata) {
|
|
||||||
redisLibevEvents *e = (redisLibevEvents*)privdata;
|
|
||||||
struct ev_loop *loop = e->loop;
|
|
||||||
((void)loop);
|
|
||||||
if (!e->writing) {
|
|
||||||
e->writing = 1;
|
|
||||||
ev_io_start(EV_A_ &e->wev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisLibevDelWrite(void *privdata) {
|
|
||||||
redisLibevEvents *e = (redisLibevEvents*)privdata;
|
|
||||||
struct ev_loop *loop = e->loop;
|
|
||||||
((void)loop);
|
|
||||||
if (e->writing) {
|
|
||||||
e->writing = 0;
|
|
||||||
ev_io_stop(EV_A_ &e->wev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisLibevCleanup(void *privdata) {
|
|
||||||
redisLibevEvents *e = (redisLibevEvents*)privdata;
|
|
||||||
redisLibevDelRead(privdata);
|
|
||||||
redisLibevDelWrite(privdata);
|
|
||||||
free(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
redisLibevEvents *e;
|
|
||||||
|
|
||||||
/* Nothing should be attached when something is already attached */
|
|
||||||
if (ac->ev.data != NULL)
|
|
||||||
return REDIS_ERR;
|
|
||||||
|
|
||||||
/* Create container for context and r/w events */
|
|
||||||
e = (redisLibevEvents*)malloc(sizeof(*e));
|
|
||||||
e->context = ac;
|
|
||||||
#if EV_MULTIPLICITY
|
|
||||||
e->loop = loop;
|
|
||||||
#else
|
|
||||||
e->loop = NULL;
|
|
||||||
#endif
|
|
||||||
e->reading = e->writing = 0;
|
|
||||||
e->rev.data = e;
|
|
||||||
e->wev.data = e;
|
|
||||||
|
|
||||||
/* Register functions to start/stop listening for events */
|
|
||||||
ac->ev.addRead = redisLibevAddRead;
|
|
||||||
ac->ev.delRead = redisLibevDelRead;
|
|
||||||
ac->ev.addWrite = redisLibevAddWrite;
|
|
||||||
ac->ev.delWrite = redisLibevDelWrite;
|
|
||||||
ac->ev.cleanup = redisLibevCleanup;
|
|
||||||
ac->ev.data = e;
|
|
||||||
|
|
||||||
/* Initialize read/write events */
|
|
||||||
ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ);
|
|
||||||
ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE);
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
172
deps/hiredis/adapters/libevent.h
vendored
172
deps/hiredis/adapters/libevent.h
vendored
@ -1,172 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __HIREDIS_LIBEVENT_H__
|
|
||||||
#define __HIREDIS_LIBEVENT_H__
|
|
||||||
#include <event2/event.h>
|
|
||||||
#include "../hiredis.h"
|
|
||||||
#include "../async.h"
|
|
||||||
|
|
||||||
#define REDIS_LIBEVENT_DELETED 0x01
|
|
||||||
#define REDIS_LIBEVENT_ENTERED 0x02
|
|
||||||
|
|
||||||
typedef struct redisLibeventEvents {
|
|
||||||
redisAsyncContext *context;
|
|
||||||
struct event *ev;
|
|
||||||
struct event_base *base;
|
|
||||||
struct timeval tv;
|
|
||||||
short flags;
|
|
||||||
short state;
|
|
||||||
} redisLibeventEvents;
|
|
||||||
|
|
||||||
static void redisLibeventDestroy(redisLibeventEvents *e) {
|
|
||||||
free(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisLibeventHandler(int fd, short event, void *arg) {
|
|
||||||
((void)fd);
|
|
||||||
redisLibeventEvents *e = (redisLibeventEvents*)arg;
|
|
||||||
e->state |= REDIS_LIBEVENT_ENTERED;
|
|
||||||
|
|
||||||
#define CHECK_DELETED() if (e->state & REDIS_LIBEVENT_DELETED) {\
|
|
||||||
redisLibeventDestroy(e);\
|
|
||||||
return; \
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((event & EV_TIMEOUT) && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
|
|
||||||
redisAsyncHandleTimeout(e->context);
|
|
||||||
CHECK_DELETED();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((event & EV_READ) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
|
|
||||||
redisAsyncHandleRead(e->context);
|
|
||||||
CHECK_DELETED();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((event & EV_WRITE) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
|
|
||||||
redisAsyncHandleWrite(e->context);
|
|
||||||
CHECK_DELETED();
|
|
||||||
}
|
|
||||||
|
|
||||||
e->state &= ~REDIS_LIBEVENT_ENTERED;
|
|
||||||
#undef CHECK_DELETED
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisLibeventUpdate(void *privdata, short flag, int isRemove) {
|
|
||||||
redisLibeventEvents *e = (redisLibeventEvents *)privdata;
|
|
||||||
const struct timeval *tv = e->tv.tv_sec || e->tv.tv_usec ? &e->tv : NULL;
|
|
||||||
|
|
||||||
if (isRemove) {
|
|
||||||
if ((e->flags & flag) == 0) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
e->flags &= ~flag;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (e->flags & flag) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
e->flags |= flag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
event_del(e->ev);
|
|
||||||
event_assign(e->ev, e->base, e->context->c.fd, e->flags | EV_PERSIST,
|
|
||||||
redisLibeventHandler, privdata);
|
|
||||||
event_add(e->ev, tv);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisLibeventAddRead(void *privdata) {
|
|
||||||
redisLibeventUpdate(privdata, EV_READ, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisLibeventDelRead(void *privdata) {
|
|
||||||
redisLibeventUpdate(privdata, EV_READ, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisLibeventAddWrite(void *privdata) {
|
|
||||||
redisLibeventUpdate(privdata, EV_WRITE, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisLibeventDelWrite(void *privdata) {
|
|
||||||
redisLibeventUpdate(privdata, EV_WRITE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisLibeventCleanup(void *privdata) {
|
|
||||||
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
|
|
||||||
if (!e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event_del(e->ev);
|
|
||||||
event_free(e->ev);
|
|
||||||
e->ev = NULL;
|
|
||||||
|
|
||||||
if (e->state & REDIS_LIBEVENT_ENTERED) {
|
|
||||||
e->state |= REDIS_LIBEVENT_DELETED;
|
|
||||||
} else {
|
|
||||||
redisLibeventDestroy(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisLibeventSetTimeout(void *privdata, struct timeval tv) {
|
|
||||||
redisLibeventEvents *e = (redisLibeventEvents *)privdata;
|
|
||||||
short flags = e->flags;
|
|
||||||
e->flags = 0;
|
|
||||||
e->tv = tv;
|
|
||||||
redisLibeventUpdate(e, flags, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
redisLibeventEvents *e;
|
|
||||||
|
|
||||||
/* Nothing should be attached when something is already attached */
|
|
||||||
if (ac->ev.data != NULL)
|
|
||||||
return REDIS_ERR;
|
|
||||||
|
|
||||||
/* Create container for context and r/w events */
|
|
||||||
e = (redisLibeventEvents*)calloc(1, sizeof(*e));
|
|
||||||
e->context = ac;
|
|
||||||
|
|
||||||
/* Register functions to start/stop listening for events */
|
|
||||||
ac->ev.addRead = redisLibeventAddRead;
|
|
||||||
ac->ev.delRead = redisLibeventDelRead;
|
|
||||||
ac->ev.addWrite = redisLibeventAddWrite;
|
|
||||||
ac->ev.delWrite = redisLibeventDelWrite;
|
|
||||||
ac->ev.cleanup = redisLibeventCleanup;
|
|
||||||
ac->ev.scheduleTimer = redisLibeventSetTimeout;
|
|
||||||
ac->ev.data = e;
|
|
||||||
|
|
||||||
/* Initialize and install read/write events */
|
|
||||||
e->ev = event_new(base, c->fd, EV_READ | EV_WRITE, redisLibeventHandler, e);
|
|
||||||
e->base = base;
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
#endif
|
|
119
deps/hiredis/adapters/libuv.h
vendored
119
deps/hiredis/adapters/libuv.h
vendored
@ -1,119 +0,0 @@
|
|||||||
#ifndef __HIREDIS_LIBUV_H__
|
|
||||||
#define __HIREDIS_LIBUV_H__
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <uv.h>
|
|
||||||
#include "../hiredis.h"
|
|
||||||
#include "../async.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
typedef struct redisLibuvEvents {
|
|
||||||
redisAsyncContext* context;
|
|
||||||
uv_poll_t handle;
|
|
||||||
int events;
|
|
||||||
} redisLibuvEvents;
|
|
||||||
|
|
||||||
|
|
||||||
static void redisLibuvPoll(uv_poll_t* handle, int status, int events) {
|
|
||||||
redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
|
|
||||||
int ev = (status ? p->events : events);
|
|
||||||
|
|
||||||
if (p->context != NULL && (ev & UV_READABLE)) {
|
|
||||||
redisAsyncHandleRead(p->context);
|
|
||||||
}
|
|
||||||
if (p->context != NULL && (ev & UV_WRITABLE)) {
|
|
||||||
redisAsyncHandleWrite(p->context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void redisLibuvAddRead(void *privdata) {
|
|
||||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
|
||||||
|
|
||||||
p->events |= UV_READABLE;
|
|
||||||
|
|
||||||
uv_poll_start(&p->handle, p->events, redisLibuvPoll);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void redisLibuvDelRead(void *privdata) {
|
|
||||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
|
||||||
|
|
||||||
p->events &= ~UV_READABLE;
|
|
||||||
|
|
||||||
if (p->events) {
|
|
||||||
uv_poll_start(&p->handle, p->events, redisLibuvPoll);
|
|
||||||
} else {
|
|
||||||
uv_poll_stop(&p->handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void redisLibuvAddWrite(void *privdata) {
|
|
||||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
|
||||||
|
|
||||||
p->events |= UV_WRITABLE;
|
|
||||||
|
|
||||||
uv_poll_start(&p->handle, p->events, redisLibuvPoll);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void redisLibuvDelWrite(void *privdata) {
|
|
||||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
|
||||||
|
|
||||||
p->events &= ~UV_WRITABLE;
|
|
||||||
|
|
||||||
if (p->events) {
|
|
||||||
uv_poll_start(&p->handle, p->events, redisLibuvPoll);
|
|
||||||
} else {
|
|
||||||
uv_poll_stop(&p->handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void on_close(uv_handle_t* handle) {
|
|
||||||
redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
|
|
||||||
|
|
||||||
free(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void redisLibuvCleanup(void *privdata) {
|
|
||||||
redisLibuvEvents* p = (redisLibuvEvents*)privdata;
|
|
||||||
|
|
||||||
p->context = NULL; // indicate that context might no longer exist
|
|
||||||
uv_close((uv_handle_t*)&p->handle, on_close);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
|
|
||||||
if (ac->ev.data != NULL) {
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
ac->ev.addRead = redisLibuvAddRead;
|
|
||||||
ac->ev.delRead = redisLibuvDelRead;
|
|
||||||
ac->ev.addWrite = redisLibuvAddWrite;
|
|
||||||
ac->ev.delWrite = redisLibuvDelWrite;
|
|
||||||
ac->ev.cleanup = redisLibuvCleanup;
|
|
||||||
|
|
||||||
redisLibuvEvents* p = (redisLibuvEvents*)malloc(sizeof(*p));
|
|
||||||
|
|
||||||
if (!p) {
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(p, 0, sizeof(*p));
|
|
||||||
|
|
||||||
if (uv_poll_init(loop, &p->handle, c->fd) != 0) {
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
ac->ev.data = p;
|
|
||||||
p->handle.data = p;
|
|
||||||
p->context = ac;
|
|
||||||
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
#endif
|
|
114
deps/hiredis/adapters/macosx.h
vendored
114
deps/hiredis/adapters/macosx.h
vendored
@ -1,114 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Дмитрий Бахвалов on 13.07.15.
|
|
||||||
// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef __HIREDIS_MACOSX_H__
|
|
||||||
#define __HIREDIS_MACOSX_H__
|
|
||||||
|
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
|
||||||
|
|
||||||
#include "../hiredis.h"
|
|
||||||
#include "../async.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
redisAsyncContext *context;
|
|
||||||
CFSocketRef socketRef;
|
|
||||||
CFRunLoopSourceRef sourceRef;
|
|
||||||
} RedisRunLoop;
|
|
||||||
|
|
||||||
static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) {
|
|
||||||
if( redisRunLoop != NULL ) {
|
|
||||||
if( redisRunLoop->sourceRef != NULL ) {
|
|
||||||
CFRunLoopSourceInvalidate(redisRunLoop->sourceRef);
|
|
||||||
CFRelease(redisRunLoop->sourceRef);
|
|
||||||
}
|
|
||||||
if( redisRunLoop->socketRef != NULL ) {
|
|
||||||
CFSocketInvalidate(redisRunLoop->socketRef);
|
|
||||||
CFRelease(redisRunLoop->socketRef);
|
|
||||||
}
|
|
||||||
free(redisRunLoop);
|
|
||||||
}
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisMacOSAddRead(void *privdata) {
|
|
||||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
|
||||||
CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisMacOSDelRead(void *privdata) {
|
|
||||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
|
||||||
CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisMacOSAddWrite(void *privdata) {
|
|
||||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
|
||||||
CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisMacOSDelWrite(void *privdata) {
|
|
||||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
|
||||||
CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisMacOSCleanup(void *privdata) {
|
|
||||||
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
|
||||||
freeRedisRunLoop(redisRunLoop);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) {
|
|
||||||
redisAsyncContext* context = (redisAsyncContext*) info;
|
|
||||||
|
|
||||||
switch (callbackType) {
|
|
||||||
case kCFSocketReadCallBack:
|
|
||||||
redisAsyncHandleRead(context);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kCFSocketWriteCallBack:
|
|
||||||
redisAsyncHandleWrite(context);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) {
|
|
||||||
redisContext *redisCtx = &(redisAsyncCtx->c);
|
|
||||||
|
|
||||||
/* Nothing should be attached when something is already attached */
|
|
||||||
if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR;
|
|
||||||
|
|
||||||
RedisRunLoop* redisRunLoop = (RedisRunLoop*) calloc(1, sizeof(RedisRunLoop));
|
|
||||||
if( !redisRunLoop ) return REDIS_ERR;
|
|
||||||
|
|
||||||
/* Setup redis stuff */
|
|
||||||
redisRunLoop->context = redisAsyncCtx;
|
|
||||||
|
|
||||||
redisAsyncCtx->ev.addRead = redisMacOSAddRead;
|
|
||||||
redisAsyncCtx->ev.delRead = redisMacOSDelRead;
|
|
||||||
redisAsyncCtx->ev.addWrite = redisMacOSAddWrite;
|
|
||||||
redisAsyncCtx->ev.delWrite = redisMacOSDelWrite;
|
|
||||||
redisAsyncCtx->ev.cleanup = redisMacOSCleanup;
|
|
||||||
redisAsyncCtx->ev.data = redisRunLoop;
|
|
||||||
|
|
||||||
/* Initialize and install read/write events */
|
|
||||||
CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL };
|
|
||||||
|
|
||||||
redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd,
|
|
||||||
kCFSocketReadCallBack | kCFSocketWriteCallBack,
|
|
||||||
redisMacOSAsyncCallback,
|
|
||||||
&socketCtx);
|
|
||||||
if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop);
|
|
||||||
|
|
||||||
redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0);
|
|
||||||
if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop);
|
|
||||||
|
|
||||||
CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode);
|
|
||||||
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
135
deps/hiredis/adapters/qt.h
vendored
135
deps/hiredis/adapters/qt.h
vendored
@ -1,135 +0,0 @@
|
|||||||
/*-
|
|
||||||
* Copyright (C) 2014 Pietro Cerutti <gahr@gahr.ch>
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __HIREDIS_QT_H__
|
|
||||||
#define __HIREDIS_QT_H__
|
|
||||||
#include <QSocketNotifier>
|
|
||||||
#include "../async.h"
|
|
||||||
|
|
||||||
static void RedisQtAddRead(void *);
|
|
||||||
static void RedisQtDelRead(void *);
|
|
||||||
static void RedisQtAddWrite(void *);
|
|
||||||
static void RedisQtDelWrite(void *);
|
|
||||||
static void RedisQtCleanup(void *);
|
|
||||||
|
|
||||||
class RedisQtAdapter : public QObject {
|
|
||||||
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
friend
|
|
||||||
void RedisQtAddRead(void * adapter) {
|
|
||||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
|
||||||
a->addRead();
|
|
||||||
}
|
|
||||||
|
|
||||||
friend
|
|
||||||
void RedisQtDelRead(void * adapter) {
|
|
||||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
|
||||||
a->delRead();
|
|
||||||
}
|
|
||||||
|
|
||||||
friend
|
|
||||||
void RedisQtAddWrite(void * adapter) {
|
|
||||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
|
||||||
a->addWrite();
|
|
||||||
}
|
|
||||||
|
|
||||||
friend
|
|
||||||
void RedisQtDelWrite(void * adapter) {
|
|
||||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
|
||||||
a->delWrite();
|
|
||||||
}
|
|
||||||
|
|
||||||
friend
|
|
||||||
void RedisQtCleanup(void * adapter) {
|
|
||||||
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
|
||||||
a->cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
RedisQtAdapter(QObject * parent = 0)
|
|
||||||
: QObject(parent), m_ctx(0), m_read(0), m_write(0) { }
|
|
||||||
|
|
||||||
~RedisQtAdapter() {
|
|
||||||
if (m_ctx != 0) {
|
|
||||||
m_ctx->ev.data = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int setContext(redisAsyncContext * ac) {
|
|
||||||
if (ac->ev.data != NULL) {
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
m_ctx = ac;
|
|
||||||
m_ctx->ev.data = this;
|
|
||||||
m_ctx->ev.addRead = RedisQtAddRead;
|
|
||||||
m_ctx->ev.delRead = RedisQtDelRead;
|
|
||||||
m_ctx->ev.addWrite = RedisQtAddWrite;
|
|
||||||
m_ctx->ev.delWrite = RedisQtDelWrite;
|
|
||||||
m_ctx->ev.cleanup = RedisQtCleanup;
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void addRead() {
|
|
||||||
if (m_read) return;
|
|
||||||
m_read = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Read, 0);
|
|
||||||
connect(m_read, SIGNAL(activated(int)), this, SLOT(read()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void delRead() {
|
|
||||||
if (!m_read) return;
|
|
||||||
delete m_read;
|
|
||||||
m_read = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addWrite() {
|
|
||||||
if (m_write) return;
|
|
||||||
m_write = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Write, 0);
|
|
||||||
connect(m_write, SIGNAL(activated(int)), this, SLOT(write()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void delWrite() {
|
|
||||||
if (!m_write) return;
|
|
||||||
delete m_write;
|
|
||||||
m_write = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cleanup() {
|
|
||||||
delRead();
|
|
||||||
delWrite();
|
|
||||||
}
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void read() { redisAsyncHandleRead(m_ctx); }
|
|
||||||
void write() { redisAsyncHandleWrite(m_ctx); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
redisAsyncContext * m_ctx;
|
|
||||||
QSocketNotifier * m_read;
|
|
||||||
QSocketNotifier * m_write;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* !__HIREDIS_QT_H__ */
|
|
24
deps/hiredis/appveyor.yml
vendored
24
deps/hiredis/appveyor.yml
vendored
@ -1,24 +0,0 @@
|
|||||||
# Appveyor configuration file for CI build of hiredis on Windows (under Cygwin)
|
|
||||||
environment:
|
|
||||||
matrix:
|
|
||||||
- CYG_BASH: C:\cygwin64\bin\bash
|
|
||||||
CC: gcc
|
|
||||||
- CYG_BASH: C:\cygwin\bin\bash
|
|
||||||
CC: gcc
|
|
||||||
CFLAGS: -m32
|
|
||||||
CXXFLAGS: -m32
|
|
||||||
LDFLAGS: -m32
|
|
||||||
|
|
||||||
clone_depth: 1
|
|
||||||
|
|
||||||
# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail
|
|
||||||
init:
|
|
||||||
- git config --global core.autocrlf input
|
|
||||||
|
|
||||||
# Install needed build dependencies
|
|
||||||
install:
|
|
||||||
- '%CYG_BASH% -lc "cygcheck -dc cygwin"'
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- 'echo building...'
|
|
||||||
- '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null; mkdir build && cd build && cmake .. -G \"Unix Makefiles\" && make VERBOSE=1"'
|
|
766
deps/hiredis/async.c
vendored
766
deps/hiredis/async.c
vendored
@ -1,766 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fmacros.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
#include <strings.h>
|
|
||||||
#endif
|
|
||||||
#include <assert.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include "async.h"
|
|
||||||
#include "net.h"
|
|
||||||
#include "dict.c"
|
|
||||||
#include "sds.h"
|
|
||||||
#include "win32.h"
|
|
||||||
|
|
||||||
#include "async_private.h"
|
|
||||||
|
|
||||||
/* Forward declaration of function in hiredis.c */
|
|
||||||
int __redisAppendCommand(redisContext *c, const char *cmd, size_t len);
|
|
||||||
|
|
||||||
/* Functions managing dictionary of callbacks for pub/sub. */
|
|
||||||
static unsigned int callbackHash(const void *key) {
|
|
||||||
return dictGenHashFunction((const unsigned char *)key,
|
|
||||||
sdslen((const sds)key));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *callbackValDup(void *privdata, const void *src) {
|
|
||||||
((void) privdata);
|
|
||||||
redisCallback *dup = malloc(sizeof(*dup));
|
|
||||||
memcpy(dup,src,sizeof(*dup));
|
|
||||||
return dup;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int callbackKeyCompare(void *privdata, const void *key1, const void *key2) {
|
|
||||||
int l1, l2;
|
|
||||||
((void) privdata);
|
|
||||||
|
|
||||||
l1 = sdslen((const sds)key1);
|
|
||||||
l2 = sdslen((const sds)key2);
|
|
||||||
if (l1 != l2) return 0;
|
|
||||||
return memcmp(key1,key2,l1) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void callbackKeyDestructor(void *privdata, void *key) {
|
|
||||||
((void) privdata);
|
|
||||||
sdsfree((sds)key);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void callbackValDestructor(void *privdata, void *val) {
|
|
||||||
((void) privdata);
|
|
||||||
free(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static dictType callbackDict = {
|
|
||||||
callbackHash,
|
|
||||||
NULL,
|
|
||||||
callbackValDup,
|
|
||||||
callbackKeyCompare,
|
|
||||||
callbackKeyDestructor,
|
|
||||||
callbackValDestructor
|
|
||||||
};
|
|
||||||
|
|
||||||
static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
|
||||||
redisAsyncContext *ac;
|
|
||||||
|
|
||||||
ac = realloc(c,sizeof(redisAsyncContext));
|
|
||||||
if (ac == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
c = &(ac->c);
|
|
||||||
|
|
||||||
/* The regular connect functions will always set the flag REDIS_CONNECTED.
|
|
||||||
* For the async API, we want to wait until the first write event is
|
|
||||||
* received up before setting this flag, so reset it here. */
|
|
||||||
c->flags &= ~REDIS_CONNECTED;
|
|
||||||
|
|
||||||
ac->err = 0;
|
|
||||||
ac->errstr = NULL;
|
|
||||||
ac->data = NULL;
|
|
||||||
|
|
||||||
ac->ev.data = NULL;
|
|
||||||
ac->ev.addRead = NULL;
|
|
||||||
ac->ev.delRead = NULL;
|
|
||||||
ac->ev.addWrite = NULL;
|
|
||||||
ac->ev.delWrite = NULL;
|
|
||||||
ac->ev.cleanup = NULL;
|
|
||||||
ac->ev.scheduleTimer = NULL;
|
|
||||||
|
|
||||||
ac->onConnect = NULL;
|
|
||||||
ac->onDisconnect = NULL;
|
|
||||||
|
|
||||||
ac->replies.head = NULL;
|
|
||||||
ac->replies.tail = NULL;
|
|
||||||
ac->sub.invalid.head = NULL;
|
|
||||||
ac->sub.invalid.tail = NULL;
|
|
||||||
ac->sub.channels = dictCreate(&callbackDict,NULL);
|
|
||||||
ac->sub.patterns = dictCreate(&callbackDict,NULL);
|
|
||||||
return ac;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We want the error field to be accessible directly instead of requiring
|
|
||||||
* an indirection to the redisContext struct. */
|
|
||||||
static void __redisAsyncCopyError(redisAsyncContext *ac) {
|
|
||||||
if (!ac)
|
|
||||||
return;
|
|
||||||
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
ac->err = c->err;
|
|
||||||
ac->errstr = c->errstr;
|
|
||||||
}
|
|
||||||
|
|
||||||
redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options) {
|
|
||||||
redisOptions myOptions = *options;
|
|
||||||
redisContext *c;
|
|
||||||
redisAsyncContext *ac;
|
|
||||||
|
|
||||||
myOptions.options |= REDIS_OPT_NONBLOCK;
|
|
||||||
c = redisConnectWithOptions(&myOptions);
|
|
||||||
if (c == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
ac = redisAsyncInitialize(c);
|
|
||||||
if (ac == NULL) {
|
|
||||||
redisFree(c);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
__redisAsyncCopyError(ac);
|
|
||||||
return ac;
|
|
||||||
}
|
|
||||||
|
|
||||||
redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
|
|
||||||
redisOptions options = {0};
|
|
||||||
REDIS_OPTIONS_SET_TCP(&options, ip, port);
|
|
||||||
return redisAsyncConnectWithOptions(&options);
|
|
||||||
}
|
|
||||||
|
|
||||||
redisAsyncContext *redisAsyncConnectBind(const char *ip, int port,
|
|
||||||
const char *source_addr) {
|
|
||||||
redisOptions options = {0};
|
|
||||||
REDIS_OPTIONS_SET_TCP(&options, ip, port);
|
|
||||||
options.endpoint.tcp.source_addr = source_addr;
|
|
||||||
return redisAsyncConnectWithOptions(&options);
|
|
||||||
}
|
|
||||||
|
|
||||||
redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
|
|
||||||
const char *source_addr) {
|
|
||||||
redisOptions options = {0};
|
|
||||||
REDIS_OPTIONS_SET_TCP(&options, ip, port);
|
|
||||||
options.options |= REDIS_OPT_REUSEADDR;
|
|
||||||
options.endpoint.tcp.source_addr = source_addr;
|
|
||||||
return redisAsyncConnectWithOptions(&options);
|
|
||||||
}
|
|
||||||
|
|
||||||
redisAsyncContext *redisAsyncConnectUnix(const char *path) {
|
|
||||||
redisOptions options = {0};
|
|
||||||
REDIS_OPTIONS_SET_UNIX(&options, path);
|
|
||||||
return redisAsyncConnectWithOptions(&options);
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
|
|
||||||
if (ac->onConnect == NULL) {
|
|
||||||
ac->onConnect = fn;
|
|
||||||
|
|
||||||
/* The common way to detect an established connection is to wait for
|
|
||||||
* the first write event to be fired. This assumes the related event
|
|
||||||
* library functions are already set. */
|
|
||||||
_EL_ADD_WRITE(ac);
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) {
|
|
||||||
if (ac->onDisconnect == NULL) {
|
|
||||||
ac->onDisconnect = fn;
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper functions to push/shift callbacks */
|
|
||||||
static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
|
|
||||||
redisCallback *cb;
|
|
||||||
|
|
||||||
/* Copy callback from stack to heap */
|
|
||||||
cb = malloc(sizeof(*cb));
|
|
||||||
if (cb == NULL)
|
|
||||||
return REDIS_ERR_OOM;
|
|
||||||
|
|
||||||
if (source != NULL) {
|
|
||||||
memcpy(cb,source,sizeof(*cb));
|
|
||||||
cb->next = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Store callback in list */
|
|
||||||
if (list->head == NULL)
|
|
||||||
list->head = cb;
|
|
||||||
if (list->tail != NULL)
|
|
||||||
list->tail->next = cb;
|
|
||||||
list->tail = cb;
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __redisShiftCallback(redisCallbackList *list, redisCallback *target) {
|
|
||||||
redisCallback *cb = list->head;
|
|
||||||
if (cb != NULL) {
|
|
||||||
list->head = cb->next;
|
|
||||||
if (cb == list->tail)
|
|
||||||
list->tail = NULL;
|
|
||||||
|
|
||||||
/* Copy callback from heap to stack */
|
|
||||||
if (target != NULL)
|
|
||||||
memcpy(target,cb,sizeof(*cb));
|
|
||||||
free(cb);
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisReply *reply) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
if (cb->fn != NULL) {
|
|
||||||
c->flags |= REDIS_IN_CALLBACK;
|
|
||||||
cb->fn(ac,reply,cb->privdata);
|
|
||||||
c->flags &= ~REDIS_IN_CALLBACK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper function to free the context. */
|
|
||||||
static void __redisAsyncFree(redisAsyncContext *ac) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
redisCallback cb;
|
|
||||||
dictIterator *it;
|
|
||||||
dictEntry *de;
|
|
||||||
|
|
||||||
/* Execute pending callbacks with NULL reply. */
|
|
||||||
while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK)
|
|
||||||
__redisRunCallback(ac,&cb,NULL);
|
|
||||||
|
|
||||||
/* Execute callbacks for invalid commands */
|
|
||||||
while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK)
|
|
||||||
__redisRunCallback(ac,&cb,NULL);
|
|
||||||
|
|
||||||
/* Run subscription callbacks callbacks with NULL reply */
|
|
||||||
it = dictGetIterator(ac->sub.channels);
|
|
||||||
while ((de = dictNext(it)) != NULL)
|
|
||||||
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
|
|
||||||
dictReleaseIterator(it);
|
|
||||||
dictRelease(ac->sub.channels);
|
|
||||||
|
|
||||||
it = dictGetIterator(ac->sub.patterns);
|
|
||||||
while ((de = dictNext(it)) != NULL)
|
|
||||||
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
|
|
||||||
dictReleaseIterator(it);
|
|
||||||
dictRelease(ac->sub.patterns);
|
|
||||||
|
|
||||||
/* Signal event lib to clean up */
|
|
||||||
_EL_CLEANUP(ac);
|
|
||||||
|
|
||||||
/* Execute disconnect callback. When redisAsyncFree() initiated destroying
|
|
||||||
* this context, the status will always be REDIS_OK. */
|
|
||||||
if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) {
|
|
||||||
if (c->flags & REDIS_FREEING) {
|
|
||||||
ac->onDisconnect(ac,REDIS_OK);
|
|
||||||
} else {
|
|
||||||
ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cleanup self */
|
|
||||||
redisFree(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free the async context. When this function is called from a callback,
|
|
||||||
* control needs to be returned to redisProcessCallbacks() before actual
|
|
||||||
* free'ing. To do so, a flag is set on the context which is picked up by
|
|
||||||
* redisProcessCallbacks(). Otherwise, the context is immediately free'd. */
|
|
||||||
void redisAsyncFree(redisAsyncContext *ac) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
c->flags |= REDIS_FREEING;
|
|
||||||
if (!(c->flags & REDIS_IN_CALLBACK))
|
|
||||||
__redisAsyncFree(ac);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper function to make the disconnect happen and clean up. */
|
|
||||||
void __redisAsyncDisconnect(redisAsyncContext *ac) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
|
|
||||||
/* Make sure error is accessible if there is any */
|
|
||||||
__redisAsyncCopyError(ac);
|
|
||||||
|
|
||||||
if (ac->err == 0) {
|
|
||||||
/* For clean disconnects, there should be no pending callbacks. */
|
|
||||||
int ret = __redisShiftCallback(&ac->replies,NULL);
|
|
||||||
assert(ret == REDIS_ERR);
|
|
||||||
} else {
|
|
||||||
/* Disconnection is caused by an error, make sure that pending
|
|
||||||
* callbacks cannot call new commands. */
|
|
||||||
c->flags |= REDIS_DISCONNECTING;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* cleanup event library on disconnect.
|
|
||||||
* this is safe to call multiple times */
|
|
||||||
_EL_CLEANUP(ac);
|
|
||||||
|
|
||||||
/* For non-clean disconnects, __redisAsyncFree() will execute pending
|
|
||||||
* callbacks with a NULL-reply. */
|
|
||||||
if (!(c->flags & REDIS_NO_AUTO_FREE)) {
|
|
||||||
__redisAsyncFree(ac);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tries to do a clean disconnect from Redis, meaning it stops new commands
|
|
||||||
* from being issued, but tries to flush the output buffer and execute
|
|
||||||
* callbacks for all remaining replies. When this function is called from a
|
|
||||||
* callback, there might be more replies and we can safely defer disconnecting
|
|
||||||
* to redisProcessCallbacks(). Otherwise, we can only disconnect immediately
|
|
||||||
* when there are no pending callbacks. */
|
|
||||||
void redisAsyncDisconnect(redisAsyncContext *ac) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
c->flags |= REDIS_DISCONNECTING;
|
|
||||||
|
|
||||||
/** unset the auto-free flag here, because disconnect undoes this */
|
|
||||||
c->flags &= ~REDIS_NO_AUTO_FREE;
|
|
||||||
if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL)
|
|
||||||
__redisAsyncDisconnect(ac);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
dict *callbacks;
|
|
||||||
redisCallback *cb;
|
|
||||||
dictEntry *de;
|
|
||||||
int pvariant;
|
|
||||||
char *stype;
|
|
||||||
sds sname;
|
|
||||||
|
|
||||||
/* Custom reply functions are not supported for pub/sub. This will fail
|
|
||||||
* very hard when they are used... */
|
|
||||||
if (reply->type == REDIS_REPLY_ARRAY) {
|
|
||||||
assert(reply->elements >= 2);
|
|
||||||
assert(reply->element[0]->type == REDIS_REPLY_STRING);
|
|
||||||
stype = reply->element[0]->str;
|
|
||||||
pvariant = (tolower(stype[0]) == 'p') ? 1 : 0;
|
|
||||||
|
|
||||||
if (pvariant)
|
|
||||||
callbacks = ac->sub.patterns;
|
|
||||||
else
|
|
||||||
callbacks = ac->sub.channels;
|
|
||||||
|
|
||||||
/* Locate the right callback */
|
|
||||||
assert(reply->element[1]->type == REDIS_REPLY_STRING);
|
|
||||||
sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);
|
|
||||||
de = dictFind(callbacks,sname);
|
|
||||||
if (de != NULL) {
|
|
||||||
cb = dictGetEntryVal(de);
|
|
||||||
|
|
||||||
/* If this is an subscribe reply decrease pending counter. */
|
|
||||||
if (strcasecmp(stype+pvariant,"subscribe") == 0) {
|
|
||||||
cb->pending_subs -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(dstcb,cb,sizeof(*dstcb));
|
|
||||||
|
|
||||||
/* If this is an unsubscribe message, remove it. */
|
|
||||||
if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
|
|
||||||
if (cb->pending_subs == 0)
|
|
||||||
dictDelete(callbacks,sname);
|
|
||||||
|
|
||||||
/* If this was the last unsubscribe message, revert to
|
|
||||||
* non-subscribe mode. */
|
|
||||||
assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
|
|
||||||
|
|
||||||
/* Unset subscribed flag only when no pipelined pending subscribe. */
|
|
||||||
if (reply->element[2]->integer == 0
|
|
||||||
&& dictSize(ac->sub.channels) == 0
|
|
||||||
&& dictSize(ac->sub.patterns) == 0)
|
|
||||||
c->flags &= ~REDIS_SUBSCRIBED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sdsfree(sname);
|
|
||||||
} else {
|
|
||||||
/* Shift callback for invalid commands. */
|
|
||||||
__redisShiftCallback(&ac->sub.invalid,dstcb);
|
|
||||||
}
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void redisProcessCallbacks(redisAsyncContext *ac) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
redisCallback cb = {NULL, NULL, 0, NULL};
|
|
||||||
void *reply = NULL;
|
|
||||||
int status;
|
|
||||||
|
|
||||||
while((status = redisGetReply(c,&reply)) == REDIS_OK) {
|
|
||||||
if (reply == NULL) {
|
|
||||||
/* When the connection is being disconnected and there are
|
|
||||||
* no more replies, this is the cue to really disconnect. */
|
|
||||||
if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0
|
|
||||||
&& ac->replies.head == NULL) {
|
|
||||||
__redisAsyncDisconnect(ac);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If monitor mode, repush callback */
|
|
||||||
if(c->flags & REDIS_MONITORING) {
|
|
||||||
__redisPushCallback(&ac->replies,&cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* When the connection is not being disconnected, simply stop
|
|
||||||
* trying to get replies and wait for the next loop tick. */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Even if the context is subscribed, pending regular callbacks will
|
|
||||||
* get a reply before pub/sub messages arrive. */
|
|
||||||
if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {
|
|
||||||
/*
|
|
||||||
* A spontaneous reply in a not-subscribed context can be the error
|
|
||||||
* reply that is sent when a new connection exceeds the maximum
|
|
||||||
* number of allowed connections on the server side.
|
|
||||||
*
|
|
||||||
* This is seen as an error instead of a regular reply because the
|
|
||||||
* server closes the connection after sending it.
|
|
||||||
*
|
|
||||||
* To prevent the error from being overwritten by an EOF error the
|
|
||||||
* connection is closed here. See issue #43.
|
|
||||||
*
|
|
||||||
* Another possibility is that the server is loading its dataset.
|
|
||||||
* In this case we also want to close the connection, and have the
|
|
||||||
* user wait until the server is ready to take our request.
|
|
||||||
*/
|
|
||||||
if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) {
|
|
||||||
c->err = REDIS_ERR_OTHER;
|
|
||||||
snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str);
|
|
||||||
c->reader->fn->freeObject(reply);
|
|
||||||
__redisAsyncDisconnect(ac);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* No more regular callbacks and no errors, the context *must* be subscribed or monitoring. */
|
|
||||||
assert((c->flags & REDIS_SUBSCRIBED || c->flags & REDIS_MONITORING));
|
|
||||||
if(c->flags & REDIS_SUBSCRIBED)
|
|
||||||
__redisGetSubscribeCallback(ac,reply,&cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cb.fn != NULL) {
|
|
||||||
__redisRunCallback(ac,&cb,reply);
|
|
||||||
c->reader->fn->freeObject(reply);
|
|
||||||
|
|
||||||
/* Proceed with free'ing when redisAsyncFree() was called. */
|
|
||||||
if (c->flags & REDIS_FREEING) {
|
|
||||||
__redisAsyncFree(ac);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* No callback for this reply. This can either be a NULL callback,
|
|
||||||
* or there were no callbacks to begin with. Either way, don't
|
|
||||||
* abort with an error, but simply ignore it because the client
|
|
||||||
* doesn't know what the server will spit out over the wire. */
|
|
||||||
c->reader->fn->freeObject(reply);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disconnect when there was an error reading the reply */
|
|
||||||
if (status != REDIS_OK)
|
|
||||||
__redisAsyncDisconnect(ac);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Internal helper function to detect socket status the first time a read or
|
|
||||||
* write event fires. When connecting was not successful, the connect callback
|
|
||||||
* is called with a REDIS_ERR status and the context is free'd. */
|
|
||||||
static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
|
|
||||||
int completed = 0;
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
if (redisCheckConnectDone(c, &completed) == REDIS_ERR) {
|
|
||||||
/* Error! */
|
|
||||||
redisCheckSocketError(c);
|
|
||||||
if (ac->onConnect) ac->onConnect(ac, REDIS_ERR);
|
|
||||||
__redisAsyncDisconnect(ac);
|
|
||||||
return REDIS_ERR;
|
|
||||||
} else if (completed == 1) {
|
|
||||||
/* connected! */
|
|
||||||
if (ac->onConnect) ac->onConnect(ac, REDIS_OK);
|
|
||||||
c->flags |= REDIS_CONNECTED;
|
|
||||||
return REDIS_OK;
|
|
||||||
} else {
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void redisAsyncRead(redisAsyncContext *ac) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
|
|
||||||
if (redisBufferRead(c) == REDIS_ERR) {
|
|
||||||
__redisAsyncDisconnect(ac);
|
|
||||||
} else {
|
|
||||||
/* Always re-schedule reads */
|
|
||||||
_EL_ADD_READ(ac);
|
|
||||||
redisProcessCallbacks(ac);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This function should be called when the socket is readable.
|
|
||||||
* It processes all replies that can be read and executes their callbacks.
|
|
||||||
*/
|
|
||||||
void redisAsyncHandleRead(redisAsyncContext *ac) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
|
|
||||||
if (!(c->flags & REDIS_CONNECTED)) {
|
|
||||||
/* Abort connect was not successful. */
|
|
||||||
if (__redisAsyncHandleConnect(ac) != REDIS_OK)
|
|
||||||
return;
|
|
||||||
/* Try again later when the context is still not connected. */
|
|
||||||
if (!(c->flags & REDIS_CONNECTED))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
c->funcs->async_read(ac);
|
|
||||||
}
|
|
||||||
|
|
||||||
void redisAsyncWrite(redisAsyncContext *ac) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
int done = 0;
|
|
||||||
|
|
||||||
if (redisBufferWrite(c,&done) == REDIS_ERR) {
|
|
||||||
__redisAsyncDisconnect(ac);
|
|
||||||
} else {
|
|
||||||
/* Continue writing when not done, stop writing otherwise */
|
|
||||||
if (!done)
|
|
||||||
_EL_ADD_WRITE(ac);
|
|
||||||
else
|
|
||||||
_EL_DEL_WRITE(ac);
|
|
||||||
|
|
||||||
/* Always schedule reads after writes */
|
|
||||||
_EL_ADD_READ(ac);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void redisAsyncHandleWrite(redisAsyncContext *ac) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
|
|
||||||
if (!(c->flags & REDIS_CONNECTED)) {
|
|
||||||
/* Abort connect was not successful. */
|
|
||||||
if (__redisAsyncHandleConnect(ac) != REDIS_OK)
|
|
||||||
return;
|
|
||||||
/* Try again later when the context is still not connected. */
|
|
||||||
if (!(c->flags & REDIS_CONNECTED))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
c->funcs->async_write(ac);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __redisSetError(redisContext *c, int type, const char *str);
|
|
||||||
|
|
||||||
void redisAsyncHandleTimeout(redisAsyncContext *ac) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
redisCallback cb;
|
|
||||||
|
|
||||||
if ((c->flags & REDIS_CONNECTED) && ac->replies.head == NULL) {
|
|
||||||
/* Nothing to do - just an idle timeout */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!c->err) {
|
|
||||||
__redisSetError(c, REDIS_ERR_TIMEOUT, "Timeout");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(c->flags & REDIS_CONNECTED) && ac->onConnect) {
|
|
||||||
ac->onConnect(ac, REDIS_ERR);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (__redisShiftCallback(&ac->replies, &cb) == REDIS_OK) {
|
|
||||||
__redisRunCallback(ac, &cb, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: Don't automatically sever the connection,
|
|
||||||
* rather, allow to ignore <x> responses before the queue is clear
|
|
||||||
*/
|
|
||||||
__redisAsyncDisconnect(ac);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sets a pointer to the first argument and its length starting at p. Returns
|
|
||||||
* the number of bytes to skip to get to the following argument. */
|
|
||||||
static const char *nextArgument(const char *start, const char **str, size_t *len) {
|
|
||||||
const char *p = start;
|
|
||||||
if (p[0] != '$') {
|
|
||||||
p = strchr(p,'$');
|
|
||||||
if (p == NULL) return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
*len = (int)strtol(p+1,NULL,10);
|
|
||||||
p = strchr(p,'\r');
|
|
||||||
assert(p);
|
|
||||||
*str = p+2;
|
|
||||||
return p+2+(*len)+2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper function for the redisAsyncCommand* family of functions. Writes a
|
|
||||||
* formatted command to the output buffer and registers the provided callback
|
|
||||||
* function with the context. */
|
|
||||||
static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
|
|
||||||
redisContext *c = &(ac->c);
|
|
||||||
redisCallback cb;
|
|
||||||
struct dict *cbdict;
|
|
||||||
dictEntry *de;
|
|
||||||
redisCallback *existcb;
|
|
||||||
int pvariant, hasnext;
|
|
||||||
const char *cstr, *astr;
|
|
||||||
size_t clen, alen;
|
|
||||||
const char *p;
|
|
||||||
sds sname;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* Don't accept new commands when the connection is about to be closed. */
|
|
||||||
if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR;
|
|
||||||
|
|
||||||
/* Setup callback */
|
|
||||||
cb.fn = fn;
|
|
||||||
cb.privdata = privdata;
|
|
||||||
cb.pending_subs = 1;
|
|
||||||
|
|
||||||
/* Find out which command will be appended. */
|
|
||||||
p = nextArgument(cmd,&cstr,&clen);
|
|
||||||
assert(p != NULL);
|
|
||||||
hasnext = (p[0] == '$');
|
|
||||||
pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0;
|
|
||||||
cstr += pvariant;
|
|
||||||
clen -= pvariant;
|
|
||||||
|
|
||||||
if (hasnext && strncasecmp(cstr,"subscribe\r\n",11) == 0) {
|
|
||||||
c->flags |= REDIS_SUBSCRIBED;
|
|
||||||
|
|
||||||
/* Add every channel/pattern to the list of subscription callbacks. */
|
|
||||||
while ((p = nextArgument(p,&astr,&alen)) != NULL) {
|
|
||||||
sname = sdsnewlen(astr,alen);
|
|
||||||
if (pvariant)
|
|
||||||
cbdict = ac->sub.patterns;
|
|
||||||
else
|
|
||||||
cbdict = ac->sub.channels;
|
|
||||||
|
|
||||||
de = dictFind(cbdict,sname);
|
|
||||||
|
|
||||||
if (de != NULL) {
|
|
||||||
existcb = dictGetEntryVal(de);
|
|
||||||
cb.pending_subs = existcb->pending_subs + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = dictReplace(cbdict,sname,&cb);
|
|
||||||
|
|
||||||
if (ret == 0) sdsfree(sname);
|
|
||||||
}
|
|
||||||
} else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) {
|
|
||||||
/* It is only useful to call (P)UNSUBSCRIBE when the context is
|
|
||||||
* subscribed to one or more channels or patterns. */
|
|
||||||
if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR;
|
|
||||||
|
|
||||||
/* (P)UNSUBSCRIBE does not have its own response: every channel or
|
|
||||||
* pattern that is unsubscribed will receive a message. This means we
|
|
||||||
* should not append a callback function for this command. */
|
|
||||||
} else if(strncasecmp(cstr,"monitor\r\n",9) == 0) {
|
|
||||||
/* Set monitor flag and push callback */
|
|
||||||
c->flags |= REDIS_MONITORING;
|
|
||||||
__redisPushCallback(&ac->replies,&cb);
|
|
||||||
} else {
|
|
||||||
if (c->flags & REDIS_SUBSCRIBED)
|
|
||||||
/* This will likely result in an error reply, but it needs to be
|
|
||||||
* received and passed to the callback. */
|
|
||||||
__redisPushCallback(&ac->sub.invalid,&cb);
|
|
||||||
else
|
|
||||||
__redisPushCallback(&ac->replies,&cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
__redisAppendCommand(c,cmd,len);
|
|
||||||
|
|
||||||
/* Always schedule a write when the write buffer is non-empty */
|
|
||||||
_EL_ADD_WRITE(ac);
|
|
||||||
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) {
|
|
||||||
char *cmd;
|
|
||||||
int len;
|
|
||||||
int status;
|
|
||||||
len = redisvFormatCommand(&cmd,format,ap);
|
|
||||||
|
|
||||||
/* We don't want to pass -1 or -2 to future functions as a length. */
|
|
||||||
if (len < 0)
|
|
||||||
return REDIS_ERR;
|
|
||||||
|
|
||||||
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
|
||||||
free(cmd);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...) {
|
|
||||||
va_list ap;
|
|
||||||
int status;
|
|
||||||
va_start(ap,format);
|
|
||||||
status = redisvAsyncCommand(ac,fn,privdata,format,ap);
|
|
||||||
va_end(ap);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) {
|
|
||||||
sds cmd;
|
|
||||||
int len;
|
|
||||||
int status;
|
|
||||||
len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
|
|
||||||
if (len < 0)
|
|
||||||
return REDIS_ERR;
|
|
||||||
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
|
||||||
sdsfree(cmd);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
|
|
||||||
int status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
void redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) {
|
|
||||||
if (!ac->c.timeout) {
|
|
||||||
ac->c.timeout = calloc(1, sizeof(tv));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tv.tv_sec == ac->c.timeout->tv_sec &&
|
|
||||||
tv.tv_usec == ac->c.timeout->tv_usec) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
*ac->c.timeout = tv;
|
|
||||||
}
|
|
142
deps/hiredis/async.h
vendored
142
deps/hiredis/async.h
vendored
@ -1,142 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __HIREDIS_ASYNC_H
|
|
||||||
#define __HIREDIS_ASYNC_H
|
|
||||||
#include "hiredis.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct redisAsyncContext; /* need forward declaration of redisAsyncContext */
|
|
||||||
struct dict; /* dictionary header is included in async.c */
|
|
||||||
|
|
||||||
/* Reply callback prototype and container */
|
|
||||||
typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*);
|
|
||||||
typedef struct redisCallback {
|
|
||||||
struct redisCallback *next; /* simple singly linked list */
|
|
||||||
redisCallbackFn *fn;
|
|
||||||
int pending_subs;
|
|
||||||
void *privdata;
|
|
||||||
} redisCallback;
|
|
||||||
|
|
||||||
/* List of callbacks for either regular replies or pub/sub */
|
|
||||||
typedef struct redisCallbackList {
|
|
||||||
redisCallback *head, *tail;
|
|
||||||
} redisCallbackList;
|
|
||||||
|
|
||||||
/* Connection callback prototypes */
|
|
||||||
typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
|
|
||||||
typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status);
|
|
||||||
typedef void(redisTimerCallback)(void *timer, void *privdata);
|
|
||||||
|
|
||||||
/* Context for an async connection to Redis */
|
|
||||||
typedef struct redisAsyncContext {
|
|
||||||
/* Hold the regular context, so it can be realloc'ed. */
|
|
||||||
redisContext c;
|
|
||||||
|
|
||||||
/* Setup error flags so they can be used directly. */
|
|
||||||
int err;
|
|
||||||
char *errstr;
|
|
||||||
|
|
||||||
/* Not used by hiredis */
|
|
||||||
void *data;
|
|
||||||
|
|
||||||
/* Event library data and hooks */
|
|
||||||
struct {
|
|
||||||
void *data;
|
|
||||||
|
|
||||||
/* Hooks that are called when the library expects to start
|
|
||||||
* reading/writing. These functions should be idempotent. */
|
|
||||||
void (*addRead)(void *privdata);
|
|
||||||
void (*delRead)(void *privdata);
|
|
||||||
void (*addWrite)(void *privdata);
|
|
||||||
void (*delWrite)(void *privdata);
|
|
||||||
void (*cleanup)(void *privdata);
|
|
||||||
void (*scheduleTimer)(void *privdata, struct timeval tv);
|
|
||||||
} ev;
|
|
||||||
|
|
||||||
/* Called when either the connection is terminated due to an error or per
|
|
||||||
* user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */
|
|
||||||
redisDisconnectCallback *onDisconnect;
|
|
||||||
|
|
||||||
/* Called when the first write event was received. */
|
|
||||||
redisConnectCallback *onConnect;
|
|
||||||
|
|
||||||
/* Regular command callbacks */
|
|
||||||
redisCallbackList replies;
|
|
||||||
|
|
||||||
/* Address used for connect() */
|
|
||||||
struct sockaddr *saddr;
|
|
||||||
size_t addrlen;
|
|
||||||
|
|
||||||
/* Subscription callbacks */
|
|
||||||
struct {
|
|
||||||
redisCallbackList invalid;
|
|
||||||
struct dict *channels;
|
|
||||||
struct dict *patterns;
|
|
||||||
} sub;
|
|
||||||
} redisAsyncContext;
|
|
||||||
|
|
||||||
/* Functions that proxy to hiredis */
|
|
||||||
redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options);
|
|
||||||
redisAsyncContext *redisAsyncConnect(const char *ip, int port);
|
|
||||||
redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr);
|
|
||||||
redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
|
|
||||||
const char *source_addr);
|
|
||||||
redisAsyncContext *redisAsyncConnectUnix(const char *path);
|
|
||||||
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
|
|
||||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
|
|
||||||
|
|
||||||
void redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv);
|
|
||||||
void redisAsyncDisconnect(redisAsyncContext *ac);
|
|
||||||
void redisAsyncFree(redisAsyncContext *ac);
|
|
||||||
|
|
||||||
/* Handle read/write events */
|
|
||||||
void redisAsyncHandleRead(redisAsyncContext *ac);
|
|
||||||
void redisAsyncHandleWrite(redisAsyncContext *ac);
|
|
||||||
void redisAsyncHandleTimeout(redisAsyncContext *ac);
|
|
||||||
void redisAsyncRead(redisAsyncContext *ac);
|
|
||||||
void redisAsyncWrite(redisAsyncContext *ac);
|
|
||||||
|
|
||||||
/* Command functions for an async context. Write the command to the
|
|
||||||
* output buffer and register the provided callback. */
|
|
||||||
int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap);
|
|
||||||
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...);
|
|
||||||
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen);
|
|
||||||
int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
72
deps/hiredis/async_private.h
vendored
72
deps/hiredis/async_private.h
vendored
@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __HIREDIS_ASYNC_PRIVATE_H
|
|
||||||
#define __HIREDIS_ASYNC_PRIVATE_H
|
|
||||||
|
|
||||||
#define _EL_ADD_READ(ctx) \
|
|
||||||
do { \
|
|
||||||
refreshTimeout(ctx); \
|
|
||||||
if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \
|
|
||||||
} while (0)
|
|
||||||
#define _EL_DEL_READ(ctx) do { \
|
|
||||||
if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \
|
|
||||||
} while(0)
|
|
||||||
#define _EL_ADD_WRITE(ctx) \
|
|
||||||
do { \
|
|
||||||
refreshTimeout(ctx); \
|
|
||||||
if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \
|
|
||||||
} while (0)
|
|
||||||
#define _EL_DEL_WRITE(ctx) do { \
|
|
||||||
if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \
|
|
||||||
} while(0)
|
|
||||||
#define _EL_CLEANUP(ctx) do { \
|
|
||||||
if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \
|
|
||||||
ctx->ev.cleanup = NULL; \
|
|
||||||
} while(0);
|
|
||||||
|
|
||||||
static inline void refreshTimeout(redisAsyncContext *ctx) {
|
|
||||||
if (ctx->c.timeout && ctx->ev.scheduleTimer &&
|
|
||||||
(ctx->c.timeout->tv_sec || ctx->c.timeout->tv_usec)) {
|
|
||||||
ctx->ev.scheduleTimer(ctx->ev.data, *ctx->c.timeout);
|
|
||||||
// } else {
|
|
||||||
// printf("Not scheduling timer.. (tmo=%p)\n", ctx->c.timeout);
|
|
||||||
// if (ctx->c.timeout){
|
|
||||||
// printf("tv_sec: %u. tv_usec: %u\n", ctx->c.timeout->tv_sec,
|
|
||||||
// ctx->c.timeout->tv_usec);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void __redisAsyncDisconnect(redisAsyncContext *ac);
|
|
||||||
void redisProcessCallbacks(redisAsyncContext *ac);
|
|
||||||
|
|
||||||
#endif /* __HIREDIS_ASYNC_PRIVATE_H */
|
|
338
deps/hiredis/dict.c
vendored
338
deps/hiredis/dict.c
vendored
@ -1,338 +0,0 @@
|
|||||||
/* Hash table implementation.
|
|
||||||
*
|
|
||||||
* This file implements in memory hash tables with insert/del/replace/find/
|
|
||||||
* get-random-element operations. Hash tables will auto resize if needed
|
|
||||||
* tables of power of two in size are used, collisions are handled by
|
|
||||||
* chaining. See the source code for more information... :)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fmacros.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include "dict.h"
|
|
||||||
|
|
||||||
/* -------------------------- private prototypes ---------------------------- */
|
|
||||||
|
|
||||||
static int _dictExpandIfNeeded(dict *ht);
|
|
||||||
static unsigned long _dictNextPower(unsigned long size);
|
|
||||||
static int _dictKeyIndex(dict *ht, const void *key);
|
|
||||||
static int _dictInit(dict *ht, dictType *type, void *privDataPtr);
|
|
||||||
|
|
||||||
/* -------------------------- hash functions -------------------------------- */
|
|
||||||
|
|
||||||
/* Generic hash function (a popular one from Bernstein).
|
|
||||||
* I tested a few and this was the best. */
|
|
||||||
static unsigned int dictGenHashFunction(const unsigned char *buf, int len) {
|
|
||||||
unsigned int hash = 5381;
|
|
||||||
|
|
||||||
while (len--)
|
|
||||||
hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------- API implementation ------------------------- */
|
|
||||||
|
|
||||||
/* Reset an hashtable already initialized with ht_init().
|
|
||||||
* NOTE: This function should only called by ht_destroy(). */
|
|
||||||
static void _dictReset(dict *ht) {
|
|
||||||
ht->table = NULL;
|
|
||||||
ht->size = 0;
|
|
||||||
ht->sizemask = 0;
|
|
||||||
ht->used = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create a new hash table */
|
|
||||||
static dict *dictCreate(dictType *type, void *privDataPtr) {
|
|
||||||
dict *ht = malloc(sizeof(*ht));
|
|
||||||
_dictInit(ht,type,privDataPtr);
|
|
||||||
return ht;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize the hash table */
|
|
||||||
static int _dictInit(dict *ht, dictType *type, void *privDataPtr) {
|
|
||||||
_dictReset(ht);
|
|
||||||
ht->type = type;
|
|
||||||
ht->privdata = privDataPtr;
|
|
||||||
return DICT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Expand or create the hashtable */
|
|
||||||
static int dictExpand(dict *ht, unsigned long size) {
|
|
||||||
dict n; /* the new hashtable */
|
|
||||||
unsigned long realsize = _dictNextPower(size), i;
|
|
||||||
|
|
||||||
/* the size is invalid if it is smaller than the number of
|
|
||||||
* elements already inside the hashtable */
|
|
||||||
if (ht->used > size)
|
|
||||||
return DICT_ERR;
|
|
||||||
|
|
||||||
_dictInit(&n, ht->type, ht->privdata);
|
|
||||||
n.size = realsize;
|
|
||||||
n.sizemask = realsize-1;
|
|
||||||
n.table = calloc(realsize,sizeof(dictEntry*));
|
|
||||||
|
|
||||||
/* Copy all the elements from the old to the new table:
|
|
||||||
* note that if the old hash table is empty ht->size is zero,
|
|
||||||
* so dictExpand just creates an hash table. */
|
|
||||||
n.used = ht->used;
|
|
||||||
for (i = 0; i < ht->size && ht->used > 0; i++) {
|
|
||||||
dictEntry *he, *nextHe;
|
|
||||||
|
|
||||||
if (ht->table[i] == NULL) continue;
|
|
||||||
|
|
||||||
/* For each hash entry on this slot... */
|
|
||||||
he = ht->table[i];
|
|
||||||
while(he) {
|
|
||||||
unsigned int h;
|
|
||||||
|
|
||||||
nextHe = he->next;
|
|
||||||
/* Get the new element index */
|
|
||||||
h = dictHashKey(ht, he->key) & n.sizemask;
|
|
||||||
he->next = n.table[h];
|
|
||||||
n.table[h] = he;
|
|
||||||
ht->used--;
|
|
||||||
/* Pass to the next element */
|
|
||||||
he = nextHe;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(ht->used == 0);
|
|
||||||
free(ht->table);
|
|
||||||
|
|
||||||
/* Remap the new hashtable in the old */
|
|
||||||
*ht = n;
|
|
||||||
return DICT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add an element to the target hash table */
|
|
||||||
static int dictAdd(dict *ht, void *key, void *val) {
|
|
||||||
int index;
|
|
||||||
dictEntry *entry;
|
|
||||||
|
|
||||||
/* Get the index of the new element, or -1 if
|
|
||||||
* the element already exists. */
|
|
||||||
if ((index = _dictKeyIndex(ht, key)) == -1)
|
|
||||||
return DICT_ERR;
|
|
||||||
|
|
||||||
/* Allocates the memory and stores key */
|
|
||||||
entry = malloc(sizeof(*entry));
|
|
||||||
entry->next = ht->table[index];
|
|
||||||
ht->table[index] = entry;
|
|
||||||
|
|
||||||
/* Set the hash entry fields. */
|
|
||||||
dictSetHashKey(ht, entry, key);
|
|
||||||
dictSetHashVal(ht, entry, val);
|
|
||||||
ht->used++;
|
|
||||||
return DICT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add an element, discarding the old if the key already exists.
|
|
||||||
* Return 1 if the key was added from scratch, 0 if there was already an
|
|
||||||
* element with such key and dictReplace() just performed a value update
|
|
||||||
* operation. */
|
|
||||||
static int dictReplace(dict *ht, void *key, void *val) {
|
|
||||||
dictEntry *entry, auxentry;
|
|
||||||
|
|
||||||
/* Try to add the element. If the key
|
|
||||||
* does not exists dictAdd will succeed. */
|
|
||||||
if (dictAdd(ht, key, val) == DICT_OK)
|
|
||||||
return 1;
|
|
||||||
/* It already exists, get the entry */
|
|
||||||
entry = dictFind(ht, key);
|
|
||||||
/* Free the old value and set the new one */
|
|
||||||
/* Set the new value and free the old one. Note that it is important
|
|
||||||
* to do that in this order, as the value may just be exactly the same
|
|
||||||
* as the previous one. In this context, think to reference counting,
|
|
||||||
* you want to increment (set), and then decrement (free), and not the
|
|
||||||
* reverse. */
|
|
||||||
auxentry = *entry;
|
|
||||||
dictSetHashVal(ht, entry, val);
|
|
||||||
dictFreeEntryVal(ht, &auxentry);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Search and remove an element */
|
|
||||||
static int dictDelete(dict *ht, const void *key) {
|
|
||||||
unsigned int h;
|
|
||||||
dictEntry *de, *prevde;
|
|
||||||
|
|
||||||
if (ht->size == 0)
|
|
||||||
return DICT_ERR;
|
|
||||||
h = dictHashKey(ht, key) & ht->sizemask;
|
|
||||||
de = ht->table[h];
|
|
||||||
|
|
||||||
prevde = NULL;
|
|
||||||
while(de) {
|
|
||||||
if (dictCompareHashKeys(ht,key,de->key)) {
|
|
||||||
/* Unlink the element from the list */
|
|
||||||
if (prevde)
|
|
||||||
prevde->next = de->next;
|
|
||||||
else
|
|
||||||
ht->table[h] = de->next;
|
|
||||||
|
|
||||||
dictFreeEntryKey(ht,de);
|
|
||||||
dictFreeEntryVal(ht,de);
|
|
||||||
free(de);
|
|
||||||
ht->used--;
|
|
||||||
return DICT_OK;
|
|
||||||
}
|
|
||||||
prevde = de;
|
|
||||||
de = de->next;
|
|
||||||
}
|
|
||||||
return DICT_ERR; /* not found */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Destroy an entire hash table */
|
|
||||||
static int _dictClear(dict *ht) {
|
|
||||||
unsigned long i;
|
|
||||||
|
|
||||||
/* Free all the elements */
|
|
||||||
for (i = 0; i < ht->size && ht->used > 0; i++) {
|
|
||||||
dictEntry *he, *nextHe;
|
|
||||||
|
|
||||||
if ((he = ht->table[i]) == NULL) continue;
|
|
||||||
while(he) {
|
|
||||||
nextHe = he->next;
|
|
||||||
dictFreeEntryKey(ht, he);
|
|
||||||
dictFreeEntryVal(ht, he);
|
|
||||||
free(he);
|
|
||||||
ht->used--;
|
|
||||||
he = nextHe;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Free the table and the allocated cache structure */
|
|
||||||
free(ht->table);
|
|
||||||
/* Re-initialize the table */
|
|
||||||
_dictReset(ht);
|
|
||||||
return DICT_OK; /* never fails */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear & Release the hash table */
|
|
||||||
static void dictRelease(dict *ht) {
|
|
||||||
_dictClear(ht);
|
|
||||||
free(ht);
|
|
||||||
}
|
|
||||||
|
|
||||||
static dictEntry *dictFind(dict *ht, const void *key) {
|
|
||||||
dictEntry *he;
|
|
||||||
unsigned int h;
|
|
||||||
|
|
||||||
if (ht->size == 0) return NULL;
|
|
||||||
h = dictHashKey(ht, key) & ht->sizemask;
|
|
||||||
he = ht->table[h];
|
|
||||||
while(he) {
|
|
||||||
if (dictCompareHashKeys(ht, key, he->key))
|
|
||||||
return he;
|
|
||||||
he = he->next;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static dictIterator *dictGetIterator(dict *ht) {
|
|
||||||
dictIterator *iter = malloc(sizeof(*iter));
|
|
||||||
|
|
||||||
iter->ht = ht;
|
|
||||||
iter->index = -1;
|
|
||||||
iter->entry = NULL;
|
|
||||||
iter->nextEntry = NULL;
|
|
||||||
return iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
static dictEntry *dictNext(dictIterator *iter) {
|
|
||||||
while (1) {
|
|
||||||
if (iter->entry == NULL) {
|
|
||||||
iter->index++;
|
|
||||||
if (iter->index >=
|
|
||||||
(signed)iter->ht->size) break;
|
|
||||||
iter->entry = iter->ht->table[iter->index];
|
|
||||||
} else {
|
|
||||||
iter->entry = iter->nextEntry;
|
|
||||||
}
|
|
||||||
if (iter->entry) {
|
|
||||||
/* We need to save the 'next' here, the iterator user
|
|
||||||
* may delete the entry we are returning. */
|
|
||||||
iter->nextEntry = iter->entry->next;
|
|
||||||
return iter->entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dictReleaseIterator(dictIterator *iter) {
|
|
||||||
free(iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------- private functions ------------------------------ */
|
|
||||||
|
|
||||||
/* Expand the hash table if needed */
|
|
||||||
static int _dictExpandIfNeeded(dict *ht) {
|
|
||||||
/* If the hash table is empty expand it to the initial size,
|
|
||||||
* if the table is "full" dobule its size. */
|
|
||||||
if (ht->size == 0)
|
|
||||||
return dictExpand(ht, DICT_HT_INITIAL_SIZE);
|
|
||||||
if (ht->used == ht->size)
|
|
||||||
return dictExpand(ht, ht->size*2);
|
|
||||||
return DICT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Our hash table capability is a power of two */
|
|
||||||
static unsigned long _dictNextPower(unsigned long size) {
|
|
||||||
unsigned long i = DICT_HT_INITIAL_SIZE;
|
|
||||||
|
|
||||||
if (size >= LONG_MAX) return LONG_MAX;
|
|
||||||
while(1) {
|
|
||||||
if (i >= size)
|
|
||||||
return i;
|
|
||||||
i *= 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the index of a free slot that can be populated with
|
|
||||||
* an hash entry for the given 'key'.
|
|
||||||
* If the key already exists, -1 is returned. */
|
|
||||||
static int _dictKeyIndex(dict *ht, const void *key) {
|
|
||||||
unsigned int h;
|
|
||||||
dictEntry *he;
|
|
||||||
|
|
||||||
/* Expand the hashtable if needed */
|
|
||||||
if (_dictExpandIfNeeded(ht) == DICT_ERR)
|
|
||||||
return -1;
|
|
||||||
/* Compute the key hash value */
|
|
||||||
h = dictHashKey(ht, key) & ht->sizemask;
|
|
||||||
/* Search if this slot does not already contain the given key */
|
|
||||||
he = ht->table[h];
|
|
||||||
while(he) {
|
|
||||||
if (dictCompareHashKeys(ht, key, he->key))
|
|
||||||
return -1;
|
|
||||||
he = he->next;
|
|
||||||
}
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
126
deps/hiredis/dict.h
vendored
126
deps/hiredis/dict.h
vendored
@ -1,126 +0,0 @@
|
|||||||
/* Hash table implementation.
|
|
||||||
*
|
|
||||||
* This file implements in memory hash tables with insert/del/replace/find/
|
|
||||||
* get-random-element operations. Hash tables will auto resize if needed
|
|
||||||
* tables of power of two in size are used, collisions are handled by
|
|
||||||
* chaining. See the source code for more information... :)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __DICT_H
|
|
||||||
#define __DICT_H
|
|
||||||
|
|
||||||
#define DICT_OK 0
|
|
||||||
#define DICT_ERR 1
|
|
||||||
|
|
||||||
/* Unused arguments generate annoying warnings... */
|
|
||||||
#define DICT_NOTUSED(V) ((void) V)
|
|
||||||
|
|
||||||
typedef struct dictEntry {
|
|
||||||
void *key;
|
|
||||||
void *val;
|
|
||||||
struct dictEntry *next;
|
|
||||||
} dictEntry;
|
|
||||||
|
|
||||||
typedef struct dictType {
|
|
||||||
unsigned int (*hashFunction)(const void *key);
|
|
||||||
void *(*keyDup)(void *privdata, const void *key);
|
|
||||||
void *(*valDup)(void *privdata, const void *obj);
|
|
||||||
int (*keyCompare)(void *privdata, const void *key1, const void *key2);
|
|
||||||
void (*keyDestructor)(void *privdata, void *key);
|
|
||||||
void (*valDestructor)(void *privdata, void *obj);
|
|
||||||
} dictType;
|
|
||||||
|
|
||||||
typedef struct dict {
|
|
||||||
dictEntry **table;
|
|
||||||
dictType *type;
|
|
||||||
unsigned long size;
|
|
||||||
unsigned long sizemask;
|
|
||||||
unsigned long used;
|
|
||||||
void *privdata;
|
|
||||||
} dict;
|
|
||||||
|
|
||||||
typedef struct dictIterator {
|
|
||||||
dict *ht;
|
|
||||||
int index;
|
|
||||||
dictEntry *entry, *nextEntry;
|
|
||||||
} dictIterator;
|
|
||||||
|
|
||||||
/* This is the initial size of every hash table */
|
|
||||||
#define DICT_HT_INITIAL_SIZE 4
|
|
||||||
|
|
||||||
/* ------------------------------- Macros ------------------------------------*/
|
|
||||||
#define dictFreeEntryVal(ht, entry) \
|
|
||||||
if ((ht)->type->valDestructor) \
|
|
||||||
(ht)->type->valDestructor((ht)->privdata, (entry)->val)
|
|
||||||
|
|
||||||
#define dictSetHashVal(ht, entry, _val_) do { \
|
|
||||||
if ((ht)->type->valDup) \
|
|
||||||
entry->val = (ht)->type->valDup((ht)->privdata, _val_); \
|
|
||||||
else \
|
|
||||||
entry->val = (_val_); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define dictFreeEntryKey(ht, entry) \
|
|
||||||
if ((ht)->type->keyDestructor) \
|
|
||||||
(ht)->type->keyDestructor((ht)->privdata, (entry)->key)
|
|
||||||
|
|
||||||
#define dictSetHashKey(ht, entry, _key_) do { \
|
|
||||||
if ((ht)->type->keyDup) \
|
|
||||||
entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \
|
|
||||||
else \
|
|
||||||
entry->key = (_key_); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define dictCompareHashKeys(ht, key1, key2) \
|
|
||||||
(((ht)->type->keyCompare) ? \
|
|
||||||
(ht)->type->keyCompare((ht)->privdata, key1, key2) : \
|
|
||||||
(key1) == (key2))
|
|
||||||
|
|
||||||
#define dictHashKey(ht, key) (ht)->type->hashFunction(key)
|
|
||||||
|
|
||||||
#define dictGetEntryKey(he) ((he)->key)
|
|
||||||
#define dictGetEntryVal(he) ((he)->val)
|
|
||||||
#define dictSlots(ht) ((ht)->size)
|
|
||||||
#define dictSize(ht) ((ht)->used)
|
|
||||||
|
|
||||||
/* API */
|
|
||||||
static unsigned int dictGenHashFunction(const unsigned char *buf, int len);
|
|
||||||
static dict *dictCreate(dictType *type, void *privDataPtr);
|
|
||||||
static int dictExpand(dict *ht, unsigned long size);
|
|
||||||
static int dictAdd(dict *ht, void *key, void *val);
|
|
||||||
static int dictReplace(dict *ht, void *key, void *val);
|
|
||||||
static int dictDelete(dict *ht, const void *key);
|
|
||||||
static void dictRelease(dict *ht);
|
|
||||||
static dictEntry * dictFind(dict *ht, const void *key);
|
|
||||||
static dictIterator *dictGetIterator(dict *ht);
|
|
||||||
static dictEntry *dictNext(dictIterator *iter);
|
|
||||||
static void dictReleaseIterator(dictIterator *iter);
|
|
||||||
|
|
||||||
#endif /* __DICT_H */
|
|
46
deps/hiredis/examples/CMakeLists.txt
vendored
46
deps/hiredis/examples/CMakeLists.txt
vendored
@ -1,46 +0,0 @@
|
|||||||
INCLUDE(FindPkgConfig)
|
|
||||||
# Check for GLib
|
|
||||||
|
|
||||||
PKG_CHECK_MODULES(GLIB2 glib-2.0)
|
|
||||||
if (GLIB2_FOUND)
|
|
||||||
INCLUDE_DIRECTORIES(${GLIB2_INCLUDE_DIRS})
|
|
||||||
LINK_DIRECTORIES(${GLIB2_LIBRARY_DIRS})
|
|
||||||
ADD_EXECUTABLE(example-glib example-glib.c)
|
|
||||||
TARGET_LINK_LIBRARIES(example-glib hiredis ${GLIB2_LIBRARIES})
|
|
||||||
ENDIF(GLIB2_FOUND)
|
|
||||||
|
|
||||||
FIND_PATH(LIBEV ev.h
|
|
||||||
HINTS /usr/local /usr/opt/local
|
|
||||||
ENV LIBEV_INCLUDE_DIR)
|
|
||||||
|
|
||||||
if (LIBEV)
|
|
||||||
# Just compile and link with libev
|
|
||||||
ADD_EXECUTABLE(example-libev example-libev.c)
|
|
||||||
TARGET_LINK_LIBRARIES(example-libev hiredis ev)
|
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
FIND_PATH(LIBEVENT event.h)
|
|
||||||
if (LIBEVENT)
|
|
||||||
ADD_EXECUTABLE(example-libevent example-libevent)
|
|
||||||
TARGET_LINK_LIBRARIES(example-libevent hiredis event)
|
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
FIND_PATH(LIBUV uv.h)
|
|
||||||
IF (LIBUV)
|
|
||||||
ADD_EXECUTABLE(example-libuv example-libuv.c)
|
|
||||||
TARGET_LINK_LIBRARIES(example-libuv hiredis uv)
|
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
IF (APPLE)
|
|
||||||
FIND_LIBRARY(CF CoreFoundation)
|
|
||||||
ADD_EXECUTABLE(example-macosx example-macosx.c)
|
|
||||||
TARGET_LINK_LIBRARIES(example-macosx hiredis ${CF})
|
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
IF (ENABLE_SSL)
|
|
||||||
ADD_EXECUTABLE(example-ssl example-ssl.c)
|
|
||||||
TARGET_LINK_LIBRARIES(example-ssl hiredis hiredis_ssl)
|
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
ADD_EXECUTABLE(example example.c)
|
|
||||||
TARGET_LINK_LIBRARIES(example hiredis)
|
|
62
deps/hiredis/examples/example-ae.c
vendored
62
deps/hiredis/examples/example-ae.c
vendored
@ -1,62 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include <hiredis.h>
|
|
||||||
#include <async.h>
|
|
||||||
#include <adapters/ae.h>
|
|
||||||
|
|
||||||
/* Put event loop in the global scope, so it can be explicitly stopped */
|
|
||||||
static aeEventLoop *loop;
|
|
||||||
|
|
||||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
|
||||||
redisReply *reply = r;
|
|
||||||
if (reply == NULL) return;
|
|
||||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
|
||||||
|
|
||||||
/* Disconnect after receiving the reply to GET */
|
|
||||||
redisAsyncDisconnect(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void connectCallback(const redisAsyncContext *c, int status) {
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
aeStop(loop);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Connected...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
aeStop(loop);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Disconnected...\n");
|
|
||||||
aeStop(loop);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main (int argc, char **argv) {
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
|
||||||
|
|
||||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
|
||||||
if (c->err) {
|
|
||||||
/* Let *c leak for now... */
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
loop = aeCreateEventLoop(64);
|
|
||||||
redisAeAttach(loop, c);
|
|
||||||
redisAsyncSetConnectCallback(c,connectCallback);
|
|
||||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
|
||||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
|
|
||||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
|
||||||
aeMain(loop);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
73
deps/hiredis/examples/example-glib.c
vendored
73
deps/hiredis/examples/example-glib.c
vendored
@ -1,73 +0,0 @@
|
|||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <hiredis.h>
|
|
||||||
#include <async.h>
|
|
||||||
#include <adapters/glib.h>
|
|
||||||
|
|
||||||
static GMainLoop *mainloop;
|
|
||||||
|
|
||||||
static void
|
|
||||||
connect_cb (const redisAsyncContext *ac G_GNUC_UNUSED,
|
|
||||||
int status)
|
|
||||||
{
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
g_printerr("Failed to connect: %s\n", ac->errstr);
|
|
||||||
g_main_loop_quit(mainloop);
|
|
||||||
} else {
|
|
||||||
g_printerr("Connected...\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
disconnect_cb (const redisAsyncContext *ac G_GNUC_UNUSED,
|
|
||||||
int status)
|
|
||||||
{
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
g_error("Failed to disconnect: %s", ac->errstr);
|
|
||||||
} else {
|
|
||||||
g_printerr("Disconnected...\n");
|
|
||||||
g_main_loop_quit(mainloop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
command_cb(redisAsyncContext *ac,
|
|
||||||
gpointer r,
|
|
||||||
gpointer user_data G_GNUC_UNUSED)
|
|
||||||
{
|
|
||||||
redisReply *reply = r;
|
|
||||||
|
|
||||||
if (reply) {
|
|
||||||
g_print("REPLY: %s\n", reply->str);
|
|
||||||
}
|
|
||||||
|
|
||||||
redisAsyncDisconnect(ac);
|
|
||||||
}
|
|
||||||
|
|
||||||
gint
|
|
||||||
main (gint argc G_GNUC_UNUSED,
|
|
||||||
gchar *argv[] G_GNUC_UNUSED)
|
|
||||||
{
|
|
||||||
redisAsyncContext *ac;
|
|
||||||
GMainContext *context = NULL;
|
|
||||||
GSource *source;
|
|
||||||
|
|
||||||
ac = redisAsyncConnect("127.0.0.1", 6379);
|
|
||||||
if (ac->err) {
|
|
||||||
g_printerr("%s\n", ac->errstr);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
source = redis_source_new(ac);
|
|
||||||
mainloop = g_main_loop_new(context, FALSE);
|
|
||||||
g_source_attach(source, context);
|
|
||||||
|
|
||||||
redisAsyncSetConnectCallback(ac, connect_cb);
|
|
||||||
redisAsyncSetDisconnectCallback(ac, disconnect_cb);
|
|
||||||
redisAsyncCommand(ac, command_cb, NULL, "SET key 1234");
|
|
||||||
redisAsyncCommand(ac, command_cb, NULL, "GET key");
|
|
||||||
|
|
||||||
g_main_loop_run(mainloop);
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
58
deps/hiredis/examples/example-ivykis.c
vendored
58
deps/hiredis/examples/example-ivykis.c
vendored
@ -1,58 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include <hiredis.h>
|
|
||||||
#include <async.h>
|
|
||||||
#include <adapters/ivykis.h>
|
|
||||||
|
|
||||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
|
||||||
redisReply *reply = r;
|
|
||||||
if (reply == NULL) return;
|
|
||||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
|
||||||
|
|
||||||
/* Disconnect after receiving the reply to GET */
|
|
||||||
redisAsyncDisconnect(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void connectCallback(const redisAsyncContext *c, int status) {
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
printf("Connected...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
printf("Disconnected...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main (int argc, char **argv) {
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
|
||||||
|
|
||||||
iv_init();
|
|
||||||
|
|
||||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
|
||||||
if (c->err) {
|
|
||||||
/* Let *c leak for now... */
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
redisIvykisAttach(c);
|
|
||||||
redisAsyncSetConnectCallback(c,connectCallback);
|
|
||||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
|
||||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
|
|
||||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
|
||||||
|
|
||||||
iv_main();
|
|
||||||
|
|
||||||
iv_deinit();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
52
deps/hiredis/examples/example-libev.c
vendored
52
deps/hiredis/examples/example-libev.c
vendored
@ -1,52 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include <hiredis.h>
|
|
||||||
#include <async.h>
|
|
||||||
#include <adapters/libev.h>
|
|
||||||
|
|
||||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
|
||||||
redisReply *reply = r;
|
|
||||||
if (reply == NULL) return;
|
|
||||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
|
||||||
|
|
||||||
/* Disconnect after receiving the reply to GET */
|
|
||||||
redisAsyncDisconnect(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void connectCallback(const redisAsyncContext *c, int status) {
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
printf("Connected...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
printf("Disconnected...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main (int argc, char **argv) {
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
|
||||||
|
|
||||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
|
||||||
if (c->err) {
|
|
||||||
/* Let *c leak for now... */
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
redisLibevAttach(EV_DEFAULT_ c);
|
|
||||||
redisAsyncSetConnectCallback(c,connectCallback);
|
|
||||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
|
||||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
|
|
||||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
|
||||||
ev_loop(EV_DEFAULT_ 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
73
deps/hiredis/examples/example-libevent-ssl.c
vendored
73
deps/hiredis/examples/example-libevent-ssl.c
vendored
@ -1,73 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include <hiredis.h>
|
|
||||||
#include <hiredis_ssl.h>
|
|
||||||
#include <async.h>
|
|
||||||
#include <adapters/libevent.h>
|
|
||||||
|
|
||||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
|
||||||
redisReply *reply = r;
|
|
||||||
if (reply == NULL) return;
|
|
||||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
|
||||||
|
|
||||||
/* Disconnect after receiving the reply to GET */
|
|
||||||
redisAsyncDisconnect(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void connectCallback(const redisAsyncContext *c, int status) {
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
printf("Connected...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
printf("Disconnected...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main (int argc, char **argv) {
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
|
||||||
struct event_base *base = event_base_new();
|
|
||||||
if (argc < 5) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Usage: %s <key> <host> <port> <cert> <certKey> [ca]\n", argv[0]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *value = argv[1];
|
|
||||||
size_t nvalue = strlen(value);
|
|
||||||
|
|
||||||
const char *hostname = argv[2];
|
|
||||||
int port = atoi(argv[3]);
|
|
||||||
|
|
||||||
const char *cert = argv[4];
|
|
||||||
const char *certKey = argv[5];
|
|
||||||
const char *caCert = argc > 5 ? argv[6] : NULL;
|
|
||||||
|
|
||||||
redisAsyncContext *c = redisAsyncConnect(hostname, port);
|
|
||||||
if (c->err) {
|
|
||||||
/* Let *c leak for now... */
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (redisSecureConnection(&c->c, caCert, cert, certKey, "sni") != REDIS_OK) {
|
|
||||||
printf("SSL Error!\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
redisLibeventAttach(c,base);
|
|
||||||
redisAsyncSetConnectCallback(c,connectCallback);
|
|
||||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
|
||||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", value, nvalue);
|
|
||||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
|
||||||
event_base_dispatch(base);
|
|
||||||
return 0;
|
|
||||||
}
|
|
64
deps/hiredis/examples/example-libevent.c
vendored
64
deps/hiredis/examples/example-libevent.c
vendored
@ -1,64 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include <hiredis.h>
|
|
||||||
#include <async.h>
|
|
||||||
#include <adapters/libevent.h>
|
|
||||||
|
|
||||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
|
||||||
redisReply *reply = r;
|
|
||||||
if (reply == NULL) {
|
|
||||||
if (c->errstr) {
|
|
||||||
printf("errstr: %s\n", c->errstr);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
|
||||||
|
|
||||||
/* Disconnect after receiving the reply to GET */
|
|
||||||
redisAsyncDisconnect(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void connectCallback(const redisAsyncContext *c, int status) {
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
printf("Connected...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
printf("Disconnected...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main (int argc, char **argv) {
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
|
||||||
struct event_base *base = event_base_new();
|
|
||||||
redisOptions options = {0};
|
|
||||||
REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
|
|
||||||
struct timeval tv = {0};
|
|
||||||
tv.tv_sec = 1;
|
|
||||||
options.timeout = &tv;
|
|
||||||
|
|
||||||
|
|
||||||
redisAsyncContext *c = redisAsyncConnectWithOptions(&options);
|
|
||||||
if (c->err) {
|
|
||||||
/* Let *c leak for now... */
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
redisLibeventAttach(c,base);
|
|
||||||
redisAsyncSetConnectCallback(c,connectCallback);
|
|
||||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
|
||||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
|
|
||||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
|
||||||
event_base_dispatch(base);
|
|
||||||
return 0;
|
|
||||||
}
|
|
53
deps/hiredis/examples/example-libuv.c
vendored
53
deps/hiredis/examples/example-libuv.c
vendored
@ -1,53 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include <hiredis.h>
|
|
||||||
#include <async.h>
|
|
||||||
#include <adapters/libuv.h>
|
|
||||||
|
|
||||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
|
||||||
redisReply *reply = r;
|
|
||||||
if (reply == NULL) return;
|
|
||||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
|
||||||
|
|
||||||
/* Disconnect after receiving the reply to GET */
|
|
||||||
redisAsyncDisconnect(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void connectCallback(const redisAsyncContext *c, int status) {
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
printf("Connected...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
printf("Disconnected...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main (int argc, char **argv) {
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
|
||||||
uv_loop_t* loop = uv_default_loop();
|
|
||||||
|
|
||||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
|
||||||
if (c->err) {
|
|
||||||
/* Let *c leak for now... */
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
redisLibuvAttach(c,loop);
|
|
||||||
redisAsyncSetConnectCallback(c,connectCallback);
|
|
||||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
|
||||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
|
|
||||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
|
||||||
uv_run(loop, UV_RUN_DEFAULT);
|
|
||||||
return 0;
|
|
||||||
}
|
|
66
deps/hiredis/examples/example-macosx.c
vendored
66
deps/hiredis/examples/example-macosx.c
vendored
@ -1,66 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Дмитрий Бахвалов on 13.07.15.
|
|
||||||
// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include <hiredis.h>
|
|
||||||
#include <async.h>
|
|
||||||
#include <adapters/macosx.h>
|
|
||||||
|
|
||||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
|
||||||
redisReply *reply = r;
|
|
||||||
if (reply == NULL) return;
|
|
||||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
|
||||||
|
|
||||||
/* Disconnect after receiving the reply to GET */
|
|
||||||
redisAsyncDisconnect(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void connectCallback(const redisAsyncContext *c, int status) {
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
printf("Connected...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
|
||||||
if (status != REDIS_OK) {
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CFRunLoopStop(CFRunLoopGetCurrent());
|
|
||||||
printf("Disconnected...\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main (int argc, char **argv) {
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
|
||||||
|
|
||||||
CFRunLoopRef loop = CFRunLoopGetCurrent();
|
|
||||||
if( !loop ) {
|
|
||||||
printf("Error: Cannot get current run loop\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
|
||||||
if (c->err) {
|
|
||||||
/* Let *c leak for now... */
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
redisMacOSAttach(c, loop);
|
|
||||||
|
|
||||||
redisAsyncSetConnectCallback(c,connectCallback);
|
|
||||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
|
||||||
|
|
||||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
|
|
||||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
|
||||||
|
|
||||||
CFRunLoopRun();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
46
deps/hiredis/examples/example-qt.cpp
vendored
46
deps/hiredis/examples/example-qt.cpp
vendored
@ -1,46 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
#include "example-qt.h"
|
|
||||||
|
|
||||||
void getCallback(redisAsyncContext *, void * r, void * privdata) {
|
|
||||||
|
|
||||||
redisReply * reply = static_cast<redisReply *>(r);
|
|
||||||
ExampleQt * ex = static_cast<ExampleQt *>(privdata);
|
|
||||||
if (reply == nullptr || ex == nullptr) return;
|
|
||||||
|
|
||||||
cout << "key: " << reply->str << endl;
|
|
||||||
|
|
||||||
ex->finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExampleQt::run() {
|
|
||||||
|
|
||||||
m_ctx = redisAsyncConnect("localhost", 6379);
|
|
||||||
|
|
||||||
if (m_ctx->err) {
|
|
||||||
cerr << "Error: " << m_ctx->errstr << endl;
|
|
||||||
redisAsyncFree(m_ctx);
|
|
||||||
emit finished();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_adapter.setContext(m_ctx);
|
|
||||||
|
|
||||||
redisAsyncCommand(m_ctx, NULL, NULL, "SET key %s", m_value);
|
|
||||||
redisAsyncCommand(m_ctx, getCallback, this, "GET key");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main (int argc, char **argv) {
|
|
||||||
|
|
||||||
QCoreApplication app(argc, argv);
|
|
||||||
|
|
||||||
ExampleQt example(argv[argc-1]);
|
|
||||||
|
|
||||||
QObject::connect(&example, SIGNAL(finished()), &app, SLOT(quit()));
|
|
||||||
QTimer::singleShot(0, &example, SLOT(run()));
|
|
||||||
|
|
||||||
return app.exec();
|
|
||||||
}
|
|
32
deps/hiredis/examples/example-qt.h
vendored
32
deps/hiredis/examples/example-qt.h
vendored
@ -1,32 +0,0 @@
|
|||||||
#ifndef __HIREDIS_EXAMPLE_QT_H
|
|
||||||
#define __HIREDIS_EXAMPLE_QT_H
|
|
||||||
|
|
||||||
#include <adapters/qt.h>
|
|
||||||
|
|
||||||
class ExampleQt : public QObject {
|
|
||||||
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
ExampleQt(const char * value, QObject * parent = 0)
|
|
||||||
: QObject(parent), m_value(value) {}
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void finished();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void run();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void finish() { emit finished(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
const char * m_value;
|
|
||||||
redisAsyncContext * m_ctx;
|
|
||||||
RedisQtAdapter m_adapter;
|
|
||||||
|
|
||||||
friend
|
|
||||||
void getCallback(redisAsyncContext *, void *, void *);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* !__HIREDIS_EXAMPLE_QT_H */
|
|
97
deps/hiredis/examples/example-ssl.c
vendored
97
deps/hiredis/examples/example-ssl.c
vendored
@ -1,97 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <hiredis.h>
|
|
||||||
#include <hiredis_ssl.h>
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
unsigned int j;
|
|
||||||
redisContext *c;
|
|
||||||
redisReply *reply;
|
|
||||||
if (argc < 4) {
|
|
||||||
printf("Usage: %s <host> <port> <cert> <key> [ca]\n", argv[0]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1";
|
|
||||||
int port = atoi(argv[2]);
|
|
||||||
const char *cert = argv[3];
|
|
||||||
const char *key = argv[4];
|
|
||||||
const char *ca = argc > 4 ? argv[5] : NULL;
|
|
||||||
|
|
||||||
struct timeval tv = { 1, 500000 }; // 1.5 seconds
|
|
||||||
redisOptions options = {0};
|
|
||||||
REDIS_OPTIONS_SET_TCP(&options, hostname, port);
|
|
||||||
options.timeout = &tv;
|
|
||||||
c = redisConnectWithOptions(&options);
|
|
||||||
|
|
||||||
if (c == NULL || c->err) {
|
|
||||||
if (c) {
|
|
||||||
printf("Connection error: %s\n", c->errstr);
|
|
||||||
redisFree(c);
|
|
||||||
} else {
|
|
||||||
printf("Connection error: can't allocate redis context\n");
|
|
||||||
}
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (redisSecureConnection(c, ca, cert, key, "sni") != REDIS_OK) {
|
|
||||||
printf("Couldn't initialize SSL!\n");
|
|
||||||
printf("Error: %s\n", c->errstr);
|
|
||||||
redisFree(c);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PING server */
|
|
||||||
reply = redisCommand(c,"PING");
|
|
||||||
printf("PING: %s\n", reply->str);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
/* Set a key */
|
|
||||||
reply = redisCommand(c,"SET %s %s", "foo", "hello world");
|
|
||||||
printf("SET: %s\n", reply->str);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
/* Set a key using binary safe API */
|
|
||||||
reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5);
|
|
||||||
printf("SET (binary API): %s\n", reply->str);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
/* Try a GET and two INCR */
|
|
||||||
reply = redisCommand(c,"GET foo");
|
|
||||||
printf("GET foo: %s\n", reply->str);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
reply = redisCommand(c,"INCR counter");
|
|
||||||
printf("INCR counter: %lld\n", reply->integer);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
/* again ... */
|
|
||||||
reply = redisCommand(c,"INCR counter");
|
|
||||||
printf("INCR counter: %lld\n", reply->integer);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
/* Create a list of numbers, from 0 to 9 */
|
|
||||||
reply = redisCommand(c,"DEL mylist");
|
|
||||||
freeReplyObject(reply);
|
|
||||||
for (j = 0; j < 10; j++) {
|
|
||||||
char buf[64];
|
|
||||||
|
|
||||||
snprintf(buf,64,"%u",j);
|
|
||||||
reply = redisCommand(c,"LPUSH mylist element-%s", buf);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Let's check what we have inside the list */
|
|
||||||
reply = redisCommand(c,"LRANGE mylist 0 -1");
|
|
||||||
if (reply->type == REDIS_REPLY_ARRAY) {
|
|
||||||
for (j = 0; j < reply->elements; j++) {
|
|
||||||
printf("%u) %s\n", j, reply->element[j]->str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
/* Disconnects and frees the context */
|
|
||||||
redisFree(c);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
91
deps/hiredis/examples/example.c
vendored
91
deps/hiredis/examples/example.c
vendored
@ -1,91 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <hiredis.h>
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
unsigned int j, isunix = 0;
|
|
||||||
redisContext *c;
|
|
||||||
redisReply *reply;
|
|
||||||
const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1";
|
|
||||||
|
|
||||||
if (argc > 2) {
|
|
||||||
if (*argv[2] == 'u' || *argv[2] == 'U') {
|
|
||||||
isunix = 1;
|
|
||||||
/* in this case, host is the path to the unix socket */
|
|
||||||
printf("Will connect to unix socket @%s\n", hostname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int port = (argc > 2) ? atoi(argv[2]) : 6379;
|
|
||||||
|
|
||||||
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
|
|
||||||
if (isunix) {
|
|
||||||
c = redisConnectUnixWithTimeout(hostname, timeout);
|
|
||||||
} else {
|
|
||||||
c = redisConnectWithTimeout(hostname, port, timeout);
|
|
||||||
}
|
|
||||||
if (c == NULL || c->err) {
|
|
||||||
if (c) {
|
|
||||||
printf("Connection error: %s\n", c->errstr);
|
|
||||||
redisFree(c);
|
|
||||||
} else {
|
|
||||||
printf("Connection error: can't allocate redis context\n");
|
|
||||||
}
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PING server */
|
|
||||||
reply = redisCommand(c,"PING");
|
|
||||||
printf("PING: %s\n", reply->str);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
/* Set a key */
|
|
||||||
reply = redisCommand(c,"SET %s %s", "foo", "hello world");
|
|
||||||
printf("SET: %s\n", reply->str);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
/* Set a key using binary safe API */
|
|
||||||
reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5);
|
|
||||||
printf("SET (binary API): %s\n", reply->str);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
/* Try a GET and two INCR */
|
|
||||||
reply = redisCommand(c,"GET foo");
|
|
||||||
printf("GET foo: %s\n", reply->str);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
reply = redisCommand(c,"INCR counter");
|
|
||||||
printf("INCR counter: %lld\n", reply->integer);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
/* again ... */
|
|
||||||
reply = redisCommand(c,"INCR counter");
|
|
||||||
printf("INCR counter: %lld\n", reply->integer);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
/* Create a list of numbers, from 0 to 9 */
|
|
||||||
reply = redisCommand(c,"DEL mylist");
|
|
||||||
freeReplyObject(reply);
|
|
||||||
for (j = 0; j < 10; j++) {
|
|
||||||
char buf[64];
|
|
||||||
|
|
||||||
snprintf(buf,64,"%u",j);
|
|
||||||
reply = redisCommand(c,"LPUSH mylist element-%s", buf);
|
|
||||||
freeReplyObject(reply);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Let's check what we have inside the list */
|
|
||||||
reply = redisCommand(c,"LRANGE mylist 0 -1");
|
|
||||||
if (reply->type == REDIS_REPLY_ARRAY) {
|
|
||||||
for (j = 0; j < reply->elements; j++) {
|
|
||||||
printf("%u) %s\n", j, reply->element[j]->str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
freeReplyObject(reply);
|
|
||||||
|
|
||||||
/* Disconnects and frees the context */
|
|
||||||
redisFree(c);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
12
deps/hiredis/fmacros.h
vendored
12
deps/hiredis/fmacros.h
vendored
@ -1,12 +0,0 @@
|
|||||||
#ifndef __HIREDIS_FMACRO_H
|
|
||||||
#define __HIREDIS_FMACRO_H
|
|
||||||
|
|
||||||
#define _XOPEN_SOURCE 600
|
|
||||||
#define _POSIX_C_SOURCE 200112L
|
|
||||||
|
|
||||||
#if defined(__APPLE__) && defined(__MACH__)
|
|
||||||
/* Enable TCP_KEEPALIVE */
|
|
||||||
#define _DARWIN_C_SOURCE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
1086
deps/hiredis/hiredis.c
vendored
1086
deps/hiredis/hiredis.c
vendored
File diff suppressed because it is too large
Load Diff
299
deps/hiredis/hiredis.h
vendored
299
deps/hiredis/hiredis.h
vendored
@ -1,299 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
* Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
* Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
|
|
||||||
* Jan-Erik Rediger <janerik at fnordig dot com>
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __HIREDIS_H
|
|
||||||
#define __HIREDIS_H
|
|
||||||
#include "read.h"
|
|
||||||
#include <stdarg.h> /* for va_list */
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
#include <sys/time.h> /* for struct timeval */
|
|
||||||
#else
|
|
||||||
struct timeval; /* forward declaration */
|
|
||||||
#endif
|
|
||||||
#include <stdint.h> /* uintXX_t, etc */
|
|
||||||
#include "sds.h" /* for sds */
|
|
||||||
|
|
||||||
#define HIREDIS_MAJOR 0
|
|
||||||
#define HIREDIS_MINOR 14
|
|
||||||
#define HIREDIS_PATCH 0
|
|
||||||
#define HIREDIS_SONAME 0.14
|
|
||||||
|
|
||||||
/* Connection type can be blocking or non-blocking and is set in the
|
|
||||||
* least significant bit of the flags field in redisContext. */
|
|
||||||
#define REDIS_BLOCK 0x1
|
|
||||||
|
|
||||||
/* Connection may be disconnected before being free'd. The second bit
|
|
||||||
* in the flags field is set when the context is connected. */
|
|
||||||
#define REDIS_CONNECTED 0x2
|
|
||||||
|
|
||||||
/* The async API might try to disconnect cleanly and flush the output
|
|
||||||
* buffer and read all subsequent replies before disconnecting.
|
|
||||||
* This flag means no new commands can come in and the connection
|
|
||||||
* should be terminated once all replies have been read. */
|
|
||||||
#define REDIS_DISCONNECTING 0x4
|
|
||||||
|
|
||||||
/* Flag specific to the async API which means that the context should be clean
|
|
||||||
* up as soon as possible. */
|
|
||||||
#define REDIS_FREEING 0x8
|
|
||||||
|
|
||||||
/* Flag that is set when an async callback is executed. */
|
|
||||||
#define REDIS_IN_CALLBACK 0x10
|
|
||||||
|
|
||||||
/* Flag that is set when the async context has one or more subscriptions. */
|
|
||||||
#define REDIS_SUBSCRIBED 0x20
|
|
||||||
|
|
||||||
/* Flag that is set when monitor mode is active */
|
|
||||||
#define REDIS_MONITORING 0x40
|
|
||||||
|
|
||||||
/* Flag that is set when we should set SO_REUSEADDR before calling bind() */
|
|
||||||
#define REDIS_REUSEADDR 0x80
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag that indicates the user does not want the context to
|
|
||||||
* be automatically freed upon error
|
|
||||||
*/
|
|
||||||
#define REDIS_NO_AUTO_FREE 0x200
|
|
||||||
|
|
||||||
#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
|
|
||||||
|
|
||||||
/* number of times we retry to connect in the case of EADDRNOTAVAIL and
|
|
||||||
* SO_REUSEADDR is being used. */
|
|
||||||
#define REDIS_CONNECT_RETRIES 10
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* This is the reply object returned by redisCommand() */
|
|
||||||
typedef struct redisReply {
|
|
||||||
int type; /* REDIS_REPLY_* */
|
|
||||||
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
|
|
||||||
double dval; /* The double when type is REDIS_REPLY_DOUBLE */
|
|
||||||
size_t len; /* Length of string */
|
|
||||||
char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING
|
|
||||||
and REDIS_REPLY_DOUBLE (in additionl to dval). */
|
|
||||||
char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null
|
|
||||||
terminated 3 character content type, such as "txt". */
|
|
||||||
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
|
|
||||||
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
|
|
||||||
} redisReply;
|
|
||||||
|
|
||||||
redisReader *redisReaderCreate(void);
|
|
||||||
|
|
||||||
/* Function to free the reply objects hiredis returns by default. */
|
|
||||||
void freeReplyObject(void *reply);
|
|
||||||
|
|
||||||
/* Functions to format a command according to the protocol. */
|
|
||||||
int redisvFormatCommand(char **target, const char *format, va_list ap);
|
|
||||||
int redisFormatCommand(char **target, const char *format, ...);
|
|
||||||
int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
|
|
||||||
int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);
|
|
||||||
void redisFreeCommand(char *cmd);
|
|
||||||
void redisFreeSdsCommand(sds cmd);
|
|
||||||
|
|
||||||
enum redisConnectionType {
|
|
||||||
REDIS_CONN_TCP,
|
|
||||||
REDIS_CONN_UNIX,
|
|
||||||
REDIS_CONN_USERFD
|
|
||||||
};
|
|
||||||
|
|
||||||
struct redisSsl;
|
|
||||||
|
|
||||||
#define REDIS_OPT_NONBLOCK 0x01
|
|
||||||
#define REDIS_OPT_REUSEADDR 0x02
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Don't automatically free the async object on a connection failure,
|
|
||||||
* or other implicit conditions. Only free on an explicit call to disconnect() or free()
|
|
||||||
*/
|
|
||||||
#define REDIS_OPT_NOAUTOFREE 0x04
|
|
||||||
|
|
||||||
/* In Unix systems a file descriptor is a regular signed int, with -1
|
|
||||||
* representing an invalid descriptor. In Windows it is a SOCKET
|
|
||||||
* (32- or 64-bit unsigned integer depending on the architecture), where
|
|
||||||
* all bits set (~0) is INVALID_SOCKET. */
|
|
||||||
#ifndef _WIN32
|
|
||||||
typedef int redisFD;
|
|
||||||
#define REDIS_INVALID_FD -1
|
|
||||||
#else
|
|
||||||
#ifdef _WIN64
|
|
||||||
typedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */
|
|
||||||
#else
|
|
||||||
typedef unsigned long redisFD; /* SOCKET = 32-bit UINT_PTR */
|
|
||||||
#endif
|
|
||||||
#define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
/*
|
|
||||||
* the type of connection to use. This also indicates which
|
|
||||||
* `endpoint` member field to use
|
|
||||||
*/
|
|
||||||
int type;
|
|
||||||
/* bit field of REDIS_OPT_xxx */
|
|
||||||
int options;
|
|
||||||
/* timeout value. if NULL, no timeout is used */
|
|
||||||
const struct timeval *timeout;
|
|
||||||
union {
|
|
||||||
/** use this field for tcp/ip connections */
|
|
||||||
struct {
|
|
||||||
const char *source_addr;
|
|
||||||
const char *ip;
|
|
||||||
int port;
|
|
||||||
} tcp;
|
|
||||||
/** use this field for unix domain sockets */
|
|
||||||
const char *unix_socket;
|
|
||||||
/**
|
|
||||||
* use this field to have hiredis operate an already-open
|
|
||||||
* file descriptor */
|
|
||||||
redisFD fd;
|
|
||||||
} endpoint;
|
|
||||||
} redisOptions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper macros to initialize options to their specified fields.
|
|
||||||
*/
|
|
||||||
#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \
|
|
||||||
(opts)->type = REDIS_CONN_TCP; \
|
|
||||||
(opts)->endpoint.tcp.ip = ip_; \
|
|
||||||
(opts)->endpoint.tcp.port = port_;
|
|
||||||
|
|
||||||
#define REDIS_OPTIONS_SET_UNIX(opts, path) \
|
|
||||||
(opts)->type = REDIS_CONN_UNIX; \
|
|
||||||
(opts)->endpoint.unix_socket = path;
|
|
||||||
|
|
||||||
struct redisAsyncContext;
|
|
||||||
struct redisContext;
|
|
||||||
|
|
||||||
typedef struct redisContextFuncs {
|
|
||||||
void (*free_privdata)(void *);
|
|
||||||
void (*async_read)(struct redisAsyncContext *);
|
|
||||||
void (*async_write)(struct redisAsyncContext *);
|
|
||||||
int (*read)(struct redisContext *, char *, size_t);
|
|
||||||
int (*write)(struct redisContext *);
|
|
||||||
} redisContextFuncs;
|
|
||||||
|
|
||||||
/* Context for a connection to Redis */
|
|
||||||
typedef struct redisContext {
|
|
||||||
const redisContextFuncs *funcs; /* Function table */
|
|
||||||
|
|
||||||
int err; /* Error flags, 0 when there is no error */
|
|
||||||
char errstr[128]; /* String representation of error when applicable */
|
|
||||||
redisFD fd;
|
|
||||||
int flags;
|
|
||||||
char *obuf; /* Write buffer */
|
|
||||||
redisReader *reader; /* Protocol reader */
|
|
||||||
|
|
||||||
enum redisConnectionType connection_type;
|
|
||||||
struct timeval *timeout;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
char *host;
|
|
||||||
char *source_addr;
|
|
||||||
int port;
|
|
||||||
} tcp;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
char *path;
|
|
||||||
} unix_sock;
|
|
||||||
|
|
||||||
/* For non-blocking connect */
|
|
||||||
struct sockadr *saddr;
|
|
||||||
size_t addrlen;
|
|
||||||
|
|
||||||
/* Additional private data for hiredis addons such as SSL */
|
|
||||||
void *privdata;
|
|
||||||
} redisContext;
|
|
||||||
|
|
||||||
redisContext *redisConnectWithOptions(const redisOptions *options);
|
|
||||||
redisContext *redisConnect(const char *ip, int port);
|
|
||||||
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
|
|
||||||
redisContext *redisConnectNonBlock(const char *ip, int port);
|
|
||||||
redisContext *redisConnectBindNonBlock(const char *ip, int port,
|
|
||||||
const char *source_addr);
|
|
||||||
redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
|
|
||||||
const char *source_addr);
|
|
||||||
redisContext *redisConnectUnix(const char *path);
|
|
||||||
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
|
|
||||||
redisContext *redisConnectUnixNonBlock(const char *path);
|
|
||||||
redisContext *redisConnectFd(redisFD fd);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reconnect the given context using the saved information.
|
|
||||||
*
|
|
||||||
* This re-uses the exact same connect options as in the initial connection.
|
|
||||||
* host, ip (or path), timeout and bind address are reused,
|
|
||||||
* flags are used unmodified from the existing context.
|
|
||||||
*
|
|
||||||
* Returns REDIS_OK on successful connect or REDIS_ERR otherwise.
|
|
||||||
*/
|
|
||||||
int redisReconnect(redisContext *c);
|
|
||||||
|
|
||||||
int redisSetTimeout(redisContext *c, const struct timeval tv);
|
|
||||||
int redisEnableKeepAlive(redisContext *c);
|
|
||||||
void redisFree(redisContext *c);
|
|
||||||
redisFD redisFreeKeepFd(redisContext *c);
|
|
||||||
int redisBufferRead(redisContext *c);
|
|
||||||
int redisBufferWrite(redisContext *c, int *done);
|
|
||||||
|
|
||||||
/* In a blocking context, this function first checks if there are unconsumed
|
|
||||||
* replies to return and returns one if so. Otherwise, it flushes the output
|
|
||||||
* buffer to the socket and reads until it has a reply. In a non-blocking
|
|
||||||
* context, it will return unconsumed replies until there are no more. */
|
|
||||||
int redisGetReply(redisContext *c, void **reply);
|
|
||||||
int redisGetReplyFromReader(redisContext *c, void **reply);
|
|
||||||
|
|
||||||
/* Write a formatted command to the output buffer. Use these functions in blocking mode
|
|
||||||
* to get a pipeline of commands. */
|
|
||||||
int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);
|
|
||||||
|
|
||||||
/* Write a command to the output buffer. Use these functions in blocking mode
|
|
||||||
* to get a pipeline of commands. */
|
|
||||||
int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
|
|
||||||
int redisAppendCommand(redisContext *c, const char *format, ...);
|
|
||||||
int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
|
|
||||||
|
|
||||||
/* Issue a command to Redis. In a blocking context, it is identical to calling
|
|
||||||
* redisAppendCommand, followed by redisGetReply. The function will return
|
|
||||||
* NULL if there was an error in performing the request, otherwise it will
|
|
||||||
* return the reply. In a non-blocking context, it is identical to calling
|
|
||||||
* only redisAppendCommand and will always return NULL. */
|
|
||||||
void *redisvCommand(redisContext *c, const char *format, va_list ap);
|
|
||||||
void *redisCommand(redisContext *c, const char *format, ...);
|
|
||||||
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
11
deps/hiredis/hiredis.pc.in
vendored
11
deps/hiredis/hiredis.pc.in
vendored
@ -1,11 +0,0 @@
|
|||||||
prefix=@CMAKE_INSTALL_PREFIX@
|
|
||||||
exec_prefix=${prefix}
|
|
||||||
libdir=${exec_prefix}/lib
|
|
||||||
includedir=${prefix}/include
|
|
||||||
pkgincludedir=${includedir}/hiredis
|
|
||||||
|
|
||||||
Name: hiredis
|
|
||||||
Description: Minimalistic C client library for Redis.
|
|
||||||
Version: @PROJECT_VERSION@
|
|
||||||
Libs: -L${libdir} -lhiredis
|
|
||||||
Cflags: -I${pkgincludedir} -D_FILE_OFFSET_BITS=64
|
|
53
deps/hiredis/hiredis_ssl.h
vendored
53
deps/hiredis/hiredis_ssl.h
vendored
@ -1,53 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2019, Redis Labs
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __HIREDIS_SSL_H
|
|
||||||
#define __HIREDIS_SSL_H
|
|
||||||
|
|
||||||
/* This is the underlying struct for SSL in ssl.h, which is not included to
|
|
||||||
* keep build dependencies short here.
|
|
||||||
*/
|
|
||||||
struct ssl_st;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Secure the connection using SSL. This should be done before any command is
|
|
||||||
* executed on the connection.
|
|
||||||
*/
|
|
||||||
int redisSecureConnection(redisContext *c, const char *capath, const char *certpath,
|
|
||||||
const char *keypath, const char *servername);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initiate SSL/TLS negotiation on a provided context.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int redisInitiateSSL(redisContext *c, struct ssl_st *ssl);
|
|
||||||
|
|
||||||
#endif /* __HIREDIS_SSL_H */
|
|
12
deps/hiredis/hiredis_ssl.pc.in
vendored
12
deps/hiredis/hiredis_ssl.pc.in
vendored
@ -1,12 +0,0 @@
|
|||||||
prefix=@CMAKE_INSTALL_PREFIX@
|
|
||||||
exec_prefix=${prefix}
|
|
||||||
libdir=${exec_prefix}/lib
|
|
||||||
includedir=${prefix}/include
|
|
||||||
pkgincludedir=${includedir}/hiredis
|
|
||||||
|
|
||||||
Name: hiredis_ssl
|
|
||||||
Description: SSL Support for hiredis.
|
|
||||||
Version: @PROJECT_VERSION@
|
|
||||||
Requires: hiredis
|
|
||||||
Libs: -L${libdir} -lhiredis_ssl
|
|
||||||
Libs.private: -lssl -lcrypto
|
|
571
deps/hiredis/net.c
vendored
571
deps/hiredis/net.c
vendored
@ -1,571 +0,0 @@
|
|||||||
/* Extracted from anet.c to work properly with Hiredis error reporting.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
* Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
* Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
|
|
||||||
* Jan-Erik Rediger <janerik at fnordig dot com>
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fmacros.h"
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "net.h"
|
|
||||||
#include "sds.h"
|
|
||||||
#include "sockcompat.h"
|
|
||||||
#include "win32.h"
|
|
||||||
|
|
||||||
/* Defined in hiredis.c */
|
|
||||||
void __redisSetError(redisContext *c, int type, const char *str);
|
|
||||||
|
|
||||||
void redisNetClose(redisContext *c) {
|
|
||||||
if (c && c->fd != REDIS_INVALID_FD) {
|
|
||||||
close(c->fd);
|
|
||||||
c->fd = REDIS_INVALID_FD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisNetRead(redisContext *c, char *buf, size_t bufcap) {
|
|
||||||
int nread = recv(c->fd, buf, bufcap, 0);
|
|
||||||
if (nread == -1) {
|
|
||||||
if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
|
|
||||||
/* Try again later */
|
|
||||||
return 0;
|
|
||||||
} else if(errno == ETIMEDOUT && (c->flags & REDIS_BLOCK)) {
|
|
||||||
/* especially in windows */
|
|
||||||
__redisSetError(c, REDIS_ERR_TIMEOUT, "recv timeout");
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
__redisSetError(c, REDIS_ERR_IO, NULL);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else if (nread == 0) {
|
|
||||||
__redisSetError(c, REDIS_ERR_EOF, "Server closed the connection");
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return nread;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisNetWrite(redisContext *c) {
|
|
||||||
int nwritten = send(c->fd, c->obuf, sdslen(c->obuf), 0);
|
|
||||||
if (nwritten < 0) {
|
|
||||||
if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
|
|
||||||
/* Try again later */
|
|
||||||
} else {
|
|
||||||
__redisSetError(c, REDIS_ERR_IO, NULL);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nwritten;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
|
|
||||||
int errorno = errno; /* snprintf() may change errno */
|
|
||||||
char buf[128] = { 0 };
|
|
||||||
size_t len = 0;
|
|
||||||
|
|
||||||
if (prefix != NULL)
|
|
||||||
len = snprintf(buf,sizeof(buf),"%s: ",prefix);
|
|
||||||
strerror_r(errorno, (char *)(buf + len), sizeof(buf) - len);
|
|
||||||
__redisSetError(c,type,buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int redisSetReuseAddr(redisContext *c) {
|
|
||||||
int on = 1;
|
|
||||||
if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
|
||||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
|
||||||
redisNetClose(c);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int redisCreateSocket(redisContext *c, int type) {
|
|
||||||
redisFD s;
|
|
||||||
if ((s = socket(type, SOCK_STREAM, 0)) == REDIS_INVALID_FD) {
|
|
||||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
c->fd = s;
|
|
||||||
if (type == AF_INET) {
|
|
||||||
if (redisSetReuseAddr(c) == REDIS_ERR) {
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int redisSetBlocking(redisContext *c, int blocking) {
|
|
||||||
#ifndef _WIN32
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
/* Set the socket nonblocking.
|
|
||||||
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
|
|
||||||
* interrupted by a signal. */
|
|
||||||
if ((flags = fcntl(c->fd, F_GETFL)) == -1) {
|
|
||||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
|
|
||||||
redisNetClose(c);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blocking)
|
|
||||||
flags &= ~O_NONBLOCK;
|
|
||||||
else
|
|
||||||
flags |= O_NONBLOCK;
|
|
||||||
|
|
||||||
if (fcntl(c->fd, F_SETFL, flags) == -1) {
|
|
||||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
|
|
||||||
redisNetClose(c);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
u_long mode = blocking ? 0 : 1;
|
|
||||||
if (ioctl(c->fd, FIONBIO, &mode) == -1) {
|
|
||||||
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "ioctl(FIONBIO)");
|
|
||||||
redisNetClose(c);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
#endif /* _WIN32 */
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisKeepAlive(redisContext *c, int interval) {
|
|
||||||
int val = 1;
|
|
||||||
redisFD fd = c->fd;
|
|
||||||
|
|
||||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
|
|
||||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
val = interval;
|
|
||||||
|
|
||||||
#if defined(__APPLE__) && defined(__MACH__)
|
|
||||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
|
|
||||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__)
|
|
||||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
|
|
||||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
val = interval/3;
|
|
||||||
if (val == 0) val = 1;
|
|
||||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
|
|
||||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
val = 3;
|
|
||||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
|
|
||||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int redisSetTcpNoDelay(redisContext *c) {
|
|
||||||
int yes = 1;
|
|
||||||
if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
|
|
||||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
|
|
||||||
redisNetClose(c);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
|
|
||||||
|
|
||||||
static int redisContextTimeoutMsec(redisContext *c, long *result)
|
|
||||||
{
|
|
||||||
const struct timeval *timeout = c->timeout;
|
|
||||||
long msec = -1;
|
|
||||||
|
|
||||||
/* Only use timeout when not NULL. */
|
|
||||||
if (timeout != NULL) {
|
|
||||||
if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
|
|
||||||
*result = msec;
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);
|
|
||||||
|
|
||||||
if (msec < 0 || msec > INT_MAX) {
|
|
||||||
msec = INT_MAX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*result = msec;
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int redisContextWaitReady(redisContext *c, long msec) {
|
|
||||||
struct pollfd wfd[1];
|
|
||||||
|
|
||||||
wfd[0].fd = c->fd;
|
|
||||||
wfd[0].events = POLLOUT;
|
|
||||||
|
|
||||||
if (errno == EINPROGRESS) {
|
|
||||||
int res;
|
|
||||||
|
|
||||||
if ((res = poll(wfd, 1, msec)) == -1) {
|
|
||||||
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
|
|
||||||
redisNetClose(c);
|
|
||||||
return REDIS_ERR;
|
|
||||||
} else if (res == 0) {
|
|
||||||
errno = ETIMEDOUT;
|
|
||||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
|
||||||
redisNetClose(c);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) {
|
|
||||||
redisCheckSocketError(c);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
|
||||||
redisNetClose(c);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisCheckConnectDone(redisContext *c, int *completed) {
|
|
||||||
int rc = connect(c->fd, (const struct sockaddr *)c->saddr, c->addrlen);
|
|
||||||
if (rc == 0) {
|
|
||||||
*completed = 1;
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
switch (errno) {
|
|
||||||
case EISCONN:
|
|
||||||
*completed = 1;
|
|
||||||
return REDIS_OK;
|
|
||||||
case EALREADY:
|
|
||||||
case EINPROGRESS:
|
|
||||||
case EWOULDBLOCK:
|
|
||||||
*completed = 0;
|
|
||||||
return REDIS_OK;
|
|
||||||
default:
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisCheckSocketError(redisContext *c) {
|
|
||||||
int err = 0, errno_saved = errno;
|
|
||||||
socklen_t errlen = sizeof(err);
|
|
||||||
|
|
||||||
if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
|
|
||||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err == 0) {
|
|
||||||
err = errno_saved;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
errno = err;
|
|
||||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
|
|
||||||
const void *to_ptr = &tv;
|
|
||||||
size_t to_sz = sizeof(tv);
|
|
||||||
#ifdef _WIN32
|
|
||||||
DWORD timeout_msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
|
||||||
to_ptr = &timeout_msec;
|
|
||||||
to_sz = sizeof(timeout_msec);
|
|
||||||
#endif
|
|
||||||
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) {
|
|
||||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,to_ptr,to_sz) == -1) {
|
|
||||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
|
||||||
const struct timeval *timeout,
|
|
||||||
const char *source_addr) {
|
|
||||||
redisFD s;
|
|
||||||
int rv, n;
|
|
||||||
char _port[6]; /* strlen("65535"); */
|
|
||||||
struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
|
|
||||||
int blocking = (c->flags & REDIS_BLOCK);
|
|
||||||
int reuseaddr = (c->flags & REDIS_REUSEADDR);
|
|
||||||
int reuses = 0;
|
|
||||||
long timeout_msec = -1;
|
|
||||||
|
|
||||||
servinfo = NULL;
|
|
||||||
c->connection_type = REDIS_CONN_TCP;
|
|
||||||
c->tcp.port = port;
|
|
||||||
|
|
||||||
/* We need to take possession of the passed parameters
|
|
||||||
* to make them reusable for a reconnect.
|
|
||||||
* We also carefully check we don't free data we already own,
|
|
||||||
* as in the case of the reconnect method.
|
|
||||||
*
|
|
||||||
* This is a bit ugly, but atleast it works and doesn't leak memory.
|
|
||||||
**/
|
|
||||||
if (c->tcp.host != addr) {
|
|
||||||
free(c->tcp.host);
|
|
||||||
|
|
||||||
c->tcp.host = strdup(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeout) {
|
|
||||||
if (c->timeout != timeout) {
|
|
||||||
if (c->timeout == NULL)
|
|
||||||
c->timeout = malloc(sizeof(struct timeval));
|
|
||||||
|
|
||||||
memcpy(c->timeout, timeout, sizeof(struct timeval));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
free(c->timeout);
|
|
||||||
c->timeout = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
|
|
||||||
__redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source_addr == NULL) {
|
|
||||||
free(c->tcp.source_addr);
|
|
||||||
c->tcp.source_addr = NULL;
|
|
||||||
} else if (c->tcp.source_addr != source_addr) {
|
|
||||||
free(c->tcp.source_addr);
|
|
||||||
c->tcp.source_addr = strdup(source_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(_port, 6, "%d", port);
|
|
||||||
memset(&hints,0,sizeof(hints));
|
|
||||||
hints.ai_family = AF_INET;
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
|
|
||||||
/* Try with IPv6 if no IPv4 address was found. We do it in this order since
|
|
||||||
* in a Redis client you can't afford to test if you have IPv6 connectivity
|
|
||||||
* as this would add latency to every connect. Otherwise a more sensible
|
|
||||||
* route could be: Use IPv6 if both addresses are available and there is IPv6
|
|
||||||
* connectivity. */
|
|
||||||
if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) {
|
|
||||||
hints.ai_family = AF_INET6;
|
|
||||||
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
|
|
||||||
__redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (p = servinfo; p != NULL; p = p->ai_next) {
|
|
||||||
addrretry:
|
|
||||||
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
c->fd = s;
|
|
||||||
if (redisSetBlocking(c,0) != REDIS_OK)
|
|
||||||
goto error;
|
|
||||||
if (c->tcp.source_addr) {
|
|
||||||
int bound = 0;
|
|
||||||
/* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
|
|
||||||
if ((rv = getaddrinfo(c->tcp.source_addr, NULL, &hints, &bservinfo)) != 0) {
|
|
||||||
char buf[128];
|
|
||||||
snprintf(buf,sizeof(buf),"Can't get addr: %s",gai_strerror(rv));
|
|
||||||
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reuseaddr) {
|
|
||||||
n = 1;
|
|
||||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,
|
|
||||||
sizeof(n)) < 0) {
|
|
||||||
freeaddrinfo(bservinfo);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (b = bservinfo; b != NULL; b = b->ai_next) {
|
|
||||||
if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
|
|
||||||
bound = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
freeaddrinfo(bservinfo);
|
|
||||||
if (!bound) {
|
|
||||||
char buf[128];
|
|
||||||
snprintf(buf,sizeof(buf),"Can't bind socket: %s",strerror(errno));
|
|
||||||
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* For repeat connection */
|
|
||||||
free(c->saddr);
|
|
||||||
c->saddr = malloc(p->ai_addrlen);
|
|
||||||
memcpy(c->saddr, p->ai_addr, p->ai_addrlen);
|
|
||||||
c->addrlen = p->ai_addrlen;
|
|
||||||
|
|
||||||
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
|
|
||||||
if (errno == EHOSTUNREACH) {
|
|
||||||
redisNetClose(c);
|
|
||||||
continue;
|
|
||||||
} else if (errno == EINPROGRESS) {
|
|
||||||
if (blocking) {
|
|
||||||
goto wait_for_ready;
|
|
||||||
}
|
|
||||||
/* This is ok.
|
|
||||||
* Note that even when it's in blocking mode, we unset blocking
|
|
||||||
* for `connect()`
|
|
||||||
*/
|
|
||||||
} else if (errno == EADDRNOTAVAIL && reuseaddr) {
|
|
||||||
if (++reuses >= REDIS_CONNECT_RETRIES) {
|
|
||||||
goto error;
|
|
||||||
} else {
|
|
||||||
redisNetClose(c);
|
|
||||||
goto addrretry;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wait_for_ready:
|
|
||||||
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (blocking && redisSetBlocking(c,1) != REDIS_OK)
|
|
||||||
goto error;
|
|
||||||
if (redisSetTcpNoDelay(c) != REDIS_OK)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
c->flags |= REDIS_CONNECTED;
|
|
||||||
rv = REDIS_OK;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
if (p == NULL) {
|
|
||||||
char buf[128];
|
|
||||||
snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno));
|
|
||||||
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
error:
|
|
||||||
rv = REDIS_ERR;
|
|
||||||
end:
|
|
||||||
if(servinfo) {
|
|
||||||
freeaddrinfo(servinfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv; // Need to return REDIS_OK if alright
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
|
||||||
const struct timeval *timeout) {
|
|
||||||
return _redisContextConnectTcp(c, addr, port, timeout, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
|
|
||||||
const struct timeval *timeout,
|
|
||||||
const char *source_addr) {
|
|
||||||
return _redisContextConnectTcp(c, addr, port, timeout, source_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
|
|
||||||
#ifndef _WIN32
|
|
||||||
int blocking = (c->flags & REDIS_BLOCK);
|
|
||||||
struct sockaddr_un *sa;
|
|
||||||
long timeout_msec = -1;
|
|
||||||
|
|
||||||
if (redisCreateSocket(c,AF_UNIX) < 0)
|
|
||||||
return REDIS_ERR;
|
|
||||||
if (redisSetBlocking(c,0) != REDIS_OK)
|
|
||||||
return REDIS_ERR;
|
|
||||||
|
|
||||||
c->connection_type = REDIS_CONN_UNIX;
|
|
||||||
if (c->unix_sock.path != path)
|
|
||||||
c->unix_sock.path = strdup(path);
|
|
||||||
|
|
||||||
if (timeout) {
|
|
||||||
if (c->timeout != timeout) {
|
|
||||||
if (c->timeout == NULL)
|
|
||||||
c->timeout = malloc(sizeof(struct timeval));
|
|
||||||
|
|
||||||
memcpy(c->timeout, timeout, sizeof(struct timeval));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
free(c->timeout);
|
|
||||||
c->timeout = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)
|
|
||||||
return REDIS_ERR;
|
|
||||||
|
|
||||||
sa = (struct sockaddr_un*)(c->saddr = malloc(sizeof(struct sockaddr_un)));
|
|
||||||
c->addrlen = sizeof(struct sockaddr_un);
|
|
||||||
sa->sun_family = AF_UNIX;
|
|
||||||
strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1);
|
|
||||||
if (connect(c->fd, (struct sockaddr*)sa, sizeof(*sa)) == -1) {
|
|
||||||
if (errno == EINPROGRESS && !blocking) {
|
|
||||||
/* This is ok. */
|
|
||||||
} else {
|
|
||||||
if (redisContextWaitReady(c,timeout_msec) != REDIS_OK)
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reset socket to be blocking after connect(2). */
|
|
||||||
if (blocking && redisSetBlocking(c,1) != REDIS_OK)
|
|
||||||
return REDIS_ERR;
|
|
||||||
|
|
||||||
c->flags |= REDIS_CONNECTED;
|
|
||||||
return REDIS_OK;
|
|
||||||
#else
|
|
||||||
/* We currently do not support Unix sockets for Windows. */
|
|
||||||
/* TODO(m): https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ */
|
|
||||||
errno = EPROTONOSUPPORT;
|
|
||||||
return REDIS_ERR;
|
|
||||||
#endif /* _WIN32 */
|
|
||||||
}
|
|
54
deps/hiredis/net.h
vendored
54
deps/hiredis/net.h
vendored
@ -1,54 +0,0 @@
|
|||||||
/* Extracted from anet.c to work properly with Hiredis error reporting.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
* Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
* Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
|
|
||||||
* Jan-Erik Rediger <janerik at fnordig dot com>
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __NET_H
|
|
||||||
#define __NET_H
|
|
||||||
|
|
||||||
#include "hiredis.h"
|
|
||||||
|
|
||||||
void redisNetClose(redisContext *c);
|
|
||||||
int redisNetRead(redisContext *c, char *buf, size_t bufcap);
|
|
||||||
int redisNetWrite(redisContext *c);
|
|
||||||
|
|
||||||
int redisCheckSocketError(redisContext *c);
|
|
||||||
int redisContextSetTimeout(redisContext *c, const struct timeval tv);
|
|
||||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);
|
|
||||||
int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
|
|
||||||
const struct timeval *timeout,
|
|
||||||
const char *source_addr);
|
|
||||||
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);
|
|
||||||
int redisKeepAlive(redisContext *c, int interval);
|
|
||||||
int redisCheckConnectDone(redisContext *c, int *completed);
|
|
||||||
|
|
||||||
#endif
|
|
681
deps/hiredis/read.c
vendored
681
deps/hiredis/read.c
vendored
@ -1,681 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fmacros.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#endif
|
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include "read.h"
|
|
||||||
#include "sds.h"
|
|
||||||
#include "win32.h"
|
|
||||||
|
|
||||||
static void __redisReaderSetError(redisReader *r, int type, const char *str) {
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
if (r->reply != NULL && r->fn && r->fn->freeObject) {
|
|
||||||
r->fn->freeObject(r->reply);
|
|
||||||
r->reply = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear input buffer on errors. */
|
|
||||||
sdsfree(r->buf);
|
|
||||||
r->buf = NULL;
|
|
||||||
r->pos = r->len = 0;
|
|
||||||
|
|
||||||
/* Reset task stack. */
|
|
||||||
r->ridx = -1;
|
|
||||||
|
|
||||||
/* Set error. */
|
|
||||||
r->err = type;
|
|
||||||
len = strlen(str);
|
|
||||||
len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1);
|
|
||||||
memcpy(r->errstr,str,len);
|
|
||||||
r->errstr[len] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t chrtos(char *buf, size_t size, char byte) {
|
|
||||||
size_t len = 0;
|
|
||||||
|
|
||||||
switch(byte) {
|
|
||||||
case '\\':
|
|
||||||
case '"':
|
|
||||||
len = snprintf(buf,size,"\"\\%c\"",byte);
|
|
||||||
break;
|
|
||||||
case '\n': len = snprintf(buf,size,"\"\\n\""); break;
|
|
||||||
case '\r': len = snprintf(buf,size,"\"\\r\""); break;
|
|
||||||
case '\t': len = snprintf(buf,size,"\"\\t\""); break;
|
|
||||||
case '\a': len = snprintf(buf,size,"\"\\a\""); break;
|
|
||||||
case '\b': len = snprintf(buf,size,"\"\\b\""); break;
|
|
||||||
default:
|
|
||||||
if (isprint(byte))
|
|
||||||
len = snprintf(buf,size,"\"%c\"",byte);
|
|
||||||
else
|
|
||||||
len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) {
|
|
||||||
char cbuf[8], sbuf[128];
|
|
||||||
|
|
||||||
chrtos(cbuf,sizeof(cbuf),byte);
|
|
||||||
snprintf(sbuf,sizeof(sbuf),
|
|
||||||
"Protocol error, got %s as reply type byte", cbuf);
|
|
||||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __redisReaderSetErrorOOM(redisReader *r) {
|
|
||||||
__redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *readBytes(redisReader *r, unsigned int bytes) {
|
|
||||||
char *p;
|
|
||||||
if (r->len-r->pos >= bytes) {
|
|
||||||
p = r->buf+r->pos;
|
|
||||||
r->pos += bytes;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find pointer to \r\n. */
|
|
||||||
static char *seekNewline(char *s, size_t len) {
|
|
||||||
int pos = 0;
|
|
||||||
int _len = len-1;
|
|
||||||
|
|
||||||
/* Position should be < len-1 because the character at "pos" should be
|
|
||||||
* followed by a \n. Note that strchr cannot be used because it doesn't
|
|
||||||
* allow to search a limited length and the buffer that is being searched
|
|
||||||
* might not have a trailing NULL character. */
|
|
||||||
while (pos < _len) {
|
|
||||||
while(pos < _len && s[pos] != '\r') pos++;
|
|
||||||
if (pos==_len) {
|
|
||||||
/* Not found. */
|
|
||||||
return NULL;
|
|
||||||
} else {
|
|
||||||
if (s[pos+1] == '\n') {
|
|
||||||
/* Found. */
|
|
||||||
return s+pos;
|
|
||||||
} else {
|
|
||||||
/* Continue searching. */
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert a string into a long long. Returns REDIS_OK if the string could be
|
|
||||||
* parsed into a (non-overflowing) long long, REDIS_ERR otherwise. The value
|
|
||||||
* will be set to the parsed value when appropriate.
|
|
||||||
*
|
|
||||||
* Note that this function demands that the string strictly represents
|
|
||||||
* a long long: no spaces or other characters before or after the string
|
|
||||||
* representing the number are accepted, nor zeroes at the start if not
|
|
||||||
* for the string "0" representing the zero number.
|
|
||||||
*
|
|
||||||
* Because of its strictness, it is safe to use this function to check if
|
|
||||||
* you can convert a string into a long long, and obtain back the string
|
|
||||||
* from the number without any loss in the string representation. */
|
|
||||||
static int string2ll(const char *s, size_t slen, long long *value) {
|
|
||||||
const char *p = s;
|
|
||||||
size_t plen = 0;
|
|
||||||
int negative = 0;
|
|
||||||
unsigned long long v;
|
|
||||||
|
|
||||||
if (plen == slen)
|
|
||||||
return REDIS_ERR;
|
|
||||||
|
|
||||||
/* Special case: first and only digit is 0. */
|
|
||||||
if (slen == 1 && p[0] == '0') {
|
|
||||||
if (value != NULL) *value = 0;
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p[0] == '-') {
|
|
||||||
negative = 1;
|
|
||||||
p++; plen++;
|
|
||||||
|
|
||||||
/* Abort on only a negative sign. */
|
|
||||||
if (plen == slen)
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* First digit should be 1-9, otherwise the string should just be 0. */
|
|
||||||
if (p[0] >= '1' && p[0] <= '9') {
|
|
||||||
v = p[0]-'0';
|
|
||||||
p++; plen++;
|
|
||||||
} else if (p[0] == '0' && slen == 1) {
|
|
||||||
*value = 0;
|
|
||||||
return REDIS_OK;
|
|
||||||
} else {
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (plen < slen && p[0] >= '0' && p[0] <= '9') {
|
|
||||||
if (v > (ULLONG_MAX / 10)) /* Overflow. */
|
|
||||||
return REDIS_ERR;
|
|
||||||
v *= 10;
|
|
||||||
|
|
||||||
if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */
|
|
||||||
return REDIS_ERR;
|
|
||||||
v += p[0]-'0';
|
|
||||||
|
|
||||||
p++; plen++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return if not all bytes were used. */
|
|
||||||
if (plen < slen)
|
|
||||||
return REDIS_ERR;
|
|
||||||
|
|
||||||
if (negative) {
|
|
||||||
if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */
|
|
||||||
return REDIS_ERR;
|
|
||||||
if (value != NULL) *value = -v;
|
|
||||||
} else {
|
|
||||||
if (v > LLONG_MAX) /* Overflow. */
|
|
||||||
return REDIS_ERR;
|
|
||||||
if (value != NULL) *value = v;
|
|
||||||
}
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *readLine(redisReader *r, int *_len) {
|
|
||||||
char *p, *s;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
p = r->buf+r->pos;
|
|
||||||
s = seekNewline(p,(r->len-r->pos));
|
|
||||||
if (s != NULL) {
|
|
||||||
len = s-(r->buf+r->pos);
|
|
||||||
r->pos += len+2; /* skip \r\n */
|
|
||||||
if (_len) *_len = len;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void moveToNextTask(redisReader *r) {
|
|
||||||
redisReadTask *cur, *prv;
|
|
||||||
while (r->ridx >= 0) {
|
|
||||||
/* Return a.s.a.p. when the stack is now empty. */
|
|
||||||
if (r->ridx == 0) {
|
|
||||||
r->ridx--;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cur = &(r->rstack[r->ridx]);
|
|
||||||
prv = &(r->rstack[r->ridx-1]);
|
|
||||||
assert(prv->type == REDIS_REPLY_ARRAY ||
|
|
||||||
prv->type == REDIS_REPLY_MAP ||
|
|
||||||
prv->type == REDIS_REPLY_SET);
|
|
||||||
if (cur->idx == prv->elements-1) {
|
|
||||||
r->ridx--;
|
|
||||||
} else {
|
|
||||||
/* Reset the type because the next item can be anything */
|
|
||||||
assert(cur->idx < prv->elements);
|
|
||||||
cur->type = -1;
|
|
||||||
cur->elements = -1;
|
|
||||||
cur->idx++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int processLineItem(redisReader *r) {
|
|
||||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
|
||||||
void *obj;
|
|
||||||
char *p;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
if ((p = readLine(r,&len)) != NULL) {
|
|
||||||
if (cur->type == REDIS_REPLY_INTEGER) {
|
|
||||||
if (r->fn && r->fn->createInteger) {
|
|
||||||
long long v;
|
|
||||||
if (string2ll(p, len, &v) == REDIS_ERR) {
|
|
||||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
|
||||||
"Bad integer value");
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
obj = r->fn->createInteger(cur,v);
|
|
||||||
} else {
|
|
||||||
obj = (void*)REDIS_REPLY_INTEGER;
|
|
||||||
}
|
|
||||||
} else if (cur->type == REDIS_REPLY_DOUBLE) {
|
|
||||||
if (r->fn && r->fn->createDouble) {
|
|
||||||
char buf[326], *eptr;
|
|
||||||
double d;
|
|
||||||
|
|
||||||
if ((size_t)len >= sizeof(buf)) {
|
|
||||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
|
||||||
"Double value is too large");
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(buf,p,len);
|
|
||||||
buf[len] = '\0';
|
|
||||||
|
|
||||||
if (strcasecmp(buf,",inf") == 0) {
|
|
||||||
d = INFINITY; /* Positive infinite. */
|
|
||||||
} else if (strcasecmp(buf,",-inf") == 0) {
|
|
||||||
d = -INFINITY; /* Nevative infinite. */
|
|
||||||
} else {
|
|
||||||
d = strtod((char*)buf,&eptr);
|
|
||||||
if (buf[0] == '\0' || eptr[0] != '\0' || isnan(d)) {
|
|
||||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
|
||||||
"Bad double value");
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
obj = r->fn->createDouble(cur,d,buf,len);
|
|
||||||
} else {
|
|
||||||
obj = (void*)REDIS_REPLY_DOUBLE;
|
|
||||||
}
|
|
||||||
} else if (cur->type == REDIS_REPLY_NIL) {
|
|
||||||
if (r->fn && r->fn->createNil)
|
|
||||||
obj = r->fn->createNil(cur);
|
|
||||||
else
|
|
||||||
obj = (void*)REDIS_REPLY_NIL;
|
|
||||||
} else if (cur->type == REDIS_REPLY_BOOL) {
|
|
||||||
int bval = p[0] == 't' || p[0] == 'T';
|
|
||||||
if (r->fn && r->fn->createBool)
|
|
||||||
obj = r->fn->createBool(cur,bval);
|
|
||||||
else
|
|
||||||
obj = (void*)REDIS_REPLY_BOOL;
|
|
||||||
} else {
|
|
||||||
/* Type will be error or status. */
|
|
||||||
if (r->fn && r->fn->createString)
|
|
||||||
obj = r->fn->createString(cur,p,len);
|
|
||||||
else
|
|
||||||
obj = (void*)(size_t)(cur->type);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj == NULL) {
|
|
||||||
__redisReaderSetErrorOOM(r);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set reply if this is the root object. */
|
|
||||||
if (r->ridx == 0) r->reply = obj;
|
|
||||||
moveToNextTask(r);
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int processBulkItem(redisReader *r) {
|
|
||||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
|
||||||
void *obj = NULL;
|
|
||||||
char *p, *s;
|
|
||||||
long long len;
|
|
||||||
unsigned long bytelen;
|
|
||||||
int success = 0;
|
|
||||||
|
|
||||||
p = r->buf+r->pos;
|
|
||||||
s = seekNewline(p,r->len-r->pos);
|
|
||||||
if (s != NULL) {
|
|
||||||
p = r->buf+r->pos;
|
|
||||||
bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
|
|
||||||
|
|
||||||
if (string2ll(p, bytelen - 2, &len) == REDIS_ERR) {
|
|
||||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
|
||||||
"Bad bulk string length");
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len < -1 || (LLONG_MAX > SIZE_MAX && len > (long long)SIZE_MAX)) {
|
|
||||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
|
||||||
"Bulk string length out of range");
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len == -1) {
|
|
||||||
/* The nil object can always be created. */
|
|
||||||
if (r->fn && r->fn->createNil)
|
|
||||||
obj = r->fn->createNil(cur);
|
|
||||||
else
|
|
||||||
obj = (void*)REDIS_REPLY_NIL;
|
|
||||||
success = 1;
|
|
||||||
} else {
|
|
||||||
/* Only continue when the buffer contains the entire bulk item. */
|
|
||||||
bytelen += len+2; /* include \r\n */
|
|
||||||
if (r->pos+bytelen <= r->len) {
|
|
||||||
if ((cur->type == REDIS_REPLY_VERB && len < 4) ||
|
|
||||||
(cur->type == REDIS_REPLY_VERB && s[5] != ':'))
|
|
||||||
{
|
|
||||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
|
||||||
"Verbatim string 4 bytes of content type are "
|
|
||||||
"missing or incorrectly encoded.");
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
if (r->fn && r->fn->createString)
|
|
||||||
obj = r->fn->createString(cur,s+2,len);
|
|
||||||
else
|
|
||||||
obj = (void*)(long)cur->type;
|
|
||||||
success = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Proceed when obj was created. */
|
|
||||||
if (success) {
|
|
||||||
if (obj == NULL) {
|
|
||||||
__redisReaderSetErrorOOM(r);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
r->pos += bytelen;
|
|
||||||
|
|
||||||
/* Set reply if this is the root object. */
|
|
||||||
if (r->ridx == 0) r->reply = obj;
|
|
||||||
moveToNextTask(r);
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Process the array, map and set types. */
|
|
||||||
static int processAggregateItem(redisReader *r) {
|
|
||||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
|
||||||
void *obj;
|
|
||||||
char *p;
|
|
||||||
long long elements;
|
|
||||||
int root = 0, len;
|
|
||||||
|
|
||||||
/* Set error for nested multi bulks with depth > 7 */
|
|
||||||
if (r->ridx == 8) {
|
|
||||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
|
||||||
"No support for nested multi bulk replies with depth > 7");
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((p = readLine(r,&len)) != NULL) {
|
|
||||||
if (string2ll(p, len, &elements) == REDIS_ERR) {
|
|
||||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
|
||||||
"Bad multi-bulk length");
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
root = (r->ridx == 0);
|
|
||||||
|
|
||||||
if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX)) {
|
|
||||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
|
||||||
"Multi-bulk length out of range");
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elements == -1) {
|
|
||||||
if (r->fn && r->fn->createNil)
|
|
||||||
obj = r->fn->createNil(cur);
|
|
||||||
else
|
|
||||||
obj = (void*)REDIS_REPLY_NIL;
|
|
||||||
|
|
||||||
if (obj == NULL) {
|
|
||||||
__redisReaderSetErrorOOM(r);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
moveToNextTask(r);
|
|
||||||
} else {
|
|
||||||
if (cur->type == REDIS_REPLY_MAP) elements *= 2;
|
|
||||||
|
|
||||||
if (r->fn && r->fn->createArray)
|
|
||||||
obj = r->fn->createArray(cur,elements);
|
|
||||||
else
|
|
||||||
obj = (void*)(long)cur->type;
|
|
||||||
|
|
||||||
if (obj == NULL) {
|
|
||||||
__redisReaderSetErrorOOM(r);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Modify task stack when there are more than 0 elements. */
|
|
||||||
if (elements > 0) {
|
|
||||||
cur->elements = elements;
|
|
||||||
cur->obj = obj;
|
|
||||||
r->ridx++;
|
|
||||||
r->rstack[r->ridx].type = -1;
|
|
||||||
r->rstack[r->ridx].elements = -1;
|
|
||||||
r->rstack[r->ridx].idx = 0;
|
|
||||||
r->rstack[r->ridx].obj = NULL;
|
|
||||||
r->rstack[r->ridx].parent = cur;
|
|
||||||
r->rstack[r->ridx].privdata = r->privdata;
|
|
||||||
} else {
|
|
||||||
moveToNextTask(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set reply if this is the root object. */
|
|
||||||
if (root) r->reply = obj;
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int processItem(redisReader *r) {
|
|
||||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
/* check if we need to read type */
|
|
||||||
if (cur->type < 0) {
|
|
||||||
if ((p = readBytes(r,1)) != NULL) {
|
|
||||||
switch (p[0]) {
|
|
||||||
case '-':
|
|
||||||
cur->type = REDIS_REPLY_ERROR;
|
|
||||||
break;
|
|
||||||
case '+':
|
|
||||||
cur->type = REDIS_REPLY_STATUS;
|
|
||||||
break;
|
|
||||||
case ':':
|
|
||||||
cur->type = REDIS_REPLY_INTEGER;
|
|
||||||
break;
|
|
||||||
case ',':
|
|
||||||
cur->type = REDIS_REPLY_DOUBLE;
|
|
||||||
break;
|
|
||||||
case '_':
|
|
||||||
cur->type = REDIS_REPLY_NIL;
|
|
||||||
break;
|
|
||||||
case '$':
|
|
||||||
cur->type = REDIS_REPLY_STRING;
|
|
||||||
break;
|
|
||||||
case '*':
|
|
||||||
cur->type = REDIS_REPLY_ARRAY;
|
|
||||||
break;
|
|
||||||
case '%':
|
|
||||||
cur->type = REDIS_REPLY_MAP;
|
|
||||||
break;
|
|
||||||
case '~':
|
|
||||||
cur->type = REDIS_REPLY_SET;
|
|
||||||
break;
|
|
||||||
case '#':
|
|
||||||
cur->type = REDIS_REPLY_BOOL;
|
|
||||||
break;
|
|
||||||
case '=':
|
|
||||||
cur->type = REDIS_REPLY_VERB;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
__redisReaderSetErrorProtocolByte(r,*p);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* could not consume 1 byte */
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* process typed item */
|
|
||||||
switch(cur->type) {
|
|
||||||
case REDIS_REPLY_ERROR:
|
|
||||||
case REDIS_REPLY_STATUS:
|
|
||||||
case REDIS_REPLY_INTEGER:
|
|
||||||
case REDIS_REPLY_DOUBLE:
|
|
||||||
case REDIS_REPLY_NIL:
|
|
||||||
case REDIS_REPLY_BOOL:
|
|
||||||
return processLineItem(r);
|
|
||||||
case REDIS_REPLY_STRING:
|
|
||||||
case REDIS_REPLY_VERB:
|
|
||||||
return processBulkItem(r);
|
|
||||||
case REDIS_REPLY_ARRAY:
|
|
||||||
case REDIS_REPLY_MAP:
|
|
||||||
case REDIS_REPLY_SET:
|
|
||||||
return processAggregateItem(r);
|
|
||||||
default:
|
|
||||||
assert(NULL);
|
|
||||||
return REDIS_ERR; /* Avoid warning. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
|
|
||||||
redisReader *r;
|
|
||||||
|
|
||||||
r = calloc(1,sizeof(redisReader));
|
|
||||||
if (r == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
r->fn = fn;
|
|
||||||
r->buf = sdsempty();
|
|
||||||
r->maxbuf = REDIS_READER_MAX_BUF;
|
|
||||||
if (r->buf == NULL) {
|
|
||||||
free(r);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
r->ridx = -1;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void redisReaderFree(redisReader *r) {
|
|
||||||
if (r == NULL)
|
|
||||||
return;
|
|
||||||
if (r->reply != NULL && r->fn && r->fn->freeObject)
|
|
||||||
r->fn->freeObject(r->reply);
|
|
||||||
sdsfree(r->buf);
|
|
||||||
free(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
|
|
||||||
sds newbuf;
|
|
||||||
|
|
||||||
/* Return early when this reader is in an erroneous state. */
|
|
||||||
if (r->err)
|
|
||||||
return REDIS_ERR;
|
|
||||||
|
|
||||||
/* Copy the provided buffer. */
|
|
||||||
if (buf != NULL && len >= 1) {
|
|
||||||
/* Destroy internal buffer when it is empty and is quite large. */
|
|
||||||
if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) {
|
|
||||||
sdsfree(r->buf);
|
|
||||||
r->buf = sdsempty();
|
|
||||||
r->pos = 0;
|
|
||||||
|
|
||||||
/* r->buf should not be NULL since we just free'd a larger one. */
|
|
||||||
assert(r->buf != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
newbuf = sdscatlen(r->buf,buf,len);
|
|
||||||
if (newbuf == NULL) {
|
|
||||||
__redisReaderSetErrorOOM(r);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
r->buf = newbuf;
|
|
||||||
r->len = sdslen(r->buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisReaderGetReply(redisReader *r, void **reply) {
|
|
||||||
/* Default target pointer to NULL. */
|
|
||||||
if (reply != NULL)
|
|
||||||
*reply = NULL;
|
|
||||||
|
|
||||||
/* Return early when this reader is in an erroneous state. */
|
|
||||||
if (r->err)
|
|
||||||
return REDIS_ERR;
|
|
||||||
|
|
||||||
/* When the buffer is empty, there will never be a reply. */
|
|
||||||
if (r->len == 0)
|
|
||||||
return REDIS_OK;
|
|
||||||
|
|
||||||
/* Set first item to process when the stack is empty. */
|
|
||||||
if (r->ridx == -1) {
|
|
||||||
r->rstack[0].type = -1;
|
|
||||||
r->rstack[0].elements = -1;
|
|
||||||
r->rstack[0].idx = -1;
|
|
||||||
r->rstack[0].obj = NULL;
|
|
||||||
r->rstack[0].parent = NULL;
|
|
||||||
r->rstack[0].privdata = r->privdata;
|
|
||||||
r->ridx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Process items in reply. */
|
|
||||||
while (r->ridx >= 0)
|
|
||||||
if (processItem(r) != REDIS_OK)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Return ASAP when an error occurred. */
|
|
||||||
if (r->err)
|
|
||||||
return REDIS_ERR;
|
|
||||||
|
|
||||||
/* Discard part of the buffer when we've consumed at least 1k, to avoid
|
|
||||||
* doing unnecessary calls to memmove() in sds.c. */
|
|
||||||
if (r->pos >= 1024) {
|
|
||||||
sdsrange(r->buf,r->pos,-1);
|
|
||||||
r->pos = 0;
|
|
||||||
r->len = sdslen(r->buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Emit a reply when there is one. */
|
|
||||||
if (r->ridx == -1) {
|
|
||||||
if (reply != NULL) {
|
|
||||||
*reply = r->reply;
|
|
||||||
} else if (r->reply != NULL && r->fn && r->fn->freeObject) {
|
|
||||||
r->fn->freeObject(r->reply);
|
|
||||||
}
|
|
||||||
r->reply = NULL;
|
|
||||||
}
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
122
deps/hiredis/read.h
vendored
122
deps/hiredis/read.h
vendored
@ -1,122 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef __HIREDIS_READ_H
|
|
||||||
#define __HIREDIS_READ_H
|
|
||||||
#include <stdio.h> /* for size_t */
|
|
||||||
|
|
||||||
#define REDIS_ERR -1
|
|
||||||
#define REDIS_OK 0
|
|
||||||
|
|
||||||
/* When an error occurs, the err flag in a context is set to hold the type of
|
|
||||||
* error that occurred. REDIS_ERR_IO means there was an I/O error and you
|
|
||||||
* should use the "errno" variable to find out what is wrong.
|
|
||||||
* For other values, the "errstr" field will hold a description. */
|
|
||||||
#define REDIS_ERR_IO 1 /* Error in read or write */
|
|
||||||
#define REDIS_ERR_EOF 3 /* End of file */
|
|
||||||
#define REDIS_ERR_PROTOCOL 4 /* Protocol error */
|
|
||||||
#define REDIS_ERR_OOM 5 /* Out of memory */
|
|
||||||
#define REDIS_ERR_TIMEOUT 6 /* Timed out */
|
|
||||||
#define REDIS_ERR_OTHER 2 /* Everything else... */
|
|
||||||
|
|
||||||
#define REDIS_REPLY_STRING 1
|
|
||||||
#define REDIS_REPLY_ARRAY 2
|
|
||||||
#define REDIS_REPLY_INTEGER 3
|
|
||||||
#define REDIS_REPLY_NIL 4
|
|
||||||
#define REDIS_REPLY_STATUS 5
|
|
||||||
#define REDIS_REPLY_ERROR 6
|
|
||||||
#define REDIS_REPLY_DOUBLE 7
|
|
||||||
#define REDIS_REPLY_BOOL 8
|
|
||||||
#define REDIS_REPLY_MAP 9
|
|
||||||
#define REDIS_REPLY_SET 10
|
|
||||||
#define REDIS_REPLY_ATTR 11
|
|
||||||
#define REDIS_REPLY_PUSH 12
|
|
||||||
#define REDIS_REPLY_BIGNUM 13
|
|
||||||
#define REDIS_REPLY_VERB 14
|
|
||||||
|
|
||||||
#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct redisReadTask {
|
|
||||||
int type;
|
|
||||||
int elements; /* number of elements in multibulk container */
|
|
||||||
int idx; /* index in parent (array) object */
|
|
||||||
void *obj; /* holds user-generated value for a read task */
|
|
||||||
struct redisReadTask *parent; /* parent task */
|
|
||||||
void *privdata; /* user-settable arbitrary field */
|
|
||||||
} redisReadTask;
|
|
||||||
|
|
||||||
typedef struct redisReplyObjectFunctions {
|
|
||||||
void *(*createString)(const redisReadTask*, char*, size_t);
|
|
||||||
void *(*createArray)(const redisReadTask*, size_t);
|
|
||||||
void *(*createInteger)(const redisReadTask*, long long);
|
|
||||||
void *(*createDouble)(const redisReadTask*, double, char*, size_t);
|
|
||||||
void *(*createNil)(const redisReadTask*);
|
|
||||||
void *(*createBool)(const redisReadTask*, int);
|
|
||||||
void (*freeObject)(void*);
|
|
||||||
} redisReplyObjectFunctions;
|
|
||||||
|
|
||||||
typedef struct redisReader {
|
|
||||||
int err; /* Error flags, 0 when there is no error */
|
|
||||||
char errstr[128]; /* String representation of error when applicable */
|
|
||||||
|
|
||||||
char *buf; /* Read buffer */
|
|
||||||
size_t pos; /* Buffer cursor */
|
|
||||||
size_t len; /* Buffer length */
|
|
||||||
size_t maxbuf; /* Max length of unused buffer */
|
|
||||||
|
|
||||||
redisReadTask rstack[9];
|
|
||||||
int ridx; /* Index of current read task */
|
|
||||||
void *reply; /* Temporary reply pointer */
|
|
||||||
|
|
||||||
redisReplyObjectFunctions *fn;
|
|
||||||
void *privdata;
|
|
||||||
} redisReader;
|
|
||||||
|
|
||||||
/* Public API for the protocol parser. */
|
|
||||||
redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn);
|
|
||||||
void redisReaderFree(redisReader *r);
|
|
||||||
int redisReaderFeed(redisReader *r, const char *buf, size_t len);
|
|
||||||
int redisReaderGetReply(redisReader *r, void **reply);
|
|
||||||
|
|
||||||
#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
|
|
||||||
#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply)
|
|
||||||
#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
1291
deps/hiredis/sds.c
vendored
1291
deps/hiredis/sds.c
vendored
File diff suppressed because it is too large
Load Diff
276
deps/hiredis/sds.h
vendored
276
deps/hiredis/sds.h
vendored
@ -1,276 +0,0 @@
|
|||||||
/* SDSLib 2.0 -- A C dynamic strings library
|
|
||||||
*
|
|
||||||
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
* Copyright (c) 2015, Oran Agra
|
|
||||||
* Copyright (c) 2015, Redis Labs, Inc
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __SDS_H
|
|
||||||
#define __SDS_H
|
|
||||||
|
|
||||||
#define SDS_MAX_PREALLOC (1024*1024)
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#define __attribute__(x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
typedef char *sds;
|
|
||||||
|
|
||||||
/* Note: sdshdr5 is never used, we just access the flags byte directly.
|
|
||||||
* However is here to document the layout of type 5 SDS strings. */
|
|
||||||
struct __attribute__ ((__packed__)) sdshdr5 {
|
|
||||||
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
|
|
||||||
char buf[];
|
|
||||||
};
|
|
||||||
struct __attribute__ ((__packed__)) sdshdr8 {
|
|
||||||
uint8_t len; /* used */
|
|
||||||
uint8_t alloc; /* excluding the header and null terminator */
|
|
||||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
|
||||||
char buf[];
|
|
||||||
};
|
|
||||||
struct __attribute__ ((__packed__)) sdshdr16 {
|
|
||||||
uint16_t len; /* used */
|
|
||||||
uint16_t alloc; /* excluding the header and null terminator */
|
|
||||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
|
||||||
char buf[];
|
|
||||||
};
|
|
||||||
struct __attribute__ ((__packed__)) sdshdr32 {
|
|
||||||
uint32_t len; /* used */
|
|
||||||
uint32_t alloc; /* excluding the header and null terminator */
|
|
||||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
|
||||||
char buf[];
|
|
||||||
};
|
|
||||||
struct __attribute__ ((__packed__)) sdshdr64 {
|
|
||||||
uint64_t len; /* used */
|
|
||||||
uint64_t alloc; /* excluding the header and null terminator */
|
|
||||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
|
||||||
char buf[];
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SDS_TYPE_5 0
|
|
||||||
#define SDS_TYPE_8 1
|
|
||||||
#define SDS_TYPE_16 2
|
|
||||||
#define SDS_TYPE_32 3
|
|
||||||
#define SDS_TYPE_64 4
|
|
||||||
#define SDS_TYPE_MASK 7
|
|
||||||
#define SDS_TYPE_BITS 3
|
|
||||||
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)));
|
|
||||||
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
|
|
||||||
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
|
|
||||||
|
|
||||||
static inline size_t sdslen(const sds s) {
|
|
||||||
unsigned char flags = s[-1];
|
|
||||||
switch(flags&SDS_TYPE_MASK) {
|
|
||||||
case SDS_TYPE_5:
|
|
||||||
return SDS_TYPE_5_LEN(flags);
|
|
||||||
case SDS_TYPE_8:
|
|
||||||
return SDS_HDR(8,s)->len;
|
|
||||||
case SDS_TYPE_16:
|
|
||||||
return SDS_HDR(16,s)->len;
|
|
||||||
case SDS_TYPE_32:
|
|
||||||
return SDS_HDR(32,s)->len;
|
|
||||||
case SDS_TYPE_64:
|
|
||||||
return SDS_HDR(64,s)->len;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline size_t sdsavail(const sds s) {
|
|
||||||
unsigned char flags = s[-1];
|
|
||||||
switch(flags&SDS_TYPE_MASK) {
|
|
||||||
case SDS_TYPE_5: {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case SDS_TYPE_8: {
|
|
||||||
SDS_HDR_VAR(8,s);
|
|
||||||
return sh->alloc - sh->len;
|
|
||||||
}
|
|
||||||
case SDS_TYPE_16: {
|
|
||||||
SDS_HDR_VAR(16,s);
|
|
||||||
return sh->alloc - sh->len;
|
|
||||||
}
|
|
||||||
case SDS_TYPE_32: {
|
|
||||||
SDS_HDR_VAR(32,s);
|
|
||||||
return sh->alloc - sh->len;
|
|
||||||
}
|
|
||||||
case SDS_TYPE_64: {
|
|
||||||
SDS_HDR_VAR(64,s);
|
|
||||||
return sh->alloc - sh->len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sdssetlen(sds s, size_t newlen) {
|
|
||||||
unsigned char flags = s[-1];
|
|
||||||
switch(flags&SDS_TYPE_MASK) {
|
|
||||||
case SDS_TYPE_5:
|
|
||||||
{
|
|
||||||
unsigned char *fp = ((unsigned char*)s)-1;
|
|
||||||
*fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SDS_TYPE_8:
|
|
||||||
SDS_HDR(8,s)->len = (uint8_t)newlen;
|
|
||||||
break;
|
|
||||||
case SDS_TYPE_16:
|
|
||||||
SDS_HDR(16,s)->len = (uint16_t)newlen;
|
|
||||||
break;
|
|
||||||
case SDS_TYPE_32:
|
|
||||||
SDS_HDR(32,s)->len = (uint32_t)newlen;
|
|
||||||
break;
|
|
||||||
case SDS_TYPE_64:
|
|
||||||
SDS_HDR(64,s)->len = (uint64_t)newlen;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sdsinclen(sds s, size_t inc) {
|
|
||||||
unsigned char flags = s[-1];
|
|
||||||
switch(flags&SDS_TYPE_MASK) {
|
|
||||||
case SDS_TYPE_5:
|
|
||||||
{
|
|
||||||
unsigned char *fp = ((unsigned char*)s)-1;
|
|
||||||
unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc;
|
|
||||||
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SDS_TYPE_8:
|
|
||||||
SDS_HDR(8,s)->len += (uint8_t)inc;
|
|
||||||
break;
|
|
||||||
case SDS_TYPE_16:
|
|
||||||
SDS_HDR(16,s)->len += (uint16_t)inc;
|
|
||||||
break;
|
|
||||||
case SDS_TYPE_32:
|
|
||||||
SDS_HDR(32,s)->len += (uint32_t)inc;
|
|
||||||
break;
|
|
||||||
case SDS_TYPE_64:
|
|
||||||
SDS_HDR(64,s)->len += (uint64_t)inc;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* sdsalloc() = sdsavail() + sdslen() */
|
|
||||||
static inline size_t sdsalloc(const sds s) {
|
|
||||||
unsigned char flags = s[-1];
|
|
||||||
switch(flags&SDS_TYPE_MASK) {
|
|
||||||
case SDS_TYPE_5:
|
|
||||||
return SDS_TYPE_5_LEN(flags);
|
|
||||||
case SDS_TYPE_8:
|
|
||||||
return SDS_HDR(8,s)->alloc;
|
|
||||||
case SDS_TYPE_16:
|
|
||||||
return SDS_HDR(16,s)->alloc;
|
|
||||||
case SDS_TYPE_32:
|
|
||||||
return SDS_HDR(32,s)->alloc;
|
|
||||||
case SDS_TYPE_64:
|
|
||||||
return SDS_HDR(64,s)->alloc;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sdssetalloc(sds s, size_t newlen) {
|
|
||||||
unsigned char flags = s[-1];
|
|
||||||
switch(flags&SDS_TYPE_MASK) {
|
|
||||||
case SDS_TYPE_5:
|
|
||||||
/* Nothing to do, this type has no total allocation info. */
|
|
||||||
break;
|
|
||||||
case SDS_TYPE_8:
|
|
||||||
SDS_HDR(8,s)->alloc = (uint8_t)newlen;
|
|
||||||
break;
|
|
||||||
case SDS_TYPE_16:
|
|
||||||
SDS_HDR(16,s)->alloc = (uint16_t)newlen;
|
|
||||||
break;
|
|
||||||
case SDS_TYPE_32:
|
|
||||||
SDS_HDR(32,s)->alloc = (uint32_t)newlen;
|
|
||||||
break;
|
|
||||||
case SDS_TYPE_64:
|
|
||||||
SDS_HDR(64,s)->alloc = (uint64_t)newlen;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sds sdsnewlen(const void *init, size_t initlen);
|
|
||||||
sds sdsnew(const char *init);
|
|
||||||
sds sdsempty(void);
|
|
||||||
sds sdsdup(const sds s);
|
|
||||||
void sdsfree(sds s);
|
|
||||||
sds sdsgrowzero(sds s, size_t len);
|
|
||||||
sds sdscatlen(sds s, const void *t, size_t len);
|
|
||||||
sds sdscat(sds s, const char *t);
|
|
||||||
sds sdscatsds(sds s, const sds t);
|
|
||||||
sds sdscpylen(sds s, const char *t, size_t len);
|
|
||||||
sds sdscpy(sds s, const char *t);
|
|
||||||
|
|
||||||
sds sdscatvprintf(sds s, const char *fmt, va_list ap);
|
|
||||||
#ifdef __GNUC__
|
|
||||||
sds sdscatprintf(sds s, const char *fmt, ...)
|
|
||||||
__attribute__((format(printf, 2, 3)));
|
|
||||||
#else
|
|
||||||
sds sdscatprintf(sds s, const char *fmt, ...);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
sds sdscatfmt(sds s, char const *fmt, ...);
|
|
||||||
sds sdstrim(sds s, const char *cset);
|
|
||||||
void sdsrange(sds s, int start, int end);
|
|
||||||
void sdsupdatelen(sds s);
|
|
||||||
void sdsclear(sds s);
|
|
||||||
int sdscmp(const sds s1, const sds s2);
|
|
||||||
sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);
|
|
||||||
void sdsfreesplitres(sds *tokens, int count);
|
|
||||||
void sdstolower(sds s);
|
|
||||||
void sdstoupper(sds s);
|
|
||||||
sds sdsfromlonglong(long long value);
|
|
||||||
sds sdscatrepr(sds s, const char *p, size_t len);
|
|
||||||
sds *sdssplitargs(const char *line, int *argc);
|
|
||||||
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
|
|
||||||
sds sdsjoin(char **argv, int argc, char *sep);
|
|
||||||
sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);
|
|
||||||
|
|
||||||
/* Low level functions exposed to the user API */
|
|
||||||
sds sdsMakeRoomFor(sds s, size_t addlen);
|
|
||||||
void sdsIncrLen(sds s, int incr);
|
|
||||||
sds sdsRemoveFreeSpace(sds s);
|
|
||||||
size_t sdsAllocSize(sds s);
|
|
||||||
void *sdsAllocPtr(sds s);
|
|
||||||
|
|
||||||
/* Export the allocator used by SDS to the program using SDS.
|
|
||||||
* Sometimes the program SDS is linked to, may use a different set of
|
|
||||||
* allocators, but may want to allocate or free things that SDS will
|
|
||||||
* respectively free or allocate. */
|
|
||||||
void *sds_malloc(size_t size);
|
|
||||||
void *sds_realloc(void *ptr, size_t size);
|
|
||||||
void sds_free(void *ptr);
|
|
||||||
|
|
||||||
#ifdef REDIS_TEST
|
|
||||||
int sdsTest(int argc, char *argv[]);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
42
deps/hiredis/sdsalloc.h
vendored
42
deps/hiredis/sdsalloc.h
vendored
@ -1,42 +0,0 @@
|
|||||||
/* SDSLib 2.0 -- A C dynamic strings library
|
|
||||||
*
|
|
||||||
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
* Copyright (c) 2015, Oran Agra
|
|
||||||
* Copyright (c) 2015, Redis Labs, Inc
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* SDS allocator selection.
|
|
||||||
*
|
|
||||||
* This file is used in order to change the SDS allocator at compile time.
|
|
||||||
* Just define the following defines to what you want to use. Also add
|
|
||||||
* the include of your alternate allocator if needed (not needed in order
|
|
||||||
* to use the default libc allocator). */
|
|
||||||
|
|
||||||
#define s_malloc malloc
|
|
||||||
#define s_realloc realloc
|
|
||||||
#define s_free free
|
|
248
deps/hiredis/sockcompat.c
vendored
248
deps/hiredis/sockcompat.c
vendored
@ -1,248 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2019, Marcus Geelnard <m at bitsnbites dot eu>
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define REDIS_SOCKCOMPAT_IMPLEMENTATION
|
|
||||||
#include "sockcompat.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
static int _wsaErrorToErrno(int err) {
|
|
||||||
switch (err) {
|
|
||||||
case WSAEWOULDBLOCK:
|
|
||||||
return EWOULDBLOCK;
|
|
||||||
case WSAEINPROGRESS:
|
|
||||||
return EINPROGRESS;
|
|
||||||
case WSAEALREADY:
|
|
||||||
return EALREADY;
|
|
||||||
case WSAENOTSOCK:
|
|
||||||
return ENOTSOCK;
|
|
||||||
case WSAEDESTADDRREQ:
|
|
||||||
return EDESTADDRREQ;
|
|
||||||
case WSAEMSGSIZE:
|
|
||||||
return EMSGSIZE;
|
|
||||||
case WSAEPROTOTYPE:
|
|
||||||
return EPROTOTYPE;
|
|
||||||
case WSAENOPROTOOPT:
|
|
||||||
return ENOPROTOOPT;
|
|
||||||
case WSAEPROTONOSUPPORT:
|
|
||||||
return EPROTONOSUPPORT;
|
|
||||||
case WSAEOPNOTSUPP:
|
|
||||||
return EOPNOTSUPP;
|
|
||||||
case WSAEAFNOSUPPORT:
|
|
||||||
return EAFNOSUPPORT;
|
|
||||||
case WSAEADDRINUSE:
|
|
||||||
return EADDRINUSE;
|
|
||||||
case WSAEADDRNOTAVAIL:
|
|
||||||
return EADDRNOTAVAIL;
|
|
||||||
case WSAENETDOWN:
|
|
||||||
return ENETDOWN;
|
|
||||||
case WSAENETUNREACH:
|
|
||||||
return ENETUNREACH;
|
|
||||||
case WSAENETRESET:
|
|
||||||
return ENETRESET;
|
|
||||||
case WSAECONNABORTED:
|
|
||||||
return ECONNABORTED;
|
|
||||||
case WSAECONNRESET:
|
|
||||||
return ECONNRESET;
|
|
||||||
case WSAENOBUFS:
|
|
||||||
return ENOBUFS;
|
|
||||||
case WSAEISCONN:
|
|
||||||
return EISCONN;
|
|
||||||
case WSAENOTCONN:
|
|
||||||
return ENOTCONN;
|
|
||||||
case WSAETIMEDOUT:
|
|
||||||
return ETIMEDOUT;
|
|
||||||
case WSAECONNREFUSED:
|
|
||||||
return ECONNREFUSED;
|
|
||||||
case WSAELOOP:
|
|
||||||
return ELOOP;
|
|
||||||
case WSAENAMETOOLONG:
|
|
||||||
return ENAMETOOLONG;
|
|
||||||
case WSAEHOSTUNREACH:
|
|
||||||
return EHOSTUNREACH;
|
|
||||||
case WSAENOTEMPTY:
|
|
||||||
return ENOTEMPTY;
|
|
||||||
default:
|
|
||||||
/* We just return a generic I/O error if we could not find a relevant error. */
|
|
||||||
return EIO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _updateErrno(int success) {
|
|
||||||
errno = success ? 0 : _wsaErrorToErrno(WSAGetLastError());
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _initWinsock() {
|
|
||||||
static int s_initialized = 0;
|
|
||||||
if (!s_initialized) {
|
|
||||||
static WSADATA wsadata;
|
|
||||||
int err = WSAStartup(MAKEWORD(2,2), &wsadata);
|
|
||||||
if (err != 0) {
|
|
||||||
errno = _wsaErrorToErrno(err);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
s_initialized = 1;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) {
|
|
||||||
/* Note: This function is likely to be called before other functions, so run init here. */
|
|
||||||
if (!_initWinsock()) {
|
|
||||||
return EAI_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (getaddrinfo(node, service, hints, res)) {
|
|
||||||
case 0: return 0;
|
|
||||||
case WSATRY_AGAIN: return EAI_AGAIN;
|
|
||||||
case WSAEINVAL: return EAI_BADFLAGS;
|
|
||||||
case WSAEAFNOSUPPORT: return EAI_FAMILY;
|
|
||||||
case WSA_NOT_ENOUGH_MEMORY: return EAI_MEMORY;
|
|
||||||
case WSAHOST_NOT_FOUND: return EAI_NONAME;
|
|
||||||
case WSATYPE_NOT_FOUND: return EAI_SERVICE;
|
|
||||||
case WSAESOCKTNOSUPPORT: return EAI_SOCKTYPE;
|
|
||||||
default: return EAI_FAIL; /* Including WSANO_RECOVERY */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *win32_gai_strerror(int errcode) {
|
|
||||||
switch (errcode) {
|
|
||||||
case 0: errcode = 0; break;
|
|
||||||
case EAI_AGAIN: errcode = WSATRY_AGAIN; break;
|
|
||||||
case EAI_BADFLAGS: errcode = WSAEINVAL; break;
|
|
||||||
case EAI_FAMILY: errcode = WSAEAFNOSUPPORT; break;
|
|
||||||
case EAI_MEMORY: errcode = WSA_NOT_ENOUGH_MEMORY; break;
|
|
||||||
case EAI_NONAME: errcode = WSAHOST_NOT_FOUND; break;
|
|
||||||
case EAI_SERVICE: errcode = WSATYPE_NOT_FOUND; break;
|
|
||||||
case EAI_SOCKTYPE: errcode = WSAESOCKTNOSUPPORT; break;
|
|
||||||
default: errcode = WSANO_RECOVERY; break; /* Including EAI_FAIL */
|
|
||||||
}
|
|
||||||
return gai_strerror(errcode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void win32_freeaddrinfo(struct addrinfo *res) {
|
|
||||||
freeaddrinfo(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
SOCKET win32_socket(int domain, int type, int protocol) {
|
|
||||||
SOCKET s;
|
|
||||||
|
|
||||||
/* Note: This function is likely to be called before other functions, so run init here. */
|
|
||||||
if (!_initWinsock()) {
|
|
||||||
return INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateErrno((s = socket(domain, type, protocol)) != INVALID_SOCKET);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp) {
|
|
||||||
int ret = ioctlsocket(fd, (long)request, argp);
|
|
||||||
_updateErrno(ret != SOCKET_ERROR);
|
|
||||||
return ret != SOCKET_ERROR ? ret : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) {
|
|
||||||
int ret = bind(sockfd, addr, addrlen);
|
|
||||||
_updateErrno(ret != SOCKET_ERROR);
|
|
||||||
return ret != SOCKET_ERROR ? ret : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) {
|
|
||||||
int ret = connect(sockfd, addr, addrlen);
|
|
||||||
_updateErrno(ret != SOCKET_ERROR);
|
|
||||||
|
|
||||||
/* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as
|
|
||||||
* EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX
|
|
||||||
* logic consistent. */
|
|
||||||
if (errno == EWOULDBLOCK) {
|
|
||||||
errno = EINPROGRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret != SOCKET_ERROR ? ret : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen) {
|
|
||||||
int ret = 0;
|
|
||||||
if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) {
|
|
||||||
if (*optlen >= sizeof (struct timeval)) {
|
|
||||||
struct timeval *tv = optval;
|
|
||||||
DWORD timeout = 0;
|
|
||||||
socklen_t dwlen = 0;
|
|
||||||
ret = getsockopt(sockfd, level, optname, (char *)&timeout, &dwlen);
|
|
||||||
tv->tv_sec = timeout / 1000;
|
|
||||||
tv->tv_usec = (timeout * 1000) % 1000000;
|
|
||||||
} else {
|
|
||||||
ret = WSAEFAULT;
|
|
||||||
}
|
|
||||||
*optlen = sizeof (struct timeval);
|
|
||||||
} else {
|
|
||||||
ret = getsockopt(sockfd, level, optname, (char*)optval, optlen);
|
|
||||||
}
|
|
||||||
_updateErrno(ret != SOCKET_ERROR);
|
|
||||||
return ret != SOCKET_ERROR ? ret : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen) {
|
|
||||||
int ret = 0;
|
|
||||||
if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) {
|
|
||||||
struct timeval *tv = optval;
|
|
||||||
DWORD timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000;
|
|
||||||
ret = setsockopt(sockfd, level, optname, (const char*)&timeout, sizeof(DWORD));
|
|
||||||
} else {
|
|
||||||
ret = setsockopt(sockfd, level, optname, (const char*)optval, optlen);
|
|
||||||
}
|
|
||||||
_updateErrno(ret != SOCKET_ERROR);
|
|
||||||
return ret != SOCKET_ERROR ? ret : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int win32_close(SOCKET fd) {
|
|
||||||
int ret = closesocket(fd);
|
|
||||||
_updateErrno(ret != SOCKET_ERROR);
|
|
||||||
return ret != SOCKET_ERROR ? ret : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags) {
|
|
||||||
int ret = recv(sockfd, (char*)buf, (int)len, flags);
|
|
||||||
_updateErrno(ret != SOCKET_ERROR);
|
|
||||||
return ret != SOCKET_ERROR ? ret : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags) {
|
|
||||||
int ret = send(sockfd, (const char*)buf, (int)len, flags);
|
|
||||||
_updateErrno(ret != SOCKET_ERROR);
|
|
||||||
return ret != SOCKET_ERROR ? ret : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) {
|
|
||||||
int ret = WSAPoll(fds, nfds, timeout);
|
|
||||||
_updateErrno(ret != SOCKET_ERROR);
|
|
||||||
return ret != SOCKET_ERROR ? ret : -1;
|
|
||||||
}
|
|
||||||
#endif /* _WIN32 */
|
|
91
deps/hiredis/sockcompat.h
vendored
91
deps/hiredis/sockcompat.h
vendored
@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2019, Marcus Geelnard <m at bitsnbites dot eu>
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __SOCKCOMPAT_H
|
|
||||||
#define __SOCKCOMPAT_H
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
/* For POSIX systems we use the standard BSD socket API. */
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/select.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <netinet/tcp.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <poll.h>
|
|
||||||
#else
|
|
||||||
/* For Windows we use winsock. */
|
|
||||||
#undef _WIN32_WINNT
|
|
||||||
#define _WIN32_WINNT 0x0600 /* To get WSAPoll etc. */
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
typedef signed long ssize_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */
|
|
||||||
int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
|
|
||||||
const char *win32_gai_strerror(int errcode);
|
|
||||||
void win32_freeaddrinfo(struct addrinfo *res);
|
|
||||||
SOCKET win32_socket(int domain, int type, int protocol);
|
|
||||||
int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp);
|
|
||||||
int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
|
||||||
int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
|
||||||
int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen);
|
|
||||||
int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen);
|
|
||||||
int win32_close(SOCKET fd);
|
|
||||||
ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags);
|
|
||||||
ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags);
|
|
||||||
typedef ULONG nfds_t;
|
|
||||||
int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout);
|
|
||||||
|
|
||||||
#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION
|
|
||||||
#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res)
|
|
||||||
#undef gai_strerror
|
|
||||||
#define gai_strerror(errcode) win32_gai_strerror(errcode)
|
|
||||||
#define freeaddrinfo(res) win32_freeaddrinfo(res)
|
|
||||||
#define socket(domain, type, protocol) win32_socket(domain, type, protocol)
|
|
||||||
#define ioctl(fd, request, argp) win32_ioctl(fd, request, argp)
|
|
||||||
#define bind(sockfd, addr, addrlen) win32_bind(sockfd, addr, addrlen)
|
|
||||||
#define connect(sockfd, addr, addrlen) win32_connect(sockfd, addr, addrlen)
|
|
||||||
#define getsockopt(sockfd, level, optname, optval, optlen) win32_getsockopt(sockfd, level, optname, optval, optlen)
|
|
||||||
#define setsockopt(sockfd, level, optname, optval, optlen) win32_setsockopt(sockfd, level, optname, optval, optlen)
|
|
||||||
#define close(fd) win32_close(fd)
|
|
||||||
#define recv(sockfd, buf, len, flags) win32_recv(sockfd, buf, len, flags)
|
|
||||||
#define send(sockfd, buf, len, flags) win32_send(sockfd, buf, len, flags)
|
|
||||||
#define poll(fds, nfds, timeout) win32_poll(fds, nfds, timeout)
|
|
||||||
#endif /* REDIS_SOCKCOMPAT_IMPLEMENTATION */
|
|
||||||
#endif /* _WIN32 */
|
|
||||||
|
|
||||||
#endif /* __SOCKCOMPAT_H */
|
|
448
deps/hiredis/ssl.c
vendored
448
deps/hiredis/ssl.c
vendored
@ -1,448 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
* Copyright (c) 2019, Redis Labs
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "hiredis.h"
|
|
||||||
#include "async.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <openssl/ssl.h>
|
|
||||||
#include <openssl/err.h>
|
|
||||||
|
|
||||||
#include "async_private.h"
|
|
||||||
|
|
||||||
void __redisSetError(redisContext *c, int type, const char *str);
|
|
||||||
|
|
||||||
/* The SSL context is attached to SSL/TLS connections as a privdata. */
|
|
||||||
typedef struct redisSSLContext {
|
|
||||||
/**
|
|
||||||
* OpenSSL SSL_CTX; It is optional and will not be set when using
|
|
||||||
* user-supplied SSL.
|
|
||||||
*/
|
|
||||||
SSL_CTX *ssl_ctx;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OpenSSL SSL object.
|
|
||||||
*/
|
|
||||||
SSL *ssl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SSL_write() requires to be called again with the same arguments it was
|
|
||||||
* previously called with in the event of an SSL_read/SSL_write situation
|
|
||||||
*/
|
|
||||||
size_t lastLen;
|
|
||||||
|
|
||||||
/** Whether the SSL layer requires read (possibly before a write) */
|
|
||||||
int wantRead;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether a write was requested prior to a read. If set, the write()
|
|
||||||
* should resume whenever a read takes place, if possible
|
|
||||||
*/
|
|
||||||
int pendingWrite;
|
|
||||||
} redisSSLContext;
|
|
||||||
|
|
||||||
/* Forward declaration */
|
|
||||||
redisContextFuncs redisContextSSLFuncs;
|
|
||||||
|
|
||||||
#ifdef HIREDIS_SSL_TRACE
|
|
||||||
/**
|
|
||||||
* Callback used for debugging
|
|
||||||
*/
|
|
||||||
static void sslLogCallback(const SSL *ssl, int where, int ret) {
|
|
||||||
const char *retstr = "";
|
|
||||||
int should_log = 1;
|
|
||||||
/* Ignore low-level SSL stuff */
|
|
||||||
|
|
||||||
if (where & SSL_CB_ALERT) {
|
|
||||||
should_log = 1;
|
|
||||||
}
|
|
||||||
if (where == SSL_CB_HANDSHAKE_START || where == SSL_CB_HANDSHAKE_DONE) {
|
|
||||||
should_log = 1;
|
|
||||||
}
|
|
||||||
if ((where & SSL_CB_EXIT) && ret == 0) {
|
|
||||||
should_log = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!should_log) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
retstr = SSL_alert_type_string(ret);
|
|
||||||
printf("ST(0x%x). %s. R(0x%x)%s\n", where, SSL_state_string_long(ssl), ret, retstr);
|
|
||||||
|
|
||||||
if (where == SSL_CB_HANDSHAKE_DONE) {
|
|
||||||
printf("Using SSL version %s. Cipher=%s\n", SSL_get_version(ssl), SSL_get_cipher_name(ssl));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OpenSSL global initialization and locking handling callbacks.
|
|
||||||
* Note that this is only required for OpenSSL < 1.1.0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
||||||
#define HIREDIS_USE_CRYPTO_LOCKS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HIREDIS_USE_CRYPTO_LOCKS
|
|
||||||
typedef pthread_mutex_t sslLockType;
|
|
||||||
static void sslLockInit(sslLockType *l) {
|
|
||||||
pthread_mutex_init(l, NULL);
|
|
||||||
}
|
|
||||||
static void sslLockAcquire(sslLockType *l) {
|
|
||||||
pthread_mutex_lock(l);
|
|
||||||
}
|
|
||||||
static void sslLockRelease(sslLockType *l) {
|
|
||||||
pthread_mutex_unlock(l);
|
|
||||||
}
|
|
||||||
static pthread_mutex_t *ossl_locks;
|
|
||||||
|
|
||||||
static void opensslDoLock(int mode, int lkid, const char *f, int line) {
|
|
||||||
sslLockType *l = ossl_locks + lkid;
|
|
||||||
|
|
||||||
if (mode & CRYPTO_LOCK) {
|
|
||||||
sslLockAcquire(l);
|
|
||||||
} else {
|
|
||||||
sslLockRelease(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
(void)f;
|
|
||||||
(void)line;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void initOpensslLocks(void) {
|
|
||||||
unsigned ii, nlocks;
|
|
||||||
if (CRYPTO_get_locking_callback() != NULL) {
|
|
||||||
/* Someone already set the callback before us. Don't destroy it! */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nlocks = CRYPTO_num_locks();
|
|
||||||
ossl_locks = malloc(sizeof(*ossl_locks) * nlocks);
|
|
||||||
for (ii = 0; ii < nlocks; ii++) {
|
|
||||||
sslLockInit(ossl_locks + ii);
|
|
||||||
}
|
|
||||||
CRYPTO_set_locking_callback(opensslDoLock);
|
|
||||||
}
|
|
||||||
#endif /* HIREDIS_USE_CRYPTO_LOCKS */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SSL Connection initialization.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
|
|
||||||
if (c->privdata) {
|
|
||||||
__redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated");
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
c->privdata = calloc(1, sizeof(redisSSLContext));
|
|
||||||
|
|
||||||
c->funcs = &redisContextSSLFuncs;
|
|
||||||
redisSSLContext *rssl = c->privdata;
|
|
||||||
|
|
||||||
rssl->ssl_ctx = ssl_ctx;
|
|
||||||
rssl->ssl = ssl;
|
|
||||||
|
|
||||||
SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
|
||||||
SSL_set_fd(rssl->ssl, c->fd);
|
|
||||||
SSL_set_connect_state(rssl->ssl);
|
|
||||||
|
|
||||||
ERR_clear_error();
|
|
||||||
int rv = SSL_connect(rssl->ssl);
|
|
||||||
if (rv == 1) {
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = SSL_get_error(rssl->ssl, rv);
|
|
||||||
if (((c->flags & REDIS_BLOCK) == 0) &&
|
|
||||||
(rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {
|
|
||||||
return REDIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c->err == 0) {
|
|
||||||
char err[512];
|
|
||||||
if (rv == SSL_ERROR_SYSCALL)
|
|
||||||
snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",strerror(errno));
|
|
||||||
else {
|
|
||||||
unsigned long e = ERR_peek_last_error();
|
|
||||||
snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",
|
|
||||||
ERR_reason_error_string(e));
|
|
||||||
}
|
|
||||||
__redisSetError(c, REDIS_ERR_IO, err);
|
|
||||||
}
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisInitiateSSL(redisContext *c, SSL *ssl) {
|
|
||||||
return redisSSLConnect(c, NULL, ssl);
|
|
||||||
}
|
|
||||||
|
|
||||||
int redisSecureConnection(redisContext *c, const char *capath,
|
|
||||||
const char *certpath, const char *keypath, const char *servername) {
|
|
||||||
|
|
||||||
SSL_CTX *ssl_ctx = NULL;
|
|
||||||
SSL *ssl = NULL;
|
|
||||||
|
|
||||||
/* Initialize global OpenSSL stuff */
|
|
||||||
static int isInit = 0;
|
|
||||||
if (!isInit) {
|
|
||||||
isInit = 1;
|
|
||||||
SSL_library_init();
|
|
||||||
#ifdef HIREDIS_USE_CRYPTO_LOCKS
|
|
||||||
initOpensslLocks();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
|
||||||
if (!ssl_ctx) {
|
|
||||||
__redisSetError(c, REDIS_ERR_OTHER, "Failed to create SSL_CTX");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HIREDIS_SSL_TRACE
|
|
||||||
SSL_CTX_set_info_callback(ssl_ctx, sslLogCallback);
|
|
||||||
#endif
|
|
||||||
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
|
||||||
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
|
|
||||||
if ((certpath != NULL && keypath == NULL) || (keypath != NULL && certpath == NULL)) {
|
|
||||||
__redisSetError(c, REDIS_ERR_OTHER, "certpath and keypath must be specified together");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (capath) {
|
|
||||||
if (!SSL_CTX_load_verify_locations(ssl_ctx, capath, NULL)) {
|
|
||||||
__redisSetError(c, REDIS_ERR_OTHER, "Invalid CA certificate");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (certpath) {
|
|
||||||
if (!SSL_CTX_use_certificate_chain_file(ssl_ctx, certpath)) {
|
|
||||||
__redisSetError(c, REDIS_ERR_OTHER, "Invalid client certificate");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (!SSL_CTX_use_PrivateKey_file(ssl_ctx, keypath, SSL_FILETYPE_PEM)) {
|
|
||||||
__redisSetError(c, REDIS_ERR_OTHER, "Invalid client key");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ssl = SSL_new(ssl_ctx);
|
|
||||||
if (!ssl) {
|
|
||||||
__redisSetError(c, REDIS_ERR_OTHER, "Couldn't create new SSL instance");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (servername) {
|
|
||||||
if (!SSL_set_tlsext_host_name(ssl, servername)) {
|
|
||||||
__redisSetError(c, REDIS_ERR_OTHER, "Couldn't set server name indication");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return redisSSLConnect(c, ssl_ctx, ssl);
|
|
||||||
|
|
||||||
error:
|
|
||||||
if (ssl) SSL_free(ssl);
|
|
||||||
if (ssl_ctx) SSL_CTX_free(ssl_ctx);
|
|
||||||
return REDIS_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int maybeCheckWant(redisSSLContext *rssl, int rv) {
|
|
||||||
/**
|
|
||||||
* If the error is WANT_READ or WANT_WRITE, the appropriate flags are set
|
|
||||||
* and true is returned. False is returned otherwise
|
|
||||||
*/
|
|
||||||
if (rv == SSL_ERROR_WANT_READ) {
|
|
||||||
rssl->wantRead = 1;
|
|
||||||
return 1;
|
|
||||||
} else if (rv == SSL_ERROR_WANT_WRITE) {
|
|
||||||
rssl->pendingWrite = 1;
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation of redisContextFuncs for SSL connections.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void redisSSLFreeContext(void *privdata){
|
|
||||||
redisSSLContext *rsc = privdata;
|
|
||||||
|
|
||||||
if (!rsc) return;
|
|
||||||
if (rsc->ssl) {
|
|
||||||
SSL_free(rsc->ssl);
|
|
||||||
rsc->ssl = NULL;
|
|
||||||
}
|
|
||||||
if (rsc->ssl_ctx) {
|
|
||||||
SSL_CTX_free(rsc->ssl_ctx);
|
|
||||||
rsc->ssl_ctx = NULL;
|
|
||||||
}
|
|
||||||
free(rsc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
|
|
||||||
redisSSLContext *rssl = c->privdata;
|
|
||||||
|
|
||||||
int nread = SSL_read(rssl->ssl, buf, bufcap);
|
|
||||||
if (nread > 0) {
|
|
||||||
return nread;
|
|
||||||
} else if (nread == 0) {
|
|
||||||
__redisSetError(c, REDIS_ERR_EOF, "Server closed the connection");
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
int err = SSL_get_error(rssl->ssl, nread);
|
|
||||||
if (c->flags & REDIS_BLOCK) {
|
|
||||||
/**
|
|
||||||
* In blocking mode, we should never end up in a situation where
|
|
||||||
* we get an error without it being an actual error, except
|
|
||||||
* in the case of EINTR, which can be spuriously received from
|
|
||||||
* debuggers or whatever.
|
|
||||||
*/
|
|
||||||
if (errno == EINTR) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
const char *msg = NULL;
|
|
||||||
if (errno == EAGAIN) {
|
|
||||||
msg = "Resource temporarily unavailable";
|
|
||||||
}
|
|
||||||
__redisSetError(c, REDIS_ERR_IO, msg);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We can very well get an EWOULDBLOCK/EAGAIN, however
|
|
||||||
*/
|
|
||||||
if (maybeCheckWant(rssl, err)) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
__redisSetError(c, REDIS_ERR_IO, NULL);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int redisSSLWrite(redisContext *c) {
|
|
||||||
redisSSLContext *rssl = c->privdata;
|
|
||||||
|
|
||||||
size_t len = rssl->lastLen ? rssl->lastLen : sdslen(c->obuf);
|
|
||||||
int rv = SSL_write(rssl->ssl, c->obuf, len);
|
|
||||||
|
|
||||||
if (rv > 0) {
|
|
||||||
rssl->lastLen = 0;
|
|
||||||
} else if (rv < 0) {
|
|
||||||
rssl->lastLen = len;
|
|
||||||
|
|
||||||
int err = SSL_get_error(rssl->ssl, rv);
|
|
||||||
if ((c->flags & REDIS_BLOCK) == 0 && maybeCheckWant(rssl, err)) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
__redisSetError(c, REDIS_ERR_IO, NULL);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisSSLAsyncRead(redisAsyncContext *ac) {
|
|
||||||
int rv;
|
|
||||||
redisSSLContext *rssl = ac->c.privdata;
|
|
||||||
redisContext *c = &ac->c;
|
|
||||||
|
|
||||||
rssl->wantRead = 0;
|
|
||||||
|
|
||||||
if (rssl->pendingWrite) {
|
|
||||||
int done;
|
|
||||||
|
|
||||||
/* This is probably just a write event */
|
|
||||||
rssl->pendingWrite = 0;
|
|
||||||
rv = redisBufferWrite(c, &done);
|
|
||||||
if (rv == REDIS_ERR) {
|
|
||||||
__redisAsyncDisconnect(ac);
|
|
||||||
return;
|
|
||||||
} else if (!done) {
|
|
||||||
_EL_ADD_WRITE(ac);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = redisBufferRead(c);
|
|
||||||
if (rv == REDIS_ERR) {
|
|
||||||
__redisAsyncDisconnect(ac);
|
|
||||||
} else {
|
|
||||||
_EL_ADD_READ(ac);
|
|
||||||
redisProcessCallbacks(ac);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void redisSSLAsyncWrite(redisAsyncContext *ac) {
|
|
||||||
int rv, done = 0;
|
|
||||||
redisSSLContext *rssl = ac->c.privdata;
|
|
||||||
redisContext *c = &ac->c;
|
|
||||||
|
|
||||||
rssl->pendingWrite = 0;
|
|
||||||
rv = redisBufferWrite(c, &done);
|
|
||||||
if (rv == REDIS_ERR) {
|
|
||||||
__redisAsyncDisconnect(ac);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!done) {
|
|
||||||
if (rssl->wantRead) {
|
|
||||||
/* Need to read-before-write */
|
|
||||||
rssl->pendingWrite = 1;
|
|
||||||
_EL_DEL_WRITE(ac);
|
|
||||||
} else {
|
|
||||||
/* No extra reads needed, just need to write more */
|
|
||||||
_EL_ADD_WRITE(ac);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Already done! */
|
|
||||||
_EL_DEL_WRITE(ac);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Always reschedule a read */
|
|
||||||
_EL_ADD_READ(ac);
|
|
||||||
}
|
|
||||||
|
|
||||||
redisContextFuncs redisContextSSLFuncs = {
|
|
||||||
.free_privdata = redisSSLFreeContext,
|
|
||||||
.async_read = redisSSLAsyncRead,
|
|
||||||
.async_write = redisSSLAsyncWrite,
|
|
||||||
.read = redisSSLRead,
|
|
||||||
.write = redisSSLWrite
|
|
||||||
};
|
|
||||||
|
|
1012
deps/hiredis/test.c
vendored
1012
deps/hiredis/test.c
vendored
File diff suppressed because it is too large
Load Diff
70
deps/hiredis/test.sh
vendored
70
deps/hiredis/test.sh
vendored
@ -1,70 +0,0 @@
|
|||||||
#!/bin/sh -ue
|
|
||||||
|
|
||||||
REDIS_SERVER=${REDIS_SERVER:-redis-server}
|
|
||||||
REDIS_PORT=${REDIS_PORT:-56379}
|
|
||||||
REDIS_SSL_PORT=${REDIS_SSL_PORT:-56443}
|
|
||||||
TEST_SSL=${TEST_SSL:-0}
|
|
||||||
SSL_TEST_ARGS=
|
|
||||||
|
|
||||||
tmpdir=$(mktemp -d)
|
|
||||||
PID_FILE=${tmpdir}/hiredis-test-redis.pid
|
|
||||||
SOCK_FILE=${tmpdir}/hiredis-test-redis.sock
|
|
||||||
|
|
||||||
if [ "$TEST_SSL" = "1" ]; then
|
|
||||||
SSL_CA_CERT=${tmpdir}/ca.crt
|
|
||||||
SSL_CA_KEY=${tmpdir}/ca.key
|
|
||||||
SSL_CERT=${tmpdir}/redis.crt
|
|
||||||
SSL_KEY=${tmpdir}/redis.key
|
|
||||||
|
|
||||||
openssl genrsa -out ${tmpdir}/ca.key 4096
|
|
||||||
openssl req \
|
|
||||||
-x509 -new -nodes -sha256 \
|
|
||||||
-key ${SSL_CA_KEY} \
|
|
||||||
-days 3650 \
|
|
||||||
-subj '/CN=Hiredis Test CA' \
|
|
||||||
-out ${SSL_CA_CERT}
|
|
||||||
openssl genrsa -out ${SSL_KEY} 2048
|
|
||||||
openssl req \
|
|
||||||
-new -sha256 \
|
|
||||||
-key ${SSL_KEY} \
|
|
||||||
-subj '/CN=Hiredis Test Cert' | \
|
|
||||||
openssl x509 \
|
|
||||||
-req -sha256 \
|
|
||||||
-CA ${SSL_CA_CERT} \
|
|
||||||
-CAkey ${SSL_CA_KEY} \
|
|
||||||
-CAserial ${tmpdir}/ca.txt \
|
|
||||||
-CAcreateserial \
|
|
||||||
-days 365 \
|
|
||||||
-out ${SSL_CERT}
|
|
||||||
|
|
||||||
SSL_TEST_ARGS="--ssl-host 127.0.0.1 --ssl-port ${REDIS_SSL_PORT} --ssl-ca-cert ${SSL_CA_CERT} --ssl-cert ${SSL_CERT} --ssl-key ${SSL_KEY}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cleanup() {
|
|
||||||
set +e
|
|
||||||
kill $(cat ${PID_FILE})
|
|
||||||
rm -rf ${tmpdir}
|
|
||||||
}
|
|
||||||
trap cleanup INT TERM EXIT
|
|
||||||
|
|
||||||
cat > ${tmpdir}/redis.conf <<EOF
|
|
||||||
daemonize yes
|
|
||||||
pidfile ${PID_FILE}
|
|
||||||
port ${REDIS_PORT}
|
|
||||||
bind 127.0.0.1
|
|
||||||
unixsocket ${SOCK_FILE}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if [ "$TEST_SSL" = "1" ]; then
|
|
||||||
cat >> ${tmpdir}/redis.conf <<EOF
|
|
||||||
tls-port ${REDIS_SSL_PORT}
|
|
||||||
tls-ca-cert-file ${SSL_CA_CERT}
|
|
||||||
tls-cert-file ${SSL_CERT}
|
|
||||||
tls-key-file ${SSL_KEY}
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat ${tmpdir}/redis.conf
|
|
||||||
${REDIS_SERVER} ${tmpdir}/redis.conf
|
|
||||||
|
|
||||||
${TEST_PREFIX:-} ./hiredis-test -h 127.0.0.1 -p ${REDIS_PORT} -s ${SOCK_FILE} ${SSL_TEST_ARGS}
|
|
56
deps/hiredis/win32.h
vendored
56
deps/hiredis/win32.h
vendored
@ -1,56 +0,0 @@
|
|||||||
#ifndef _WIN32_HELPER_INCLUDE
|
|
||||||
#define _WIN32_HELPER_INCLUDE
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
|
|
||||||
#include <winsock2.h> /* for struct timeval */
|
|
||||||
|
|
||||||
#ifndef inline
|
|
||||||
#define inline __inline
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef strcasecmp
|
|
||||||
#define strcasecmp stricmp
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef strncasecmp
|
|
||||||
#define strncasecmp strnicmp
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef va_copy
|
|
||||||
#define va_copy(d,s) ((d) = (s))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef snprintf
|
|
||||||
#define snprintf c99_snprintf
|
|
||||||
|
|
||||||
__inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap)
|
|
||||||
{
|
|
||||||
int count = -1;
|
|
||||||
|
|
||||||
if (size != 0)
|
|
||||||
count = _vsnprintf_s(str, size, _TRUNCATE, format, ap);
|
|
||||||
if (count == -1)
|
|
||||||
count = _vscprintf(format, ap);
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
__inline int c99_snprintf(char* str, size_t size, const char* format, ...)
|
|
||||||
{
|
|
||||||
int count;
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, format);
|
|
||||||
count = c99_vsnprintf(str, size, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif /* _MSC_VER */
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define strerror_r(errno,buf,len) strerror_s(buf,len,errno)
|
|
||||||
#endif /* _WIN32 */
|
|
||||||
|
|
||||||
#endif /* _WIN32_HELPER_INCLUDE */
|
|
Loading…
x
Reference in New Issue
Block a user