Commit 64da57e0 authored by Mark Nunberg's avatar Mark Nunberg
Browse files

more test fixes:

- include "main" test file which can be configured through various
  options
- include sample ssl test files
- add boilerplate async test
parent 91717bd1
...@@ -4,7 +4,14 @@ INCLUDE_DIRECTORIES(PROJECT_SOURCE_DIR) ...@@ -4,7 +4,14 @@ INCLUDE_DIRECTORIES(PROJECT_SOURCE_DIR)
INCLUDE_DIRECTORIES(${LIBEVENT_INCLUDES}) INCLUDE_DIRECTORIES(${LIBEVENT_INCLUDES})
ADD_EXECUTABLE(hiredis-gtest t_basic.cpp t_client.cpp t_async.cpp ADD_EXECUTABLE(hiredis-gtest t_basic.cpp t_client.cpp t_async.cpp
common.cpp) common.cpp main.cpp)
TARGET_LINK_LIBRARIES(hiredis-gtest gtest_main hiredis event) TARGET_LINK_LIBRARIES(hiredis-gtest gtest hiredis event)
ADD_TEST(NAME hiredis-test COMMAND hiredis-test) 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)
\ No newline at end of file
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}")
\ No newline at end of file
#include "common.h" #include "common.h"
#include <sstream>
hiredis::ClientSettings hiredis::settings_g; hiredis::ClientSettings hiredis::settings_g;
using namespace hiredis; 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;
}
void ClientSettings::setUnix(const char *s) {
m_mode = REDIS_CONN_UNIX;
m_hostname = s;
}
ClientSettings::ClientSettings() { void ClientSettings::applyEnv() {
std::string hostval; std::string hostval;
if (getenv("REDIS_SOCKET")) { if (getenv("REDIS_SOCKET")) {
m_hostname.assign(getenv("REDIS_SOCKET")); m_hostname.assign(getenv("REDIS_SOCKET"));
...@@ -14,24 +36,20 @@ ClientSettings::ClientSettings() { ...@@ -14,24 +36,20 @@ ClientSettings::ClientSettings() {
} }
if (getenv("REDIS_HOST")) { if (getenv("REDIS_HOST")) {
hostval.assign(getenv("REDIS_HOST")); setHost(getenv("REDIS_HOST"));
} }
size_t idx = hostval.find(':');
if (idx == std::string::npos) { // Handle SSL settings as well
// First part is hostname only if (getenv("REDIS_SSL_CLIENT_CERT")) {
m_hostname = hostval; m_ssl_cert_path = getenv("REDIS_SSL_CLIENT_CERT");
} else {
m_port = atoi(hostval.c_str() + idx);
hostval.resize(idx);
} }
if (!m_port) { if (getenv("REDIS_SSL_CLIENT_KEY")) {
m_port = 6379; m_ssl_key_path = getenv("REDIS_SSL_CLIENT_KEY");
}
if (getenv("REDIS_SSL_CA")) {
m_ssl_ca_path = getenv("REDIS_SSL_CA");
} }
// 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");
} }
void ClientSettings::initOptions(redisOptions& options) const { void ClientSettings::initOptions(redisOptions& options) const {
...@@ -41,3 +59,48 @@ void ClientSettings::initOptions(redisOptions& options) const { ...@@ -41,3 +59,48 @@ void ClientSettings::initOptions(redisOptions& options) const {
REDIS_OPTIONS_SET_UNIX(&options, hostname()); 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 @@ ...@@ -3,23 +3,29 @@
#include <string> #include <string>
#include <cstdlib> #include <cstdlib>
#include <stdexcept>
#include "hiredis.h" #include "hiredis.h"
namespace hiredis { namespace hiredis {
class ClientSettings { class ClientSettings {
public: 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_cert() const { return m_ssl_cert_path; }
const char *ssl_key() const { return m_ssl_key_path; } const char *ssl_key() const { return m_ssl_key_path; }
const char *ssl_ca() const { return m_ssl_ca_path; } const char *ssl_ca() const { return m_ssl_ca_path; }
bool is_ssl() const { bool is_ssl() const {
return m_ssl_ca_path != NULL; return m_ssl_enabled;
} }
bool is_unix() const { bool is_unix() const {
return false; return m_mode == REDIS_CONN_UNIX;
} }
const char *hostname() const { const char *hostname() const {
return m_hostname.c_str(); return m_hostname.c_str();
} }
...@@ -32,17 +38,68 @@ public: ...@@ -32,17 +38,68 @@ public:
void initOptions(redisOptions& options) const; void initOptions(redisOptions& options) const;
private: std::string m_hostname = "localhost";
std::string m_hostname; uint16_t m_port = 6379;
uint16_t m_port;
int m_mode = REDIS_CONN_TCP; int m_mode = REDIS_CONN_TCP;
const char *m_ssl_cert_path = NULL; const char *m_ssl_cert_path = NULL;
const char *m_ssl_ca_path = NULL; const char *m_ssl_ca_path = NULL;
const char *m_ssl_key_path = NULL; const char *m_ssl_key_path = NULL;
bool m_ssl_enabled = false;
}; };
extern ClientSettings settings_g; 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 #endif
-----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-----
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----
#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();
}
#!/usr/bin/env python
import argparse
import subprocess
\ No newline at end of file
; 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 { ...@@ -22,24 +22,43 @@ struct CmdData {
}; };
struct AsyncClient { 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 = redisAsyncConnectWithOptions(&options);
ac->data = this;
redisLibeventAttach(ac, b); redisLibeventAttach(ac, b);
redisAsyncSetConnectCallback(ac, realConnectCb); 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 }; redisOptions options = { 0 };
ac = redisAsyncConnectWithOptions(&options); settings.initOptions(options);
redisLibeventAttach(ac, b); connectCommon(options, b, timeoutMs);
redisAsyncSetConnectCallback(ac, realConnectCb);
if (ac->c.err != REDIS_OK) {
ClientError::throwContext(&ac->c);
}
if (settings.is_ssl()) { 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(), settings.ssl_ca(), settings.ssl_cert(), settings.ssl_key(),
NULL); NULL);
if (rc != REDIS_OK) {
throw SSLError(ac->c.errstr);
}
} }
ac->data = this;
} }
AsyncClient(redisAsyncContext *ac) : ac(ac) { AsyncClient(redisAsyncContext *ac) : ac(ac) {
...@@ -68,11 +87,15 @@ struct AsyncClient { ...@@ -68,11 +87,15 @@ struct AsyncClient {
void disconnect(DisconnectCallback cb) { void disconnect(DisconnectCallback cb) {
disconncb = cb; disconncb = cb;
redisAsyncDisconnect(ac); auto tmpac = ac;
ac = NULL;
redisAsyncDisconnect(tmpac);
} }
void disconnect() { void disconnect() {
redisAsyncDisconnect(ac); auto tmpac = ac;
ac = NULL;
redisAsyncDisconnect(tmpac);
} }
ConnectionCallback conncb; ConnectionCallback conncb;
...@@ -80,7 +103,6 @@ struct AsyncClient { ...@@ -80,7 +103,6 @@ struct AsyncClient {
redisAsyncContext *ac; redisAsyncContext *ac;
}; };
static void realConnectCb(const redisAsyncContext *ac, int status) { static void realConnectCb(const redisAsyncContext *ac, int status) {
auto self = reinterpret_cast<AsyncClient*>(ac->data); auto self = reinterpret_cast<AsyncClient*>(ac->data);
if (self->conncb) { if (self->conncb) {
...@@ -119,21 +141,24 @@ protected: ...@@ -119,21 +141,24 @@ protected:
}; };
TEST_F(AsyncTest, testAsync) { TEST_F(AsyncTest, testAsync) {
redisOptions options = {0}; AsyncClient client(settings_g, libevent, 1000);
struct timeval tv = {0};
tv.tv_sec = 1; bool gotConnect = false;
options.timeout = &tv; bool gotCommand = false;
settings_g.initOptions(options); client.onConnect([&](AsyncClient*, bool status) {
AsyncClient client(options, libevent); ASSERT_TRUE(status);
gotConnect = true;
client.onConnect([](AsyncClient*, bool status) {
printf("Status: %d\n", status);
}); });
client.cmd([](AsyncClient *c, redisReply*){ client.cmd([&](AsyncClient *c, redisReply *r) {
printf("Got reply!\n"); ASSERT_TRUE(r != NULL);
ASSERT_EQ(REDIS_REPLY_STATUS, r->type);
ASSERT_STREQ("PONG", r->str);
c->disconnect(); c->disconnect();
gotCommand = true;
}, "PING"); }, "PING");
wait(); wait();
ASSERT_TRUE(gotConnect);
ASSERT_TRUE(gotCommand);
} }
...@@ -7,63 +7,6 @@ ...@@ -7,63 +7,6 @@
using namespace hiredis; 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 { class Client {
public: public:
operator redisContext*() { operator redisContext*() {
...@@ -107,7 +50,7 @@ public: ...@@ -107,7 +50,7 @@ public:
void flushdb() { void flushdb() {
redisReply *p = cmd("FLUSHDB"); redisReply *p = cmd("FLUSHDB");
if (p == NULL) { if (p == NULL) {
errorFromCode(ctx->err); ClientError::throwCode(ctx->err);
} }
if (p->type == REDIS_REPLY_ERROR) { if (p->type == REDIS_REPLY_ERROR) {
auto pp = CommandError(p); auto pp = CommandError(p);
...@@ -136,7 +79,7 @@ private: ...@@ -136,7 +79,7 @@ private:
int err = ctx->err; int err = ctx->err;
redisFree(ctx); redisFree(ctx);
ctx = NULL; ctx = NULL;
errorFromCode(err); ClientError::throwCode(err);
} }
void connectOrThrow(const redisOptions& options) { void connectOrThrow(const redisOptions& options) {
ctx = redisConnectWithOptions(&options); ctx = redisConnectWithOptions(&options);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment