more test fixes:
- include "main" test file which can be configured through various options - include sample ssl test files - add boilerplate async test
This commit is contained in:
parent
91717bd15c
commit
64da57e0c7
@ -4,7 +4,14 @@ INCLUDE_DIRECTORIES(PROJECT_SOURCE_DIR)
|
||||
INCLUDE_DIRECTORIES(${LIBEVENT_INCLUDES})
|
||||
|
||||
ADD_EXECUTABLE(hiredis-gtest t_basic.cpp t_client.cpp t_async.cpp
|
||||
common.cpp)
|
||||
TARGET_LINK_LIBRARIES(hiredis-gtest gtest_main hiredis event)
|
||||
common.cpp main.cpp)
|
||||
TARGET_LINK_LIBRARIES(hiredis-gtest gtest hiredis event)
|
||||
ADD_TEST(NAME hiredis-test COMMAND hiredis-test)
|
||||
SET_PROPERTY(TARGET hiredis-gtest PROPERTY CXX_STANDARD 11)
|
||||
SET_PROPERTY(TARGET hiredis-gtest PROPERTY CXX_STANDARD 11)
|
||||
|
||||
SET(EXAMPLE_CERT "${CMAKE_CURRENT_SOURCE_DIR}/example.crt")
|
||||
SET(EXAMPLE_KEY "${CMAKE_CURRENT_SOURCE_DIR}/example.key")
|
||||
|
||||
ADD_DEFINITIONS(-DHIREDIS_TEST_SSL_CA="${EXAMPLE_CERT}")
|
||||
ADD_DEFINITIONS(-DHIREDIS_TEST_SSL_KEY="${EXAMPLE_KEY}")
|
||||
ADD_DEFINITIONS(-DHIREDIS_TEST_SSL_CERT="${EXAMPLE_CERT}")
|
||||
@ -1,11 +1,33 @@
|
||||
#include "common.h"
|
||||
#include <sstream>
|
||||
|
||||
hiredis::ClientSettings hiredis::settings_g;
|
||||
|
||||
using namespace hiredis;
|
||||
|
||||
void ClientSettings::setHost(const char *s) {
|
||||
m_mode = REDIS_CONN_TCP;
|
||||
std::string hostval(s);
|
||||
size_t idx = hostval.find(':');
|
||||
if (idx == std::string::npos) {
|
||||
// First part is hostname only
|
||||
m_hostname = hostval;
|
||||
} else {
|
||||
m_port = atoi(hostval.c_str() + idx + 1);
|
||||
hostval.resize(idx);
|
||||
}
|
||||
if (!m_port) {
|
||||
m_port = 6379;
|
||||
}
|
||||
m_hostname = hostval;
|
||||
}
|
||||
|
||||
ClientSettings::ClientSettings() {
|
||||
void ClientSettings::setUnix(const char *s) {
|
||||
m_mode = REDIS_CONN_UNIX;
|
||||
m_hostname = s;
|
||||
}
|
||||
|
||||
void ClientSettings::applyEnv() {
|
||||
std::string hostval;
|
||||
if (getenv("REDIS_SOCKET")) {
|
||||
m_hostname.assign(getenv("REDIS_SOCKET"));
|
||||
@ -14,24 +36,20 @@ ClientSettings::ClientSettings() {
|
||||
}
|
||||
|
||||
if (getenv("REDIS_HOST")) {
|
||||
hostval.assign(getenv("REDIS_HOST"));
|
||||
}
|
||||
size_t idx = hostval.find(':');
|
||||
if (idx == std::string::npos) {
|
||||
// First part is hostname only
|
||||
m_hostname = hostval;
|
||||
} else {
|
||||
m_port = atoi(hostval.c_str() + idx);
|
||||
hostval.resize(idx);
|
||||
}
|
||||
if (!m_port) {
|
||||
m_port = 6379;
|
||||
setHost(getenv("REDIS_HOST"));
|
||||
}
|
||||
|
||||
// Handle SSL settings as well
|
||||
m_ssl_cert_path = getenv("REDIS_SSL_CLIENT_CERT");
|
||||
m_ssl_key_path = getenv("REDIS_SSL_CLIENT_KEY");
|
||||
m_ssl_ca_path = getenv("REDIS_SSL_CA");
|
||||
if (getenv("REDIS_SSL_CLIENT_CERT")) {
|
||||
m_ssl_cert_path = getenv("REDIS_SSL_CLIENT_CERT");
|
||||
}
|
||||
if (getenv("REDIS_SSL_CLIENT_KEY")) {
|
||||
m_ssl_key_path = getenv("REDIS_SSL_CLIENT_KEY");
|
||||
}
|
||||
if (getenv("REDIS_SSL_CA")) {
|
||||
m_ssl_ca_path = getenv("REDIS_SSL_CA");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ClientSettings::initOptions(redisOptions& options) const {
|
||||
@ -41,3 +59,48 @@ void ClientSettings::initOptions(redisOptions& options) const {
|
||||
REDIS_OPTIONS_SET_UNIX(&options, hostname());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ConnectError::ConnectError(const redisOptions& options) {
|
||||
if (options.type == REDIS_CONN_TCP) {
|
||||
endpoint = options.endpoint.tcp.ip;
|
||||
endpoint += ":";
|
||||
endpoint += options.endpoint.tcp.port;
|
||||
} else if (options.type == REDIS_CONN_UNIX) {
|
||||
endpoint = "unix://";
|
||||
endpoint += options.endpoint.unix_socket;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientError::throwCode(int code) {
|
||||
switch (code) {
|
||||
case REDIS_ERR_IO:
|
||||
throw IOError();
|
||||
case REDIS_ERR_EOF:
|
||||
throw IOError("EOF");
|
||||
case REDIS_ERR_PROTOCOL:
|
||||
throw IOError("Protocol Error");
|
||||
case REDIS_ERR_TIMEOUT:
|
||||
throw TimeoutError();
|
||||
default: {
|
||||
std::stringstream ss;
|
||||
ss << "unknown error code: ";
|
||||
ss << code;
|
||||
throw ClientError(ss.str().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClientError::throwContext(const redisContext *c) {
|
||||
const char *s;
|
||||
switch (c->err) {
|
||||
case REDIS_ERR_IO:
|
||||
case REDIS_ERR_EOF:
|
||||
case REDIS_ERR_PROTOCOL:
|
||||
throw IOError(c->errstr);
|
||||
case REDIS_ERR_TIMEOUT:
|
||||
throw TimeoutError();
|
||||
default:
|
||||
throw ClientError(c->errstr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,23 +3,29 @@
|
||||
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
#include "hiredis.h"
|
||||
|
||||
namespace hiredis {
|
||||
class ClientSettings {
|
||||
public:
|
||||
ClientSettings();
|
||||
void applyEnv();
|
||||
void setHost(const char *s);
|
||||
void setUnix(const char *s);
|
||||
void setSsl(bool v) { m_ssl_enabled = v; }
|
||||
|
||||
const char *ssl_cert() const { return m_ssl_cert_path; }
|
||||
const char *ssl_key() const { return m_ssl_key_path; }
|
||||
const char *ssl_ca() const { return m_ssl_ca_path; }
|
||||
|
||||
bool is_ssl() const {
|
||||
return m_ssl_ca_path != NULL;
|
||||
return m_ssl_enabled;
|
||||
}
|
||||
|
||||
bool is_unix() const {
|
||||
return false;
|
||||
return m_mode == REDIS_CONN_UNIX;
|
||||
}
|
||||
|
||||
const char *hostname() const {
|
||||
return m_hostname.c_str();
|
||||
}
|
||||
@ -32,17 +38,68 @@ public:
|
||||
|
||||
void initOptions(redisOptions& options) const;
|
||||
|
||||
private:
|
||||
std::string m_hostname;
|
||||
uint16_t m_port;
|
||||
std::string m_hostname = "localhost";
|
||||
uint16_t m_port = 6379;
|
||||
|
||||
int m_mode = REDIS_CONN_TCP;
|
||||
const char *m_ssl_cert_path = NULL;
|
||||
const char *m_ssl_ca_path = NULL;
|
||||
const char *m_ssl_key_path = NULL;
|
||||
bool m_ssl_enabled = false;
|
||||
};
|
||||
|
||||
extern ClientSettings settings_g;
|
||||
|
||||
class ClientError : public std::runtime_error {
|
||||
public:
|
||||
ClientError() : std::runtime_error("hiredis error") {
|
||||
}
|
||||
ClientError(const char *s) : std::runtime_error(s) {
|
||||
}
|
||||
static void throwCode(int code);
|
||||
static void throwContext(const redisContext *ac);
|
||||
};
|
||||
|
||||
class ConnectError : public ClientError {
|
||||
public:
|
||||
ConnectError() : ClientError(){}
|
||||
ConnectError(const redisOptions& options);
|
||||
virtual const char *what() const noexcept override{
|
||||
return endpoint.c_str();
|
||||
}
|
||||
private:
|
||||
std::string endpoint;
|
||||
};
|
||||
|
||||
class IOError : public ClientError {
|
||||
public:
|
||||
IOError() : ClientError(){}
|
||||
IOError(const char *s) : ClientError(s) {}
|
||||
};
|
||||
|
||||
class TimeoutError : public ClientError {
|
||||
public:
|
||||
TimeoutError() : ClientError("timed out") {}
|
||||
TimeoutError(const char *s) : ClientError(s) {}
|
||||
};
|
||||
|
||||
class SSLError : public ClientError {
|
||||
public:
|
||||
SSLError() : ClientError() {}
|
||||
SSLError(const char *what) : ClientError(what) {}
|
||||
};
|
||||
|
||||
class CommandError : public ClientError {
|
||||
public:
|
||||
CommandError(const redisReply *r) {
|
||||
errstr = r->str;
|
||||
}
|
||||
virtual const char *what() const noexcept override {
|
||||
return errstr.c_str();
|
||||
}
|
||||
private:
|
||||
std::string errstr;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
30
cpptests/example.crt
Normal file
30
cpptests/example.crt
Normal file
@ -0,0 +1,30 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFJjCCAw6gAwIBAgIJALM3AMcxMe0VMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV
|
||||
BAMTC2V4YW1wbGUuY29tMB4XDTE4MDUwNzExNTM1MloXDTI4MDUwNDExNTM1Mlow
|
||||
FjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
|
||||
ggIKAoICAQCyNzRDE8b+COhBYZNiFnMeuF6mXRnd/jaK0axpKhRNx8JG2xGkmzq9
|
||||
Fse74dBbH/mmFwgpnOmRL2QK+qStUws6PQ8fk9fw3ntTQvMccCJqO/Ns1Rd3oFCt
|
||||
kL5q3VMW3lYn/ZTZ9jaFfRRdOkfACkDUQ3ff2/8QVU+BFNL47GyVPiAQeN5efjMj
|
||||
XSpE3aX3pn468wElO3JMxrUtE/pOg/TvlX4oDOJ9n56RhdpFU5rnvcdzxIMUgBih
|
||||
9XRymPj/0aD9UCq5++6CQ0U0MP4tcCe6iMQsHOfReUPH81pyMVpox/MBUAHPXKqd
|
||||
EePWz+OeEJfT8CeDt41DF6kEBledM41IYirlB/mCoV8TDnQYVmLIGyTiDQUsoGx/
|
||||
iZuYm+6bszdtgVK0ofBYDNgrY4VnC85YXg9sr+2fpZrNqzPY3W1esHdmdfQHP/Ff
|
||||
Q1pDVz8jWhWKwEE8QSpw9tRzL2U6hDdAxdmxt6h35dZvJF30ZfLylX16KDNLcN2E
|
||||
qRm/UTjjs06rzPGEK6rRuOgJ2ixlHzK2mZFaAD22THn1+H4QXOkylqAbGXemryrF
|
||||
ESLtCSskOuacfz1bUoWXa0a9pI67vilvagO8/Yc73rqtYLrTnHEwmFDd2X33UryB
|
||||
NADSV/a4VCIoOvThAbhZIaRAPCi7nn+hzu62myoVg6KDYLGTk7v4gQIDAQABo3cw
|
||||
dTAdBgNVHQ4EFgQUOYtIaf/4JJK9IBtulHRfJSEa2G8wRgYDVR0jBD8wPYAUOYtI
|
||||
af/4JJK9IBtulHRfJSEa2G+hGqQYMBYxFDASBgNVBAMTC2V4YW1wbGUuY29tggkA
|
||||
szcAxzEx7RUwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAgXe3OTSB
|
||||
Xma+IU9MtZChAf4p6n+5magLRVeraImKeFFrk2euPcQ0FxzpwA/GlhzDGLLUIKcB
|
||||
NnPOhZ3TVZTbzPhVSeBkFgpM/D+RnYnd2TLuVbubRynqKPPIa01hnI5UPodeQnuR
|
||||
1p+kLXEcQvUj7eSipYOEIO78cy/1QKZq8Jb6qosgM1w140qthFEqrgg3ewgNHbB+
|
||||
SLOxUGIbVfi5pdWiekGwcR3nyaJdyq3Dvq65Rt6wfzfX21FZI7VGs5NPzA/TdRIX
|
||||
uwTNe0Rif+4mbAah5SZlY9J/rKY+s41EdGPY24+ZywlyLgGgeRF2LaNu5iqG2FiQ
|
||||
h/EvpFzZQttSFTNhfMwZZg0pbQ2kpG53P7Y61rtuTHI0ykTb+Ot/972DefP0jLUl
|
||||
+SCuUHSb6OTlSddZt4zvxVgSOftrSLMl6PQpQZLLtxWvmFpWHTTpHEoRAXLv41bo
|
||||
JEVNZNO+L0pVWCOWyqn/4A4umtXWRzU8qXxed7mVNbpLFj9EYFChdcJ7OO9gEW05
|
||||
RJZrzEqd0Moh0na9ol9qQmc52Z/27ex+hh6ZrF7vVjUzWUMwIgFKx7/ELL9BNBN3
|
||||
ir6hkQpH6lNA71MgkW6aUuo2exZwffpjo2/cydwcdieNyP1kHwakN6ClwCYcFZpd
|
||||
LPKxSkVpeDRSDNFyYYUOTY4BJVbwnxy5Kto=
|
||||
-----END CERTIFICATE-----
|
||||
51
cpptests/example.key
Normal file
51
cpptests/example.key
Normal file
@ -0,0 +1,51 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
||||
50
cpptests/main.cpp
Normal file
50
cpptests/main.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <tuple>
|
||||
#include <gtest/gtest.h>
|
||||
#include "common.h"
|
||||
|
||||
using namespace hiredis;
|
||||
|
||||
static std::string getKvValue(const std::string& s) {
|
||||
size_t n = s.find('=');
|
||||
if (n == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
return s.substr(n+1);
|
||||
}
|
||||
|
||||
static bool isArg(const char *arg, const char *s) {
|
||||
return strncasecmp(arg, s, strlen(s)) == 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
ClientSettings* settings = &settings_g;
|
||||
|
||||
#ifdef HIREDIS_TEST_SSL_CA
|
||||
printf("Setting SSL compile time defaults\n");
|
||||
settings->m_ssl_ca_path = HIREDIS_TEST_SSL_CA;
|
||||
settings->m_ssl_cert_path = HIREDIS_TEST_SSL_CERT;
|
||||
settings->m_ssl_key_path = HIREDIS_TEST_SSL_KEY;
|
||||
#endif
|
||||
|
||||
for (size_t ii = 1; ii < argc; ++ii) {
|
||||
const char *ss = argv[ii];
|
||||
if (isArg(ss, "--unix")) {
|
||||
settings->setUnix(getKvValue(ss).c_str());
|
||||
} else if (isArg(ss, "--host")) {
|
||||
settings->setHost(getKvValue(ss).c_str());
|
||||
printf("Set host to %s:%u\n", settings->m_hostname.c_str(), settings->m_port);
|
||||
} else if (isArg(ss, "--ssl")) {
|
||||
auto v = getKvValue(ss);
|
||||
if (v == "0" || v == "false") {
|
||||
settings->setSsl(false);
|
||||
} else {
|
||||
printf("enabling ssl for tests\n");
|
||||
settings->setSsl(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
3
cpptests/run_test.py
Normal file
3
cpptests/run_test.py
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env python
|
||||
import argparse
|
||||
import subprocess
|
||||
128
cpptests/stunnel.conf
Normal file
128
cpptests/stunnel.conf
Normal file
@ -0,0 +1,128 @@
|
||||
; Sample stunnel configuration file for Unix by Michal Trojnara 1998-2018
|
||||
; Some options used here may be inadequate for your particular configuration
|
||||
; This sample file does *not* represent stunnel.conf defaults
|
||||
; Please consult the manual for detailed description of available options
|
||||
|
||||
; **************************************************************************
|
||||
; * Global options *
|
||||
; **************************************************************************
|
||||
|
||||
; It is recommended to drop root privileges if stunnel is started by root
|
||||
;setuid = nobody
|
||||
;setgid = nogroup
|
||||
|
||||
; PID file is created inside the chroot jail (if enabled)
|
||||
;pid = /usr/local/var/run/stunnel.pid
|
||||
|
||||
; Debugging stuff (may be useful for troubleshooting)
|
||||
foreground = yes
|
||||
debug = debug
|
||||
;output = /usr/local/var/log/stunnel.log
|
||||
|
||||
; Enable FIPS 140-2 mode if needed for compliance
|
||||
;fips = yes
|
||||
|
||||
; The pkcs11 engine allows for authentication with cryptographic
|
||||
; keys isolated in a hardware or software token
|
||||
; MODULE_PATH specifies the path to the pkcs11 module shared library,
|
||||
; e.g. softhsm2.dll or opensc-pkcs11.so
|
||||
; Each section using this feature also needs the "engineId = pkcs11" option
|
||||
;engine = pkcs11
|
||||
;engineCtrl = MODULE_PATH:/usr/lib/softhsm/libsofthsm2.so
|
||||
;engineCtrl = PIN:1234
|
||||
|
||||
; **************************************************************************
|
||||
; * Service defaults may also be specified in individual service sections *
|
||||
; **************************************************************************
|
||||
|
||||
; Enable support for the insecure SSLv3 protocol
|
||||
delay = yes
|
||||
options = NO_SSLv3
|
||||
options = NO_TLSv1
|
||||
options = CIPHER_SERVER_PREFERENCE
|
||||
options = DONT_INSERT_EMPTY_FRAGMENTS
|
||||
|
||||
|
||||
; These options provide additional security at some performance degradation
|
||||
;options = SINGLE_ECDH_USE
|
||||
;options = SINGLE_DH_USE
|
||||
|
||||
; **************************************************************************
|
||||
; * Include all configuration file fragments from the specified folder *
|
||||
; **************************************************************************
|
||||
|
||||
;include = /usr/local/etc/stunnel/conf.d
|
||||
|
||||
; **************************************************************************
|
||||
; * Service definitions (remove all services for inetd mode) *
|
||||
; **************************************************************************
|
||||
|
||||
; ***************************************** Example TLS client mode services
|
||||
|
||||
; The following examples use /etc/ssl/certs, which is the common location
|
||||
; of a hashed directory containing trusted CA certificates. This is not
|
||||
; a hardcoded path of the stunnel package, as it is not related to the
|
||||
; stunnel configuration in /usr/local/etc/stunnel/.
|
||||
|
||||
[redis]
|
||||
;client = yes
|
||||
accept = 16379
|
||||
connect = 6379
|
||||
cert = example.crt
|
||||
key = example.key
|
||||
|
||||
; Encrypted HTTP proxy authenticated with a client certificate
|
||||
; located in a cryptographic token
|
||||
;[example-pkcs11]
|
||||
;client = yes
|
||||
;accept = 127.0.0.1:8080
|
||||
;connect = example.com:8443
|
||||
;engineId = pkcs11
|
||||
;cert = pkcs11:token=MyToken;object=MyCert
|
||||
;key = pkcs11:token=MyToken;object=MyKey
|
||||
|
||||
; ***************************************** Example TLS server mode services
|
||||
|
||||
;[pop3s]
|
||||
;accept = 995
|
||||
;connect = 110
|
||||
;cert = /usr/local/etc/stunnel/stunnel.pem
|
||||
|
||||
;[imaps]
|
||||
;accept = 993
|
||||
;connect = 143
|
||||
;cert = /usr/local/etc/stunnel/stunnel.pem
|
||||
|
||||
; Either only expose this service to trusted networks, or require
|
||||
; authentication when relaying emails originated from loopback.
|
||||
; Otherwise the following configuration creates an open relay.
|
||||
;[ssmtp]
|
||||
;accept = 465
|
||||
;connect = 25
|
||||
;cert = /usr/local/etc/stunnel/stunnel.pem
|
||||
|
||||
; TLS front-end to a web server
|
||||
;[https]
|
||||
;accept = 443
|
||||
;connect = 80
|
||||
;cert = /usr/local/etc/stunnel/stunnel.pem
|
||||
; "TIMEOUTclose = 0" is a workaround for a design flaw in Microsoft SChannel
|
||||
; Microsoft implementations do not use TLS close-notify alert and thus they
|
||||
; are vulnerable to truncation attacks
|
||||
;TIMEOUTclose = 0
|
||||
|
||||
; Remote shell protected with PSK-authenticated TLS
|
||||
; Create "/usr/local/etc/stunnel/secrets.txt" containing IDENTITY:KEY pairs
|
||||
;[shell]
|
||||
;accept = 1337
|
||||
;exec = /bin/sh
|
||||
;execArgs = sh -i
|
||||
;PSKsecrets = /usr/local/etc/stunnel/secrets.txt
|
||||
|
||||
; Non-standard MySQL-over-TLS encapsulation connecting the Unix socket
|
||||
;[mysql]
|
||||
;cert = /usr/local/etc/stunnel/stunnel.pem
|
||||
;accept = 3307
|
||||
;connect = /run/mysqld/mysqld.sock
|
||||
|
||||
; vim:ft=dosini
|
||||
@ -22,24 +22,43 @@ struct CmdData {
|
||||
};
|
||||
|
||||
struct AsyncClient {
|
||||
AsyncClient(const redisOptions& options, event_base* b) {
|
||||
void connectCommon(const redisOptions& orig, event_base *b, unsigned timeoutMs) {
|
||||
redisOptions options = orig;
|
||||
struct timeval tv = { 0 };
|
||||
if (timeoutMs) {
|
||||
tv.tv_usec = timeoutMs * 1000;
|
||||
options.timeout = &tv;
|
||||
}
|
||||
|
||||
ac = redisAsyncConnectWithOptions(&options);
|
||||
ac->data = this;
|
||||
redisLibeventAttach(ac, b);
|
||||
redisAsyncSetConnectCallback(ac, realConnectCb);
|
||||
ac->data = this;
|
||||
|
||||
}
|
||||
|
||||
AsyncClient(const ClientSettings& settings, event_base *b) {
|
||||
AsyncClient(const redisOptions& options, event_base* b, unsigned timeoutMs = 0) {
|
||||
connectCommon(options, b, timeoutMs);
|
||||
}
|
||||
|
||||
AsyncClient(const ClientSettings& settings, event_base *b, unsigned timeoutMs = 0) {
|
||||
redisOptions options = { 0 };
|
||||
ac = redisAsyncConnectWithOptions(&options);
|
||||
redisLibeventAttach(ac, b);
|
||||
redisAsyncSetConnectCallback(ac, realConnectCb);
|
||||
settings.initOptions(options);
|
||||
connectCommon(options, b, timeoutMs);
|
||||
|
||||
if (ac->c.err != REDIS_OK) {
|
||||
ClientError::throwContext(&ac->c);
|
||||
}
|
||||
|
||||
if (settings.is_ssl()) {
|
||||
redisSecureConnection(&ac->c,
|
||||
printf("Securing async connection...\n");
|
||||
int rc = redisSecureConnection(&ac->c,
|
||||
settings.ssl_ca(), settings.ssl_cert(), settings.ssl_key(),
|
||||
NULL);
|
||||
if (rc != REDIS_OK) {
|
||||
throw SSLError(ac->c.errstr);
|
||||
}
|
||||
}
|
||||
ac->data = this;
|
||||
}
|
||||
|
||||
AsyncClient(redisAsyncContext *ac) : ac(ac) {
|
||||
@ -68,11 +87,15 @@ struct AsyncClient {
|
||||
|
||||
void disconnect(DisconnectCallback cb) {
|
||||
disconncb = cb;
|
||||
redisAsyncDisconnect(ac);
|
||||
auto tmpac = ac;
|
||||
ac = NULL;
|
||||
redisAsyncDisconnect(tmpac);
|
||||
}
|
||||
|
||||
void disconnect() {
|
||||
redisAsyncDisconnect(ac);
|
||||
auto tmpac = ac;
|
||||
ac = NULL;
|
||||
redisAsyncDisconnect(tmpac);
|
||||
}
|
||||
|
||||
ConnectionCallback conncb;
|
||||
@ -80,7 +103,6 @@ struct AsyncClient {
|
||||
redisAsyncContext *ac;
|
||||
};
|
||||
|
||||
|
||||
static void realConnectCb(const redisAsyncContext *ac, int status) {
|
||||
auto self = reinterpret_cast<AsyncClient*>(ac->data);
|
||||
if (self->conncb) {
|
||||
@ -119,21 +141,24 @@ protected:
|
||||
};
|
||||
|
||||
TEST_F(AsyncTest, testAsync) {
|
||||
redisOptions options = {0};
|
||||
struct timeval tv = {0};
|
||||
tv.tv_sec = 1;
|
||||
options.timeout = &tv;
|
||||
settings_g.initOptions(options);
|
||||
AsyncClient client(options, libevent);
|
||||
AsyncClient client(settings_g, libevent, 1000);
|
||||
|
||||
client.onConnect([](AsyncClient*, bool status) {
|
||||
printf("Status: %d\n", status);
|
||||
bool gotConnect = false;
|
||||
bool gotCommand = false;
|
||||
client.onConnect([&](AsyncClient*, bool status) {
|
||||
ASSERT_TRUE(status);
|
||||
gotConnect = true;
|
||||
});
|
||||
|
||||
client.cmd([](AsyncClient *c, redisReply*){
|
||||
printf("Got reply!\n");
|
||||
client.cmd([&](AsyncClient *c, redisReply *r) {
|
||||
ASSERT_TRUE(r != NULL);
|
||||
ASSERT_EQ(REDIS_REPLY_STATUS, r->type);
|
||||
ASSERT_STREQ("PONG", r->str);
|
||||
c->disconnect();
|
||||
gotCommand = true;
|
||||
}, "PING");
|
||||
|
||||
wait();
|
||||
|
||||
ASSERT_TRUE(gotConnect);
|
||||
ASSERT_TRUE(gotCommand);
|
||||
}
|
||||
|
||||
@ -7,63 +7,6 @@
|
||||
|
||||
using namespace hiredis;
|
||||
|
||||
class ClientError : public std::runtime_error {
|
||||
public:
|
||||
ClientError() : std::runtime_error("hiredis error") {
|
||||
}
|
||||
ClientError(const char *s) : std::runtime_error(s) {
|
||||
}
|
||||
};
|
||||
|
||||
class ConnectError : public ClientError {
|
||||
public:
|
||||
ConnectError() : ClientError(){}
|
||||
ConnectError(const redisOptions& options) {
|
||||
if (options.type == REDIS_CONN_TCP) {
|
||||
endpoint = options.endpoint.tcp.ip;
|
||||
endpoint += ":";
|
||||
endpoint += options.endpoint.tcp.port;
|
||||
} else if (options.type == REDIS_CONN_UNIX) {
|
||||
endpoint = "unix://";
|
||||
endpoint += options.endpoint.unix_socket;
|
||||
}
|
||||
}
|
||||
virtual const char *what() const noexcept override{
|
||||
return endpoint.c_str();
|
||||
}
|
||||
private:
|
||||
std::string endpoint;
|
||||
};
|
||||
|
||||
class IOError : public ClientError {};
|
||||
class TimeoutError : public ClientError {};
|
||||
class SSLError : public ClientError {};
|
||||
|
||||
class CommandError : public ClientError {
|
||||
public:
|
||||
CommandError(const redisReply *r) {
|
||||
errstr = r->str;
|
||||
}
|
||||
virtual const char *what() const noexcept override {
|
||||
return errstr.c_str();
|
||||
}
|
||||
private:
|
||||
std::string errstr;
|
||||
};
|
||||
|
||||
void errorFromCode(int code) {
|
||||
switch (code) {
|
||||
case REDIS_ERR_IO:
|
||||
case REDIS_ERR_EOF:
|
||||
case REDIS_ERR_PROTOCOL:
|
||||
throw IOError();
|
||||
case REDIS_ERR_TIMEOUT:
|
||||
throw TimeoutError();
|
||||
default:
|
||||
throw ClientError();
|
||||
}
|
||||
}
|
||||
|
||||
class Client {
|
||||
public:
|
||||
operator redisContext*() {
|
||||
@ -107,7 +50,7 @@ public:
|
||||
void flushdb() {
|
||||
redisReply *p = cmd("FLUSHDB");
|
||||
if (p == NULL) {
|
||||
errorFromCode(ctx->err);
|
||||
ClientError::throwCode(ctx->err);
|
||||
}
|
||||
if (p->type == REDIS_REPLY_ERROR) {
|
||||
auto pp = CommandError(p);
|
||||
@ -136,7 +79,7 @@ private:
|
||||
int err = ctx->err;
|
||||
redisFree(ctx);
|
||||
ctx = NULL;
|
||||
errorFromCode(err);
|
||||
ClientError::throwCode(err);
|
||||
}
|
||||
void connectOrThrow(const redisOptions& options) {
|
||||
ctx = redisConnectWithOptions(&options);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user