Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
3f073b1d
Unverified
Commit
3f073b1d
authored
Aug 09, 2020
by
Yossi Gottlieb
Committed by
GitHub
Aug 09, 2020
Browse files
Merge pull request #7609 from michael-grunder/redis-cli-resp3-push
Add redis-cli RESP3 Push support
parents
3f494cc4
81879bc1
Changes
47
Hide whitespace changes
Inline
Side-by-side
deps/hiredis/sdsalloc.h
View file @
3f073b1d
...
@@ -37,6 +37,8 @@
...
@@ -37,6 +37,8 @@
* the include of your alternate allocator if needed (not needed in order
* the include of your alternate allocator if needed (not needed in order
* to use the default libc allocator). */
* to use the default libc allocator). */
#define s_malloc malloc
#include "alloc.h"
#define s_realloc realloc
#define s_free free
#define s_malloc hi_malloc
#define s_realloc hi_realloc
#define s_free hi_free
deps/hiredis/sockcompat.c
View file @
3f073b1d
...
@@ -212,7 +212,7 @@ int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, sockle
...
@@ -212,7 +212,7 @@ int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, sockle
int
win32_setsockopt
(
SOCKET
sockfd
,
int
level
,
int
optname
,
const
void
*
optval
,
socklen_t
optlen
)
{
int
win32_setsockopt
(
SOCKET
sockfd
,
int
level
,
int
optname
,
const
void
*
optval
,
socklen_t
optlen
)
{
int
ret
=
0
;
int
ret
=
0
;
if
((
level
==
SOL_SOCKET
)
&&
((
optname
==
SO_RCVTIMEO
)
||
(
optname
==
SO_SNDTIMEO
)))
{
if
((
level
==
SOL_SOCKET
)
&&
((
optname
==
SO_RCVTIMEO
)
||
(
optname
==
SO_SNDTIMEO
)))
{
struct
timeval
*
tv
=
optval
;
const
struct
timeval
*
tv
=
optval
;
DWORD
timeout
=
tv
->
tv_sec
*
1000
+
tv
->
tv_usec
/
1000
;
DWORD
timeout
=
tv
->
tv_sec
*
1000
+
tv
->
tv_usec
/
1000
;
ret
=
setsockopt
(
sockfd
,
level
,
optname
,
(
const
char
*
)
&
timeout
,
sizeof
(
DWORD
));
ret
=
setsockopt
(
sockfd
,
level
,
optname
,
(
const
char
*
)
&
timeout
,
sizeof
(
DWORD
));
}
else
{
}
else
{
...
...
deps/hiredis/sockcompat.h
View file @
3f073b1d
...
@@ -49,9 +49,10 @@
...
@@ -49,9 +49,10 @@
#include <winsock2.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <ws2tcpip.h>
#include <stddef.h>
#include <stddef.h>
#include <errno.h>
#ifdef _MSC_VER
#ifdef _MSC_VER
typedef
signed
long
ssize_t
;
typedef
long
long
ssize_t
;
#endif
#endif
/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */
/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */
...
...
deps/hiredis/ssl.c
View file @
3f073b1d
...
@@ -34,25 +34,33 @@
...
@@ -34,25 +34,33 @@
#include "async.h"
#include "async.h"
#include <assert.h>
#include <assert.h>
#include <pthread.h>
#include <errno.h>
#include <errno.h>
#include <string.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif
#include <openssl/ssl.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/err.h>
#include "win32.h"
#include "async_private.h"
#include "async_private.h"
#include "hiredis_ssl.h"
void
__redisSetError
(
redisContext
*
c
,
int
type
,
const
char
*
str
);
void
__redisSetError
(
redisContext
*
c
,
int
type
,
const
char
*
str
);
/* The SSL context is attached to SSL/TLS connections as a privdata. */
struct
redisSSLContext
{
typedef
struct
redisSSLContext
{
/* Associated OpenSSL SSL_CTX as created by redisCreateSSLContext() */
/**
* OpenSSL SSL_CTX; It is optional and will not be set when using
* user-supplied SSL.
*/
SSL_CTX
*
ssl_ctx
;
SSL_CTX
*
ssl_ctx
;
/* Requested SNI, or NULL */
char
*
server_name
;
};
/* The SSL connection context is attached to SSL/TLS connections as a privdata. */
typedef
struct
redisSSL
{
/**
/**
* OpenSSL SSL object.
* OpenSSL SSL object.
*/
*/
...
@@ -72,43 +80,11 @@ typedef struct redisSSLContext {
...
@@ -72,43 +80,11 @@ typedef struct redisSSLContext {
* should resume whenever a read takes place, if possible
* should resume whenever a read takes place, if possible
*/
*/
int
pendingWrite
;
int
pendingWrite
;
}
redisSSL
Context
;
}
redisSSL
;
/* Forward declaration */
/* Forward declaration */
redisContextFuncs
redisContextSSLFuncs
;
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.
* OpenSSL global initialization and locking handling callbacks.
* Note that this is only required for OpenSSL < 1.1.0.
* Note that this is only required for OpenSSL < 1.1.0.
...
@@ -119,6 +95,18 @@ static void sslLogCallback(const SSL *ssl, int where, int ret) {
...
@@ -119,6 +95,18 @@ static void sslLogCallback(const SSL *ssl, int where, int ret) {
#endif
#endif
#ifdef HIREDIS_USE_CRYPTO_LOCKS
#ifdef HIREDIS_USE_CRYPTO_LOCKS
#ifdef _WIN32
typedef
CRITICAL_SECTION
sslLockType
;
static
void
sslLockInit
(
sslLockType
*
l
)
{
InitializeCriticalSection
(
l
);
}
static
void
sslLockAcquire
(
sslLockType
*
l
)
{
EnterCriticalSection
(
l
);
}
static
void
sslLockRelease
(
sslLockType
*
l
)
{
LeaveCriticalSection
(
l
);
}
#else
typedef
pthread_mutex_t
sslLockType
;
typedef
pthread_mutex_t
sslLockType
;
static
void
sslLockInit
(
sslLockType
*
l
)
{
static
void
sslLockInit
(
sslLockType
*
l
)
{
pthread_mutex_init
(
l
,
NULL
);
pthread_mutex_init
(
l
,
NULL
);
...
@@ -129,7 +117,9 @@ static void sslLockAcquire(sslLockType *l) {
...
@@ -129,7 +117,9 @@ static void sslLockAcquire(sslLockType *l) {
static
void
sslLockRelease
(
sslLockType
*
l
)
{
static
void
sslLockRelease
(
sslLockType
*
l
)
{
pthread_mutex_unlock
(
l
);
pthread_mutex_unlock
(
l
);
}
}
static
pthread_mutex_t
*
ossl_locks
;
#endif
static
sslLockType
*
ossl_locks
;
static
void
opensslDoLock
(
int
mode
,
int
lkid
,
const
char
*
f
,
int
line
)
{
static
void
opensslDoLock
(
int
mode
,
int
lkid
,
const
char
*
f
,
int
line
)
{
sslLockType
*
l
=
ossl_locks
+
lkid
;
sslLockType
*
l
=
ossl_locks
+
lkid
;
...
@@ -144,36 +134,151 @@ static void opensslDoLock(int mode, int lkid, const char *f, int line) {
...
@@ -144,36 +134,151 @@ static void opensslDoLock(int mode, int lkid, const char *f, int line) {
(
void
)
line
;
(
void
)
line
;
}
}
static
void
initOpensslLocks
(
void
)
{
static
int
initOpensslLocks
(
void
)
{
unsigned
ii
,
nlocks
;
unsigned
ii
,
nlocks
;
if
(
CRYPTO_get_locking_callback
()
!=
NULL
)
{
if
(
CRYPTO_get_locking_callback
()
!=
NULL
)
{
/* Someone already set the callback before us. Don't destroy it! */
/* Someone already set the callback before us. Don't destroy it! */
return
;
return
REDIS_OK
;
}
}
nlocks
=
CRYPTO_num_locks
();
nlocks
=
CRYPTO_num_locks
();
ossl_locks
=
malloc
(
sizeof
(
*
ossl_locks
)
*
nlocks
);
ossl_locks
=
hi_malloc
(
sizeof
(
*
ossl_locks
)
*
nlocks
);
if
(
ossl_locks
==
NULL
)
return
REDIS_ERR
;
for
(
ii
=
0
;
ii
<
nlocks
;
ii
++
)
{
for
(
ii
=
0
;
ii
<
nlocks
;
ii
++
)
{
sslLockInit
(
ossl_locks
+
ii
);
sslLockInit
(
ossl_locks
+
ii
);
}
}
CRYPTO_set_locking_callback
(
opensslDoLock
);
CRYPTO_set_locking_callback
(
opensslDoLock
);
return
REDIS_OK
;
}
}
#endif
/* HIREDIS_USE_CRYPTO_LOCKS */
#endif
/* HIREDIS_USE_CRYPTO_LOCKS */
int
redisInitOpenSSL
(
void
)
{
SSL_library_init
();
#ifdef HIREDIS_USE_CRYPTO_LOCKS
initOpensslLocks
();
#endif
return
REDIS_OK
;
}
/**
* redisSSLContext helper context destruction.
*/
const
char
*
redisSSLContextGetError
(
redisSSLContextError
error
)
{
switch
(
error
)
{
case
REDIS_SSL_CTX_NONE
:
return
"No Error"
;
case
REDIS_SSL_CTX_CREATE_FAILED
:
return
"Failed to create OpenSSL SSL_CTX"
;
case
REDIS_SSL_CTX_CERT_KEY_REQUIRED
:
return
"Client cert and key must both be specified or skipped"
;
case
REDIS_SSL_CTX_CA_CERT_LOAD_FAILED
:
return
"Failed to load CA Certificate or CA Path"
;
case
REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED
:
return
"Failed to load client certificate"
;
case
REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED
:
return
"Failed to load private key"
;
default:
return
"Unknown error code"
;
}
}
void
redisFreeSSLContext
(
redisSSLContext
*
ctx
)
{
if
(
!
ctx
)
return
;
if
(
ctx
->
server_name
)
{
hi_free
(
ctx
->
server_name
);
ctx
->
server_name
=
NULL
;
}
if
(
ctx
->
ssl_ctx
)
{
SSL_CTX_free
(
ctx
->
ssl_ctx
);
ctx
->
ssl_ctx
=
NULL
;
}
hi_free
(
ctx
);
}
/**
* redisSSLContext helper context initialization.
*/
redisSSLContext
*
redisCreateSSLContext
(
const
char
*
cacert_filename
,
const
char
*
capath
,
const
char
*
cert_filename
,
const
char
*
private_key_filename
,
const
char
*
server_name
,
redisSSLContextError
*
error
)
{
redisSSLContext
*
ctx
=
hi_calloc
(
1
,
sizeof
(
redisSSLContext
));
if
(
ctx
==
NULL
)
goto
error
;
ctx
->
ssl_ctx
=
SSL_CTX_new
(
SSLv23_client_method
());
if
(
!
ctx
->
ssl_ctx
)
{
if
(
error
)
*
error
=
REDIS_SSL_CTX_CREATE_FAILED
;
goto
error
;
}
SSL_CTX_set_options
(
ctx
->
ssl_ctx
,
SSL_OP_NO_SSLv2
|
SSL_OP_NO_SSLv3
);
SSL_CTX_set_verify
(
ctx
->
ssl_ctx
,
SSL_VERIFY_PEER
,
NULL
);
if
((
cert_filename
!=
NULL
&&
private_key_filename
==
NULL
)
||
(
private_key_filename
!=
NULL
&&
cert_filename
==
NULL
))
{
if
(
error
)
*
error
=
REDIS_SSL_CTX_CERT_KEY_REQUIRED
;
goto
error
;
}
if
(
capath
||
cacert_filename
)
{
if
(
!
SSL_CTX_load_verify_locations
(
ctx
->
ssl_ctx
,
cacert_filename
,
capath
))
{
if
(
error
)
*
error
=
REDIS_SSL_CTX_CA_CERT_LOAD_FAILED
;
goto
error
;
}
}
if
(
cert_filename
)
{
if
(
!
SSL_CTX_use_certificate_chain_file
(
ctx
->
ssl_ctx
,
cert_filename
))
{
if
(
error
)
*
error
=
REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED
;
goto
error
;
}
if
(
!
SSL_CTX_use_PrivateKey_file
(
ctx
->
ssl_ctx
,
private_key_filename
,
SSL_FILETYPE_PEM
))
{
if
(
error
)
*
error
=
REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED
;
goto
error
;
}
}
if
(
server_name
)
ctx
->
server_name
=
hi_strdup
(
server_name
);
return
ctx
;
error:
redisFreeSSLContext
(
ctx
);
return
NULL
;
}
/**
/**
* SSL Connection initialization.
* SSL Connection initialization.
*/
*/
static
int
redisSSLConnect
(
redisContext
*
c
,
SSL_CTX
*
ssl_ctx
,
SSL
*
ssl
)
{
if
(
c
->
privdata
)
{
static
int
redisSSLConnect
(
redisContext
*
c
,
SSL
*
ssl
)
{
if
(
c
->
privctx
)
{
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"redisContext was already associated"
);
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"redisContext was already associated"
);
return
REDIS_ERR
;
return
REDIS_ERR
;
}
}
c
->
privdata
=
calloc
(
1
,
sizeof
(
redisSSLContext
));
c
->
funcs
=
&
redisContextSSLFuncs
;
redisSSL
*
rssl
=
hi_calloc
(
1
,
sizeof
(
redisSSL
));
redisSSLContext
*
rssl
=
c
->
privdata
;
if
(
rssl
==
NULL
)
{
__redisSetError
(
c
,
REDIS_ERR_OOM
,
"Out of memory"
);
return
REDIS_ERR
;
}
rssl
->
ssl_ctx
=
ssl_ctx
;
c
->
funcs
=
&
redisContextSSLFuncs
;
rssl
->
ssl
=
ssl
;
rssl
->
ssl
=
ssl
;
SSL_set_mode
(
rssl
->
ssl
,
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
);
SSL_set_mode
(
rssl
->
ssl
,
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
);
...
@@ -183,12 +288,14 @@ static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
...
@@ -183,12 +288,14 @@ static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
ERR_clear_error
();
ERR_clear_error
();
int
rv
=
SSL_connect
(
rssl
->
ssl
);
int
rv
=
SSL_connect
(
rssl
->
ssl
);
if
(
rv
==
1
)
{
if
(
rv
==
1
)
{
c
->
privctx
=
rssl
;
return
REDIS_OK
;
return
REDIS_OK
;
}
}
rv
=
SSL_get_error
(
rssl
->
ssl
,
rv
);
rv
=
SSL_get_error
(
rssl
->
ssl
,
rv
);
if
(((
c
->
flags
&
REDIS_BLOCK
)
==
0
)
&&
if
(((
c
->
flags
&
REDIS_BLOCK
)
==
0
)
&&
(
rv
==
SSL_ERROR_WANT_READ
||
rv
==
SSL_ERROR_WANT_WRITE
))
{
(
rv
==
SSL_ERROR_WANT_READ
||
rv
==
SSL_ERROR_WANT_WRITE
))
{
c
->
privctx
=
rssl
;
return
REDIS_OK
;
return
REDIS_OK
;
}
}
...
@@ -203,83 +310,58 @@ static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
...
@@ -203,83 +310,58 @@ static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
}
}
__redisSetError
(
c
,
REDIS_ERR_IO
,
err
);
__redisSetError
(
c
,
REDIS_ERR_IO
,
err
);
}
}
hi_free
(
rssl
);
return
REDIS_ERR
;
return
REDIS_ERR
;
}
}
/**
* A wrapper around redisSSLConnect() for users who manage their own context and
* create their own SSL object.
*/
int
redisInitiateSSL
(
redisContext
*
c
,
SSL
*
ssl
)
{
int
redisInitiateSSL
(
redisContext
*
c
,
SSL
*
ssl
)
{
return
redisSSLConnect
(
c
,
NULL
,
ssl
);
return
redisSSLConnect
(
c
,
ssl
);
}
}
int
redisSecureConnection
(
redisContext
*
c
,
const
char
*
capath
,
/**
const
char
*
certpath
,
const
char
*
keypath
,
const
char
*
servername
)
{
* A wrapper around redisSSLConnect() for users who use redisSSLContext and don't
* manage their own SSL objects.
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
int
redisInitiateSSLWithContext
(
redisContext
*
c
,
redisSSLContext
*
redis_ssl_ctx
)
SSL_CTX_set_info_callback
(
ssl_ctx
,
sslLogCallback
);
{
#endif
if
(
!
c
||
!
redis_ssl_ctx
)
SSL_CTX_set_options
(
ssl_ctx
,
SSL_OP_NO_SSLv2
|
SSL_OP_NO_SSLv3
);
return
REDIS_ERR
;
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
)
{
/* We want to verify that redisSSLConnect() won't fail on this, as it will
if
(
!
SSL_CTX_load_verify_locations
(
ssl_ctx
,
capath
,
NULL
))
{
* not own the SSL object in that case and we'll end up leaking.
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"Invalid CA certificate"
);
*/
goto
error
;
if
(
c
->
privctx
)
}
return
REDIS_ERR
;
}
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
);
SSL
*
ssl
=
SSL_new
(
redis_ssl_ctx
->
ssl_ctx
);
if
(
!
ssl
)
{
if
(
!
ssl
)
{
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"Couldn't create new SSL instance"
);
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"Couldn't create new SSL instance"
);
goto
error
;
goto
error
;
}
}
if
(
servername
)
{
if
(
!
SSL_set_tlsext_host_name
(
ssl
,
servername
))
{
if
(
redis_ssl_ctx
->
server_name
)
{
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"Couldn't set server name indication"
);
if
(
!
SSL_set_tlsext_host_name
(
ssl
,
redis_ssl_ctx
->
server_name
))
{
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"Failed to set server_name/SNI"
);
goto
error
;
goto
error
;
}
}
}
}
return
redisSSLConnect
(
c
,
ssl_ctx
,
ssl
);
return
redisSSLConnect
(
c
,
ssl
);
error:
error:
if
(
ssl
)
SSL_free
(
ssl
);
if
(
ssl
)
if
(
ssl_ctx
)
SSL_
CTX_
free
(
ssl
_ctx
);
SSL_free
(
ssl
);
return
REDIS_ERR
;
return
REDIS_ERR
;
}
}
static
int
maybeCheckWant
(
redisSSL
Context
*
rssl
,
int
rv
)
{
static
int
maybeCheckWant
(
redisSSL
*
rssl
,
int
rv
)
{
/**
/**
* If the error is WANT_READ or WANT_WRITE, the appropriate flags are set
* If the error is WANT_READ or WANT_WRITE, the appropriate flags are set
* and true is returned. False is returned otherwise
* and true is returned. False is returned otherwise
...
@@ -299,23 +381,19 @@ static int maybeCheckWant(redisSSLContext *rssl, int rv) {
...
@@ -299,23 +381,19 @@ static int maybeCheckWant(redisSSLContext *rssl, int rv) {
* Implementation of redisContextFuncs for SSL connections.
* Implementation of redisContextFuncs for SSL connections.
*/
*/
static
void
redisSSLFree
Context
(
void
*
priv
data
){
static
void
redisSSLFree
(
void
*
priv
ctx
){
redisSSL
Context
*
rsc
=
priv
data
;
redisSSL
*
rsc
=
priv
ctx
;
if
(
!
rsc
)
return
;
if
(
!
rsc
)
return
;
if
(
rsc
->
ssl
)
{
if
(
rsc
->
ssl
)
{
SSL_free
(
rsc
->
ssl
);
SSL_free
(
rsc
->
ssl
);
rsc
->
ssl
=
NULL
;
rsc
->
ssl
=
NULL
;
}
}
if
(
rsc
->
ssl_ctx
)
{
hi_free
(
rsc
);
SSL_CTX_free
(
rsc
->
ssl_ctx
);
rsc
->
ssl_ctx
=
NULL
;
}
free
(
rsc
);
}
}
static
in
t
redisSSLRead
(
redisContext
*
c
,
char
*
buf
,
size_t
bufcap
)
{
static
ssize_
t
redisSSLRead
(
redisContext
*
c
,
char
*
buf
,
size_t
bufcap
)
{
redisSSL
Context
*
rssl
=
c
->
priv
data
;
redisSSL
*
rssl
=
c
->
priv
ctx
;
int
nread
=
SSL_read
(
rssl
->
ssl
,
buf
,
bufcap
);
int
nread
=
SSL_read
(
rssl
->
ssl
,
buf
,
bufcap
);
if
(
nread
>
0
)
{
if
(
nread
>
0
)
{
...
@@ -356,8 +434,8 @@ static int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
...
@@ -356,8 +434,8 @@ static int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
}
}
}
}
static
in
t
redisSSLWrite
(
redisContext
*
c
)
{
static
ssize_
t
redisSSLWrite
(
redisContext
*
c
)
{
redisSSL
Context
*
rssl
=
c
->
priv
data
;
redisSSL
*
rssl
=
c
->
priv
ctx
;
size_t
len
=
rssl
->
lastLen
?
rssl
->
lastLen
:
sdslen
(
c
->
obuf
);
size_t
len
=
rssl
->
lastLen
?
rssl
->
lastLen
:
sdslen
(
c
->
obuf
);
int
rv
=
SSL_write
(
rssl
->
ssl
,
c
->
obuf
,
len
);
int
rv
=
SSL_write
(
rssl
->
ssl
,
c
->
obuf
,
len
);
...
@@ -380,7 +458,7 @@ static int redisSSLWrite(redisContext *c) {
...
@@ -380,7 +458,7 @@ static int redisSSLWrite(redisContext *c) {
static
void
redisSSLAsyncRead
(
redisAsyncContext
*
ac
)
{
static
void
redisSSLAsyncRead
(
redisAsyncContext
*
ac
)
{
int
rv
;
int
rv
;
redisSSL
Context
*
rssl
=
ac
->
c
.
priv
data
;
redisSSL
*
rssl
=
ac
->
c
.
priv
ctx
;
redisContext
*
c
=
&
ac
->
c
;
redisContext
*
c
=
&
ac
->
c
;
rssl
->
wantRead
=
0
;
rssl
->
wantRead
=
0
;
...
@@ -410,7 +488,7 @@ static void redisSSLAsyncRead(redisAsyncContext *ac) {
...
@@ -410,7 +488,7 @@ static void redisSSLAsyncRead(redisAsyncContext *ac) {
static
void
redisSSLAsyncWrite
(
redisAsyncContext
*
ac
)
{
static
void
redisSSLAsyncWrite
(
redisAsyncContext
*
ac
)
{
int
rv
,
done
=
0
;
int
rv
,
done
=
0
;
redisSSL
Context
*
rssl
=
ac
->
c
.
priv
data
;
redisSSL
*
rssl
=
ac
->
c
.
priv
ctx
;
redisContext
*
c
=
&
ac
->
c
;
redisContext
*
c
=
&
ac
->
c
;
rssl
->
pendingWrite
=
0
;
rssl
->
pendingWrite
=
0
;
...
@@ -439,7 +517,7 @@ static void redisSSLAsyncWrite(redisAsyncContext *ac) {
...
@@ -439,7 +517,7 @@ static void redisSSLAsyncWrite(redisAsyncContext *ac) {
}
}
redisContextFuncs
redisContextSSLFuncs
=
{
redisContextFuncs
redisContextSSLFuncs
=
{
.
free_priv
data
=
redisSSLFree
Context
,
.
free_priv
ctx
=
redisSSLFree
,
.
async_read
=
redisSSLAsyncRead
,
.
async_read
=
redisSSLAsyncRead
,
.
async_write
=
redisSSLAsyncWrite
,
.
async_write
=
redisSSLAsyncWrite
,
.
read
=
redisSSLRead
,
.
read
=
redisSSLRead
,
...
...
deps/hiredis/test.c
View file @
3f073b1d
#include "fmacros.h"
#include "fmacros.h"
#include "sockcompat.h"
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#ifndef _WIN32
#include <strings.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/time.h>
#
include <netdb.h>
#
endif
#include <assert.h>
#include <assert.h>
#include <unistd.h>
#include <signal.h>
#include <signal.h>
#include <errno.h>
#include <errno.h>
#include <limits.h>
#include <limits.h>
#include "hiredis.h"
#include "hiredis.h"
#include "async.h"
#ifdef HIREDIS_TEST_SSL
#ifdef HIREDIS_TEST_SSL
#include "hiredis_ssl.h"
#include "hiredis_ssl.h"
#endif
#endif
#include "net.h"
#include "net.h"
#include "win32.h"
enum
connection_type
{
enum
connection_type
{
CONN_TCP
,
CONN_TCP
,
...
@@ -47,15 +49,30 @@ struct config {
...
@@ -47,15 +49,30 @@ struct config {
}
ssl
;
}
ssl
;
};
};
struct
privdata
{
int
dtor_counter
;
};
#ifdef HIREDIS_TEST_SSL
redisSSLContext
*
_ssl_ctx
=
NULL
;
#endif
/* The following lines make up our testing "framework" :) */
/* The following lines make up our testing "framework" :) */
static
int
tests
=
0
,
fails
=
0
;
static
int
tests
=
0
,
fails
=
0
,
skips
=
0
;
#define test(_s) { printf("#%02d ", ++tests); printf(_s); }
#define test(_s) { printf("#%02d ", ++tests); printf(_s); }
#define test_cond(_c) if(_c) printf("\033[0;32mPASSED\033[0;0m\n"); else {printf("\033[0;31mFAILED\033[0;0m\n"); fails++;}
#define test_cond(_c) if(_c) printf("\033[0;32mPASSED\033[0;0m\n"); else {printf("\033[0;31mFAILED\033[0;0m\n"); fails++;}
#define test_skipped() { printf("\033[01;33mSKIPPED\033[0;0m\n"); skips++; }
static
long
long
usec
(
void
)
{
static
long
long
usec
(
void
)
{
#ifndef _MSC_VER
struct
timeval
tv
;
struct
timeval
tv
;
gettimeofday
(
&
tv
,
NULL
);
gettimeofday
(
&
tv
,
NULL
);
return
(((
long
long
)
tv
.
tv_sec
)
*
1000000
)
+
tv
.
tv_usec
;
return
(((
long
long
)
tv
.
tv_sec
)
*
1000000
)
+
tv
.
tv_usec
;
#else
FILETIME
ft
;
GetSystemTimeAsFileTime
(
&
ft
);
return
(((
long
long
)
ft
.
dwHighDateTime
<<
32
)
|
ft
.
dwLowDateTime
)
/
10
;
#endif
}
}
/* The assert() calls below have side effects, so we need assert()
/* The assert() calls below have side effects, so we need assert()
...
@@ -65,6 +82,43 @@ static long long usec(void) {
...
@@ -65,6 +82,43 @@ static long long usec(void) {
#define assert(e) (void)(e)
#define assert(e) (void)(e)
#endif
#endif
/* Helper to extract Redis version information. Aborts on any failure. */
#define REDIS_VERSION_FIELD "redis_version:"
void
get_redis_version
(
redisContext
*
c
,
int
*
majorptr
,
int
*
minorptr
)
{
redisReply
*
reply
;
char
*
eptr
,
*
s
,
*
e
;
int
major
,
minor
;
reply
=
redisCommand
(
c
,
"INFO"
);
if
(
reply
==
NULL
||
c
->
err
||
reply
->
type
!=
REDIS_REPLY_STRING
)
goto
abort
;
if
((
s
=
strstr
(
reply
->
str
,
REDIS_VERSION_FIELD
))
==
NULL
)
goto
abort
;
s
+=
strlen
(
REDIS_VERSION_FIELD
);
/* We need a field terminator and at least 'x.y.z' (5) bytes of data */
if
((
e
=
strstr
(
s
,
"
\r\n
"
))
==
NULL
||
(
e
-
s
)
<
5
)
goto
abort
;
/* Extract version info */
major
=
strtol
(
s
,
&
eptr
,
10
);
if
(
*
eptr
!=
'.'
)
goto
abort
;
minor
=
strtol
(
eptr
+
1
,
NULL
,
10
);
/* Push info the caller wants */
if
(
majorptr
)
*
majorptr
=
major
;
if
(
minorptr
)
*
minorptr
=
minor
;
freeReplyObject
(
reply
);
return
;
abort:
freeReplyObject
(
reply
);
fprintf
(
stderr
,
"Error: Cannot determine Redis version, aborting
\n
"
);
exit
(
1
);
}
static
redisContext
*
select_database
(
redisContext
*
c
)
{
static
redisContext
*
select_database
(
redisContext
*
c
)
{
redisReply
*
reply
;
redisReply
*
reply
;
...
@@ -87,6 +141,26 @@ static redisContext *select_database(redisContext *c) {
...
@@ -87,6 +141,26 @@ static redisContext *select_database(redisContext *c) {
return
c
;
return
c
;
}
}
/* Switch protocol */
static
void
send_hello
(
redisContext
*
c
,
int
version
)
{
redisReply
*
reply
;
int
expected
;
reply
=
redisCommand
(
c
,
"HELLO %d"
,
version
);
expected
=
version
==
3
?
REDIS_REPLY_MAP
:
REDIS_REPLY_ARRAY
;
assert
(
reply
!=
NULL
&&
reply
->
type
==
expected
);
freeReplyObject
(
reply
);
}
/* Togggle client tracking */
static
void
send_client_tracking
(
redisContext
*
c
,
const
char
*
str
)
{
redisReply
*
reply
;
reply
=
redisCommand
(
c
,
"CLIENT TRACKING %s"
,
str
);
assert
(
reply
!=
NULL
&&
reply
->
type
==
REDIS_REPLY_STATUS
);
freeReplyObject
(
reply
);
}
static
int
disconnect
(
redisContext
*
c
,
int
keep_fd
)
{
static
int
disconnect
(
redisContext
*
c
,
int
keep_fd
)
{
redisReply
*
reply
;
redisReply
*
reply
;
...
@@ -105,9 +179,9 @@ static int disconnect(redisContext *c, int keep_fd) {
...
@@ -105,9 +179,9 @@ static int disconnect(redisContext *c, int keep_fd) {
return
-
1
;
return
-
1
;
}
}
static
void
do_ssl_handshake
(
redisContext
*
c
,
struct
config
config
)
{
static
void
do_ssl_handshake
(
redisContext
*
c
)
{
#ifdef HIREDIS_TEST_SSL
#ifdef HIREDIS_TEST_SSL
redis
SecureConnection
(
c
,
config
.
ssl
.
ca_cert
,
config
.
ssl
.
cert
,
config
.
ssl
.
key
,
NULL
);
redis
InitiateSSLWithContext
(
c
,
_ssl_ctx
);
if
(
c
->
err
)
{
if
(
c
->
err
)
{
printf
(
"SSL error: %s
\n
"
,
c
->
errstr
);
printf
(
"SSL error: %s
\n
"
,
c
->
errstr
);
redisFree
(
c
);
redisFree
(
c
);
...
@@ -115,7 +189,6 @@ static void do_ssl_handshake(redisContext *c, struct config config) {
...
@@ -115,7 +189,6 @@ static void do_ssl_handshake(redisContext *c, struct config config) {
}
}
#else
#else
(
void
)
c
;
(
void
)
c
;
(
void
)
config
;
#endif
#endif
}
}
...
@@ -150,7 +223,7 @@ static redisContext *do_connect(struct config config) {
...
@@ -150,7 +223,7 @@ static redisContext *do_connect(struct config config) {
}
}
if
(
config
.
type
==
CONN_SSL
)
{
if
(
config
.
type
==
CONN_SSL
)
{
do_ssl_handshake
(
c
,
config
);
do_ssl_handshake
(
c
);
}
}
return
select_database
(
c
);
return
select_database
(
c
);
...
@@ -160,7 +233,7 @@ static void do_reconnect(redisContext *c, struct config config) {
...
@@ -160,7 +233,7 @@ static void do_reconnect(redisContext *c, struct config config) {
redisReconnect
(
c
);
redisReconnect
(
c
);
if
(
config
.
type
==
CONN_SSL
)
{
if
(
config
.
type
==
CONN_SSL
)
{
do_ssl_handshake
(
c
,
config
);
do_ssl_handshake
(
c
);
}
}
}
}
...
@@ -172,43 +245,43 @@ static void test_format_commands(void) {
...
@@ -172,43 +245,43 @@ static void test_format_commands(void) {
len
=
redisFormatCommand
(
&
cmd
,
"SET foo bar"
);
len
=
redisFormatCommand
(
&
cmd
,
"SET foo bar"
);
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$3
\r\n
foo
\r\n
$3
\r\n
bar
\r\n
"
,
len
)
==
0
&&
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$3
\r\n
foo
\r\n
$3
\r\n
bar
\r\n
"
,
len
)
==
0
&&
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
));
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
));
free
(
cmd
);
hi_
free
(
cmd
);
test
(
"Format command with %%s string interpolation: "
);
test
(
"Format command with %%s string interpolation: "
);
len
=
redisFormatCommand
(
&
cmd
,
"SET %s %s"
,
"foo"
,
"bar"
);
len
=
redisFormatCommand
(
&
cmd
,
"SET %s %s"
,
"foo"
,
"bar"
);
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$3
\r\n
foo
\r\n
$3
\r\n
bar
\r\n
"
,
len
)
==
0
&&
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$3
\r\n
foo
\r\n
$3
\r\n
bar
\r\n
"
,
len
)
==
0
&&
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
));
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
));
free
(
cmd
);
hi_
free
(
cmd
);
test
(
"Format command with %%s and an empty string: "
);
test
(
"Format command with %%s and an empty string: "
);
len
=
redisFormatCommand
(
&
cmd
,
"SET %s %s"
,
"foo"
,
""
);
len
=
redisFormatCommand
(
&
cmd
,
"SET %s %s"
,
"foo"
,
""
);
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$3
\r\n
foo
\r\n
$0
\r\n\r\n
"
,
len
)
==
0
&&
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$3
\r\n
foo
\r\n
$0
\r\n\r\n
"
,
len
)
==
0
&&
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
)
+
4
+
(
0
+
2
));
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
)
+
4
+
(
0
+
2
));
free
(
cmd
);
hi_
free
(
cmd
);
test
(
"Format command with an empty string in between proper interpolations: "
);
test
(
"Format command with an empty string in between proper interpolations: "
);
len
=
redisFormatCommand
(
&
cmd
,
"SET %s %s"
,
""
,
"foo"
);
len
=
redisFormatCommand
(
&
cmd
,
"SET %s %s"
,
""
,
"foo"
);
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$0
\r\n\r\n
$3
\r\n
foo
\r\n
"
,
len
)
==
0
&&
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$0
\r\n\r\n
$3
\r\n
foo
\r\n
"
,
len
)
==
0
&&
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
0
+
2
)
+
4
+
(
3
+
2
));
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
0
+
2
)
+
4
+
(
3
+
2
));
free
(
cmd
);
hi_
free
(
cmd
);
test
(
"Format command with %%b string interpolation: "
);
test
(
"Format command with %%b string interpolation: "
);
len
=
redisFormatCommand
(
&
cmd
,
"SET %b %b"
,
"foo"
,(
size_t
)
3
,
"b
\0
r"
,(
size_t
)
3
);
len
=
redisFormatCommand
(
&
cmd
,
"SET %b %b"
,
"foo"
,(
size_t
)
3
,
"b
\0
r"
,(
size_t
)
3
);
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$3
\r\n
foo
\r\n
$3
\r\n
b
\0
r
\r\n
"
,
len
)
==
0
&&
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$3
\r\n
foo
\r\n
$3
\r\n
b
\0
r
\r\n
"
,
len
)
==
0
&&
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
));
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
));
free
(
cmd
);
hi_
free
(
cmd
);
test
(
"Format command with %%b and an empty string: "
);
test
(
"Format command with %%b and an empty string: "
);
len
=
redisFormatCommand
(
&
cmd
,
"SET %b %b"
,
"foo"
,(
size_t
)
3
,
""
,(
size_t
)
0
);
len
=
redisFormatCommand
(
&
cmd
,
"SET %b %b"
,
"foo"
,(
size_t
)
3
,
""
,(
size_t
)
0
);
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$3
\r\n
foo
\r\n
$0
\r\n\r\n
"
,
len
)
==
0
&&
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$3
\r\n
foo
\r\n
$0
\r\n\r\n
"
,
len
)
==
0
&&
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
)
+
4
+
(
0
+
2
));
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
)
+
4
+
(
0
+
2
));
free
(
cmd
);
hi_
free
(
cmd
);
test
(
"Format command with literal %%: "
);
test
(
"Format command with literal %%: "
);
len
=
redisFormatCommand
(
&
cmd
,
"SET %% %%"
);
len
=
redisFormatCommand
(
&
cmd
,
"SET %% %%"
);
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$1
\r\n
%
\r\n
$1
\r\n
%
\r\n
"
,
len
)
==
0
&&
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$1
\r\n
%
\r\n
$1
\r\n
%
\r\n
"
,
len
)
==
0
&&
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
1
+
2
)
+
4
+
(
1
+
2
));
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
1
+
2
)
+
4
+
(
1
+
2
));
free
(
cmd
);
hi_
free
(
cmd
);
/* Vararg width depends on the type. These tests make sure that the
/* Vararg width depends on the type. These tests make sure that the
* width is correctly determined using the format and subsequent varargs
* width is correctly determined using the format and subsequent varargs
...
@@ -219,7 +292,7 @@ static void test_format_commands(void) {
...
@@ -219,7 +292,7 @@ static void test_format_commands(void) {
len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \
len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \
test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \
test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \
len == 4+5+(12+2)+4+(9+2)); \
len == 4+5+(12+2)+4+(9+2)); \
free(cmd);
\
hi_
free(cmd); \
} while(0)
} while(0)
#define FLOAT_WIDTH_TEST(type) do { \
#define FLOAT_WIDTH_TEST(type) do { \
...
@@ -228,7 +301,7 @@ static void test_format_commands(void) {
...
@@ -228,7 +301,7 @@ static void test_format_commands(void) {
len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \
len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \
test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \
test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \
len == 4+5+(12+2)+4+(9+2)); \
len == 4+5+(12+2)+4+(9+2)); \
free(cmd);
\
hi_
free(cmd); \
} while(0)
} while(0)
INTEGER_WIDTH_TEST
(
"d"
,
int
);
INTEGER_WIDTH_TEST
(
"d"
,
int
);
...
@@ -259,24 +332,24 @@ static void test_format_commands(void) {
...
@@ -259,24 +332,24 @@ static void test_format_commands(void) {
len
=
redisFormatCommandArgv
(
&
cmd
,
argc
,
argv
,
NULL
);
len
=
redisFormatCommandArgv
(
&
cmd
,
argc
,
argv
,
NULL
);
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$3
\r\n
foo
\r\n
$3
\r\n
bar
\r\n
"
,
len
)
==
0
&&
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$3
\r\n
foo
\r\n
$3
\r\n
bar
\r\n
"
,
len
)
==
0
&&
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
));
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
));
free
(
cmd
);
hi_
free
(
cmd
);
test
(
"Format command by passing argc/argv with lengths: "
);
test
(
"Format command by passing argc/argv with lengths: "
);
len
=
redisFormatCommandArgv
(
&
cmd
,
argc
,
argv
,
lens
);
len
=
redisFormatCommandArgv
(
&
cmd
,
argc
,
argv
,
lens
);
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$7
\r\n
foo
\0
xxx
\r\n
$3
\r\n
bar
\r\n
"
,
len
)
==
0
&&
test_cond
(
strncmp
(
cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$7
\r\n
foo
\0
xxx
\r\n
$3
\r\n
bar
\r\n
"
,
len
)
==
0
&&
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
7
+
2
)
+
4
+
(
3
+
2
));
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
7
+
2
)
+
4
+
(
3
+
2
));
free
(
cmd
);
hi_
free
(
cmd
);
sds
sds_cmd
;
sds
sds_cmd
;
sds_cmd
=
sdsempty
()
;
sds_cmd
=
NULL
;
test
(
"Format command into sds by passing argc/argv without lengths: "
);
test
(
"Format command into sds by passing argc/argv without lengths: "
);
len
=
redisFormatSdsCommandArgv
(
&
sds_cmd
,
argc
,
argv
,
NULL
);
len
=
redisFormatSdsCommandArgv
(
&
sds_cmd
,
argc
,
argv
,
NULL
);
test_cond
(
strncmp
(
sds_cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$3
\r\n
foo
\r\n
$3
\r\n
bar
\r\n
"
,
len
)
==
0
&&
test_cond
(
strncmp
(
sds_cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$3
\r\n
foo
\r\n
$3
\r\n
bar
\r\n
"
,
len
)
==
0
&&
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
));
len
==
4
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
)
+
4
+
(
3
+
2
));
sdsfree
(
sds_cmd
);
sdsfree
(
sds_cmd
);
sds_cmd
=
sdsempty
()
;
sds_cmd
=
NULL
;
test
(
"Format command into sds by passing argc/argv with lengths: "
);
test
(
"Format command into sds by passing argc/argv with lengths: "
);
len
=
redisFormatSdsCommandArgv
(
&
sds_cmd
,
argc
,
argv
,
lens
);
len
=
redisFormatSdsCommandArgv
(
&
sds_cmd
,
argc
,
argv
,
lens
);
test_cond
(
strncmp
(
sds_cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$7
\r\n
foo
\0
xxx
\r\n
$3
\r\n
bar
\r\n
"
,
len
)
==
0
&&
test_cond
(
strncmp
(
sds_cmd
,
"*3
\r\n
$3
\r\n
SET
\r\n
$7
\r\n
foo
\0
xxx
\r\n
$3
\r\n
bar
\r\n
"
,
len
)
==
0
&&
...
@@ -300,7 +373,7 @@ static void test_append_formatted_commands(struct config config) {
...
@@ -300,7 +373,7 @@ static void test_append_formatted_commands(struct config config) {
assert
(
redisGetReply
(
c
,
(
void
*
)
&
reply
)
==
REDIS_OK
);
assert
(
redisGetReply
(
c
,
(
void
*
)
&
reply
)
==
REDIS_OK
);
free
(
cmd
);
hi_
free
(
cmd
);
freeReplyObject
(
reply
);
freeReplyObject
(
reply
);
disconnect
(
c
,
0
);
disconnect
(
c
,
0
);
...
@@ -308,7 +381,7 @@ static void test_append_formatted_commands(struct config config) {
...
@@ -308,7 +381,7 @@ static void test_append_formatted_commands(struct config config) {
static
void
test_reply_reader
(
void
)
{
static
void
test_reply_reader
(
void
)
{
redisReader
*
reader
;
redisReader
*
reader
;
void
*
reply
;
void
*
reply
,
*
root
;
int
ret
;
int
ret
;
int
i
;
int
i
;
...
@@ -332,16 +405,26 @@ static void test_reply_reader(void) {
...
@@ -332,16 +405,26 @@ static void test_reply_reader(void) {
strcasecmp
(
reader
->
errstr
,
"Protocol error, got
\"
@
\"
as reply type byte"
)
==
0
);
strcasecmp
(
reader
->
errstr
,
"Protocol error, got
\"
@
\"
as reply type byte"
)
==
0
);
redisReaderFree
(
reader
);
redisReaderFree
(
reader
);
test
(
"Set error on nested multi bulks with depth > 7: "
);
reader
=
redisReaderCreate
();
reader
=
redisReaderCreate
();
test
(
"Can handle arbitrarily nested multi-bulks: "
);
for
(
i
=
0
;
i
<
9
;
i
++
)
{
for
(
i
=
0
;
i
<
128
;
i
++
)
{
redisReaderFeed
(
reader
,(
char
*
)
"*1
\r\n
"
,
4
);
redisReaderFeed
(
reader
,(
char
*
)
"*1
\r\n
"
,
4
);
}
}
redisReaderFeed
(
reader
,(
char
*
)
"$6
\r\n
LOLWUT
\r\n
"
,
12
);
ret
=
redisReaderGetReply
(
reader
,
&
reply
);
root
=
reply
;
/* Keep track of the root reply */
test_cond
(
ret
==
REDIS_OK
&&
((
redisReply
*
)
reply
)
->
type
==
REDIS_REPLY_ARRAY
&&
((
redisReply
*
)
reply
)
->
elements
==
1
);
ret
=
redisReaderGetReply
(
reader
,
NULL
);
test
(
"Can parse arbitrarily nested multi-bulks correctly: "
);
test_cond
(
ret
==
REDIS_ERR
&&
while
(
i
--
)
{
strncasecmp
(
reader
->
errstr
,
"No support for"
,
14
)
==
0
);
assert
(
reply
!=
NULL
&&
((
redisReply
*
)
reply
)
->
type
==
REDIS_REPLY_ARRAY
);
reply
=
((
redisReply
*
)
reply
)
->
element
[
0
];
}
test_cond
(((
redisReply
*
)
reply
)
->
type
==
REDIS_REPLY_STRING
&&
!
memcmp
(((
redisReply
*
)
reply
)
->
str
,
"LOLWUT"
,
6
));
freeReplyObject
(
root
);
redisReaderFree
(
reader
);
redisReaderFree
(
reader
);
test
(
"Correctly parses LLONG_MAX: "
);
test
(
"Correctly parses LLONG_MAX: "
);
...
@@ -400,6 +483,16 @@ static void test_reply_reader(void) {
...
@@ -400,6 +483,16 @@ static void test_reply_reader(void) {
freeReplyObject
(
reply
);
freeReplyObject
(
reply
);
redisReaderFree
(
reader
);
redisReaderFree
(
reader
);
test
(
"Can configure maximum multi-bulk elements: "
);
reader
=
redisReaderCreate
();
reader
->
maxelements
=
1024
;
redisReaderFeed
(
reader
,
"*1025
\r\n
"
,
7
);
ret
=
redisReaderGetReply
(
reader
,
&
reply
);
test_cond
(
ret
==
REDIS_ERR
&&
strcasecmp
(
reader
->
errstr
,
"Multi-bulk length out of range"
)
==
0
);
freeReplyObject
(
reply
);
redisReaderFree
(
reader
);
#if LLONG_MAX > SIZE_MAX
#if LLONG_MAX > SIZE_MAX
test
(
"Set error when array > SIZE_MAX: "
);
test
(
"Set error when array > SIZE_MAX: "
);
reader
=
redisReaderCreate
();
reader
=
redisReaderCreate
();
...
@@ -459,6 +552,32 @@ static void test_reply_reader(void) {
...
@@ -459,6 +552,32 @@ static void test_reply_reader(void) {
((
redisReply
*
)
reply
)
->
elements
==
0
);
((
redisReply
*
)
reply
)
->
elements
==
0
);
freeReplyObject
(
reply
);
freeReplyObject
(
reply
);
redisReaderFree
(
reader
);
redisReaderFree
(
reader
);
/* RESP3 verbatim strings (GitHub issue #802) */
test
(
"Can parse RESP3 verbatim strings: "
);
reader
=
redisReaderCreate
();
redisReaderFeed
(
reader
,(
char
*
)
"=10
\r\n
txt:LOLWUT
\r\n
"
,
17
);
ret
=
redisReaderGetReply
(
reader
,
&
reply
);
test_cond
(
ret
==
REDIS_OK
&&
((
redisReply
*
)
reply
)
->
type
==
REDIS_REPLY_VERB
&&
!
memcmp
(((
redisReply
*
)
reply
)
->
str
,
"LOLWUT"
,
6
));
freeReplyObject
(
reply
);
redisReaderFree
(
reader
);
/* RESP3 push messages (Github issue #815) */
test
(
"Can parse RESP3 push messages: "
);
reader
=
redisReaderCreate
();
redisReaderFeed
(
reader
,(
char
*
)
">2
\r\n
$6
\r\n
LOLWUT
\r\n
:42
\r\n
"
,
21
);
ret
=
redisReaderGetReply
(
reader
,
&
reply
);
test_cond
(
ret
==
REDIS_OK
&&
((
redisReply
*
)
reply
)
->
type
==
REDIS_REPLY_PUSH
&&
((
redisReply
*
)
reply
)
->
elements
==
2
&&
((
redisReply
*
)
reply
)
->
element
[
0
]
->
type
==
REDIS_REPLY_STRING
&&
!
memcmp
(((
redisReply
*
)
reply
)
->
element
[
0
]
->
str
,
"LOLWUT"
,
6
)
&&
((
redisReply
*
)
reply
)
->
element
[
1
]
->
type
==
REDIS_REPLY_INTEGER
&&
((
redisReply
*
)
reply
)
->
element
[
1
]
->
integer
==
42
);
freeReplyObject
(
reply
);
redisReaderFree
(
reader
);
}
}
static
void
test_free_null
(
void
)
{
static
void
test_free_null
(
void
)
{
...
@@ -474,6 +593,47 @@ static void test_free_null(void) {
...
@@ -474,6 +593,47 @@ static void test_free_null(void) {
test_cond
(
reply
==
NULL
);
test_cond
(
reply
==
NULL
);
}
}
static
void
*
hi_malloc_fail
(
size_t
size
)
{
(
void
)
size
;
return
NULL
;
}
static
void
*
hi_calloc_fail
(
size_t
nmemb
,
size_t
size
)
{
(
void
)
nmemb
;
(
void
)
size
;
return
NULL
;
}
static
void
*
hi_realloc_fail
(
void
*
ptr
,
size_t
size
)
{
(
void
)
ptr
;
(
void
)
size
;
return
NULL
;
}
static
void
test_allocator_injection
(
void
)
{
hiredisAllocFuncs
ha
=
{
.
mallocFn
=
hi_malloc_fail
,
.
callocFn
=
hi_calloc_fail
,
.
reallocFn
=
hi_realloc_fail
,
.
strdupFn
=
strdup
,
.
freeFn
=
free
,
};
// Override hiredis allocators
hiredisSetAllocators
(
&
ha
);
test
(
"redisContext uses injected allocators: "
);
redisContext
*
c
=
redisConnect
(
"localhost"
,
6379
);
test_cond
(
c
==
NULL
);
test
(
"redisReader uses injected allocators: "
);
redisReader
*
reader
=
redisReaderCreate
();
test_cond
(
reader
==
NULL
);
// Return allocators to default
hiredisResetAllocators
();
}
#define HIREDIS_BAD_DOMAIN "idontexist-noreally.com"
#define HIREDIS_BAD_DOMAIN "idontexist-noreally.com"
static
void
test_blocking_connection_errors
(
void
)
{
static
void
test_blocking_connection_errors
(
void
)
{
redisContext
*
c
;
redisContext
*
c
;
...
@@ -491,19 +651,19 @@ static void test_blocking_connection_errors(void) {
...
@@ -491,19 +651,19 @@ static void test_blocking_connection_errors(void) {
(
strcmp
(
c
->
errstr
,
"Name or service not known"
)
==
0
||
(
strcmp
(
c
->
errstr
,
"Name or service not known"
)
==
0
||
strcmp
(
c
->
errstr
,
"Can't resolve: "
HIREDIS_BAD_DOMAIN
)
==
0
||
strcmp
(
c
->
errstr
,
"Can't resolve: "
HIREDIS_BAD_DOMAIN
)
==
0
||
strcmp
(
c
->
errstr
,
"Name does not resolve"
)
==
0
||
strcmp
(
c
->
errstr
,
"Name does not resolve"
)
==
0
||
strcmp
(
c
->
errstr
,
strcmp
(
c
->
errstr
,
"nodename nor servname provided, or not known"
)
==
0
||
"nodename nor servname provided, or not known"
)
==
0
||
strcmp
(
c
->
errstr
,
"No address associated with hostname"
)
==
0
||
strcmp
(
c
->
errstr
,
"No address associated with hostname"
)
==
0
||
strcmp
(
c
->
errstr
,
"Temporary failure in name resolution"
)
==
0
||
strcmp
(
c
->
errstr
,
"Temporary failure in name resolution"
)
==
0
||
strcmp
(
c
->
errstr
,
strcmp
(
c
->
errstr
,
"hostname nor servname provided, or not known"
)
==
0
||
"hostname nor servname provided, or not known
"
)
==
0
||
strcmp
(
c
->
errstr
,
"no address associated with name
"
)
==
0
||
strcmp
(
c
->
errstr
,
"
n
o
address associated with name
"
)
==
0
));
strcmp
(
c
->
errstr
,
"
N
o
such host is known.
"
)
==
0
));
redisFree
(
c
);
redisFree
(
c
);
}
else
{
}
else
{
printf
(
"Skipping NXDOMAIN test. Found evil ISP!
\n
"
);
printf
(
"Skipping NXDOMAIN test. Found evil ISP!
\n
"
);
freeaddrinfo
(
ai_tmp
);
freeaddrinfo
(
ai_tmp
);
}
}
#ifndef _WIN32
test
(
"Returns error when the port is not open: "
);
test
(
"Returns error when the port is not open: "
);
c
=
redisConnect
((
char
*
)
"localhost"
,
1
);
c
=
redisConnect
((
char
*
)
"localhost"
,
1
);
test_cond
(
c
->
err
==
REDIS_ERR_IO
&&
test_cond
(
c
->
err
==
REDIS_ERR_IO
&&
...
@@ -514,11 +674,147 @@ static void test_blocking_connection_errors(void) {
...
@@ -514,11 +674,147 @@ static void test_blocking_connection_errors(void) {
c
=
redisConnectUnix
((
char
*
)
"/tmp/idontexist.sock"
);
c
=
redisConnectUnix
((
char
*
)
"/tmp/idontexist.sock"
);
test_cond
(
c
->
err
==
REDIS_ERR_IO
);
/* Don't care about the message... */
test_cond
(
c
->
err
==
REDIS_ERR_IO
);
/* Don't care about the message... */
redisFree
(
c
);
redisFree
(
c
);
#endif
}
/* Dummy push handler */
void
push_handler
(
void
*
privdata
,
void
*
reply
)
{
int
*
counter
=
privdata
;
freeReplyObject
(
reply
);
*
counter
+=
1
;
}
/* Dummy function just to test setting a callback with redisOptions */
void
push_handler_async
(
redisAsyncContext
*
ac
,
void
*
reply
)
{
(
void
)
ac
;
(
void
)
reply
;
}
static
void
test_resp3_push_handler
(
redisContext
*
c
)
{
redisPushFn
*
old
=
NULL
;
redisReply
*
reply
;
void
*
privdata
;
int
n
=
0
;
/* Switch to RESP3 and turn on client tracking */
send_hello
(
c
,
3
);
send_client_tracking
(
c
,
"ON"
);
privdata
=
c
->
privdata
;
c
->
privdata
=
&
n
;
reply
=
redisCommand
(
c
,
"GET key:0"
);
assert
(
reply
!=
NULL
);
freeReplyObject
(
reply
);
test
(
"RESP3 PUSH messages are handled out of band by default: "
);
reply
=
redisCommand
(
c
,
"SET key:0 val:0"
);
test_cond
(
reply
!=
NULL
&&
reply
->
type
==
REDIS_REPLY_STATUS
);
freeReplyObject
(
reply
);
assert
((
reply
=
redisCommand
(
c
,
"GET key:0"
))
!=
NULL
);
freeReplyObject
(
reply
);
old
=
redisSetPushCallback
(
c
,
push_handler
);
test
(
"We can set a custom RESP3 PUSH handler: "
);
reply
=
redisCommand
(
c
,
"SET key:0 val:0"
);
test_cond
(
reply
!=
NULL
&&
reply
->
type
==
REDIS_REPLY_STATUS
&&
n
==
1
);
freeReplyObject
(
reply
);
/* Unset the push callback and generate an invalidate message making
* sure it is not handled out of band. */
test
(
"With no handler, PUSH replies come in-band: "
);
redisSetPushCallback
(
c
,
NULL
);
assert
((
reply
=
redisCommand
(
c
,
"GET key:0"
))
!=
NULL
);
freeReplyObject
(
reply
);
assert
((
reply
=
redisCommand
(
c
,
"SET key:0 invalid"
))
!=
NULL
);
test_cond
(
reply
->
type
==
REDIS_REPLY_PUSH
);
freeReplyObject
(
reply
);
test
(
"With no PUSH handler, no replies are lost: "
);
assert
(
redisGetReply
(
c
,
(
void
**
)
&
reply
)
==
REDIS_OK
);
test_cond
(
reply
!=
NULL
&&
reply
->
type
==
REDIS_REPLY_STATUS
);
freeReplyObject
(
reply
);
/* Return to the originally set PUSH handler */
assert
(
old
!=
NULL
);
redisSetPushCallback
(
c
,
old
);
/* Switch back to RESP2 and disable tracking */
c
->
privdata
=
privdata
;
send_client_tracking
(
c
,
"OFF"
);
send_hello
(
c
,
2
);
}
redisOptions
get_redis_tcp_options
(
struct
config
config
)
{
redisOptions
options
=
{
0
};
REDIS_OPTIONS_SET_TCP
(
&
options
,
config
.
tcp
.
host
,
config
.
tcp
.
port
);
return
options
;
}
static
void
test_resp3_push_options
(
struct
config
config
)
{
redisAsyncContext
*
ac
;
redisContext
*
c
;
redisOptions
options
;
test
(
"We set a default RESP3 handler for redisContext: "
);
options
=
get_redis_tcp_options
(
config
);
assert
((
c
=
redisConnectWithOptions
(
&
options
))
!=
NULL
);
test_cond
(
c
->
push_cb
!=
NULL
);
redisFree
(
c
);
test
(
"We don't set a default RESP3 push handler for redisAsyncContext: "
);
options
=
get_redis_tcp_options
(
config
);
assert
((
ac
=
redisAsyncConnectWithOptions
(
&
options
))
!=
NULL
);
test_cond
(
ac
->
c
.
push_cb
==
NULL
);
redisAsyncFree
(
ac
);
test
(
"Our REDIS_OPT_NO_PUSH_AUTOFREE flag works: "
);
options
=
get_redis_tcp_options
(
config
);
options
.
options
|=
REDIS_OPT_NO_PUSH_AUTOFREE
;
assert
((
c
=
redisConnectWithOptions
(
&
options
))
!=
NULL
);
test_cond
(
c
->
push_cb
==
NULL
);
redisFree
(
c
);
test
(
"We can use redisOptions to set a custom PUSH handler for redisContext: "
);
options
=
get_redis_tcp_options
(
config
);
options
.
push_cb
=
push_handler
;
assert
((
c
=
redisConnectWithOptions
(
&
options
))
!=
NULL
);
test_cond
(
c
->
push_cb
==
push_handler
);
redisFree
(
c
);
test
(
"We can use redisOptions to set a custom PUSH handler for redisAsyncContext: "
);
options
=
get_redis_tcp_options
(
config
);
options
.
async_push_cb
=
push_handler_async
;
assert
((
ac
=
redisAsyncConnectWithOptions
(
&
options
))
!=
NULL
);
test_cond
(
ac
->
push_cb
==
push_handler_async
);
redisAsyncFree
(
ac
);
}
void
free_privdata
(
void
*
privdata
)
{
struct
privdata
*
data
=
privdata
;
data
->
dtor_counter
++
;
}
static
void
test_privdata_hooks
(
struct
config
config
)
{
struct
privdata
data
=
{
0
};
redisOptions
options
;
redisContext
*
c
;
test
(
"We can use redisOptions to set privdata: "
);
options
=
get_redis_tcp_options
(
config
);
REDIS_OPTIONS_SET_PRIVDATA
(
&
options
,
&
data
,
free_privdata
);
assert
((
c
=
redisConnectWithOptions
(
&
options
))
!=
NULL
);
test_cond
(
c
->
privdata
==
&
data
);
test
(
"Our privdata destructor fires when we free the context: "
);
redisFree
(
c
);
test_cond
(
data
.
dtor_counter
==
1
);
}
}
static
void
test_blocking_connection
(
struct
config
config
)
{
static
void
test_blocking_connection
(
struct
config
config
)
{
redisContext
*
c
;
redisContext
*
c
;
redisReply
*
reply
;
redisReply
*
reply
;
int
major
;
c
=
do_connect
(
config
);
c
=
do_connect
(
config
);
...
@@ -591,14 +887,42 @@ static void test_blocking_connection(struct config config) {
...
@@ -591,14 +887,42 @@ static void test_blocking_connection(struct config config) {
strcasecmp
(
reply
->
element
[
1
]
->
str
,
"pong"
)
==
0
);
strcasecmp
(
reply
->
element
[
1
]
->
str
,
"pong"
)
==
0
);
freeReplyObject
(
reply
);
freeReplyObject
(
reply
);
/* Make sure passing NULL to redisGetReply is safe */
test
(
"Can pass NULL to redisGetReply: "
);
assert
(
redisAppendCommand
(
c
,
"PING"
)
==
REDIS_OK
);
test_cond
(
redisGetReply
(
c
,
NULL
)
==
REDIS_OK
);
get_redis_version
(
c
,
&
major
,
NULL
);
if
(
major
>=
6
)
test_resp3_push_handler
(
c
);
test_resp3_push_options
(
config
);
test_privdata_hooks
(
config
);
disconnect
(
c
,
0
);
disconnect
(
c
,
0
);
}
}
/* Send DEBUG SLEEP 0 to detect if we have this command */
static
int
detect_debug_sleep
(
redisContext
*
c
)
{
int
detected
;
redisReply
*
reply
=
redisCommand
(
c
,
"DEBUG SLEEP 0
\r\n
"
);
if
(
reply
==
NULL
||
c
->
err
)
{
const
char
*
cause
=
c
->
err
?
c
->
errstr
:
"(none)"
;
fprintf
(
stderr
,
"Error testing for DEBUG SLEEP (Redis error: %s), exiting
\n
"
,
cause
);
exit
(
-
1
);
}
detected
=
reply
->
type
==
REDIS_REPLY_STATUS
;
freeReplyObject
(
reply
);
return
detected
;
}
static
void
test_blocking_connection_timeouts
(
struct
config
config
)
{
static
void
test_blocking_connection_timeouts
(
struct
config
config
)
{
redisContext
*
c
;
redisContext
*
c
;
redisReply
*
reply
;
redisReply
*
reply
;
ssize_t
s
;
ssize_t
s
;
const
char
*
cmd
=
"DEBUG SLEEP 3
\r\n
"
;
const
char
*
sleep_
cmd
=
"DEBUG SLEEP 3
\r\n
"
;
struct
timeval
tv
;
struct
timeval
tv
;
c
=
do_connect
(
config
);
c
=
do_connect
(
config
);
...
@@ -615,14 +939,24 @@ static void test_blocking_connection_timeouts(struct config config) {
...
@@ -615,14 +939,24 @@ static void test_blocking_connection_timeouts(struct config config) {
c
=
do_connect
(
config
);
c
=
do_connect
(
config
);
test
(
"Does not return a reply when the command times out: "
);
test
(
"Does not return a reply when the command times out: "
);
redisAppendFormattedCommand
(
c
,
cmd
,
strlen
(
cmd
));
if
(
detect_debug_sleep
(
c
))
{
s
=
c
->
funcs
->
write
(
c
);
redisAppendFormattedCommand
(
c
,
sleep_cmd
,
strlen
(
sleep_cmd
));
tv
.
tv_sec
=
0
;
s
=
c
->
funcs
->
write
(
c
);
tv
.
tv_usec
=
10000
;
tv
.
tv_sec
=
0
;
redisSetTimeout
(
c
,
tv
);
tv
.
tv_usec
=
10000
;
reply
=
redisCommand
(
c
,
"GET foo"
);
redisSetTimeout
(
c
,
tv
);
test_cond
(
s
>
0
&&
reply
==
NULL
&&
c
->
err
==
REDIS_ERR_IO
&&
strcmp
(
c
->
errstr
,
"Resource temporarily unavailable"
)
==
0
);
reply
=
redisCommand
(
c
,
"GET foo"
);
freeReplyObject
(
reply
);
#ifndef _WIN32
test_cond
(
s
>
0
&&
reply
==
NULL
&&
c
->
err
==
REDIS_ERR_IO
&&
strcmp
(
c
->
errstr
,
"Resource temporarily unavailable"
)
==
0
);
#else
test_cond
(
s
>
0
&&
reply
==
NULL
&&
c
->
err
==
REDIS_ERR_TIMEOUT
&&
strcmp
(
c
->
errstr
,
"recv timeout"
)
==
0
);
#endif
freeReplyObject
(
reply
);
}
else
{
test_skipped
();
}
test
(
"Reconnect properly reconnects after a timeout: "
);
test
(
"Reconnect properly reconnects after a timeout: "
);
do_reconnect
(
c
,
config
);
do_reconnect
(
c
,
config
);
...
@@ -649,18 +983,7 @@ static void test_blocking_io_errors(struct config config) {
...
@@ -649,18 +983,7 @@ static void test_blocking_io_errors(struct config config) {
/* Connect to target given by config. */
/* Connect to target given by config. */
c
=
do_connect
(
config
);
c
=
do_connect
(
config
);
{
get_redis_version
(
c
,
&
major
,
&
minor
);
/* Find out Redis version to determine the path for the next test */
const
char
*
field
=
"redis_version:"
;
char
*
p
,
*
eptr
;
reply
=
redisCommand
(
c
,
"INFO"
);
p
=
strstr
(
reply
->
str
,
field
);
major
=
strtol
(
p
+
strlen
(
field
),
&
eptr
,
10
);
p
=
eptr
+
1
;
/* char next to the first "." */
minor
=
strtol
(
p
,
&
eptr
,
10
);
freeReplyObject
(
reply
);
}
test
(
"Returns I/O error when the connection is lost: "
);
test
(
"Returns I/O error when the connection is lost: "
);
reply
=
redisCommand
(
c
,
"QUIT"
);
reply
=
redisCommand
(
c
,
"QUIT"
);
...
@@ -674,6 +997,7 @@ static void test_blocking_io_errors(struct config config) {
...
@@ -674,6 +997,7 @@ static void test_blocking_io_errors(struct config config) {
test_cond
(
reply
==
NULL
);
test_cond
(
reply
==
NULL
);
}
}
#ifndef _WIN32
/* On 2.0, QUIT will cause the connection to be closed immediately and
/* On 2.0, QUIT will cause the connection to be closed immediately and
* the read(2) for the reply on QUIT will set the error to EOF.
* the read(2) for the reply on QUIT will set the error to EOF.
* On >2.0, QUIT will return with OK and another read(2) needed to be
* On >2.0, QUIT will return with OK and another read(2) needed to be
...
@@ -681,14 +1005,19 @@ static void test_blocking_io_errors(struct config config) {
...
@@ -681,14 +1005,19 @@ static void test_blocking_io_errors(struct config config) {
* conditions, the error will be set to EOF. */
* conditions, the error will be set to EOF. */
assert
(
c
->
err
==
REDIS_ERR_EOF
&&
assert
(
c
->
err
==
REDIS_ERR_EOF
&&
strcmp
(
c
->
errstr
,
"Server closed the connection"
)
==
0
);
strcmp
(
c
->
errstr
,
"Server closed the connection"
)
==
0
);
#endif
redisFree
(
c
);
redisFree
(
c
);
c
=
do_connect
(
config
);
c
=
do_connect
(
config
);
test
(
"Returns I/O error on socket timeout: "
);
test
(
"Returns I/O error on socket timeout: "
);
struct
timeval
tv
=
{
0
,
1000
};
struct
timeval
tv
=
{
0
,
1000
};
assert
(
redisSetTimeout
(
c
,
tv
)
==
REDIS_OK
);
assert
(
redisSetTimeout
(
c
,
tv
)
==
REDIS_OK
);
test_cond
(
redisGetReply
(
c
,
&
_reply
)
==
REDIS_ERR
&&
int
respcode
=
redisGetReply
(
c
,
&
_reply
);
c
->
err
==
REDIS_ERR_IO
&&
errno
==
EAGAIN
);
#ifndef _WIN32
test_cond
(
respcode
==
REDIS_ERR
&&
c
->
err
==
REDIS_ERR_IO
&&
errno
==
EAGAIN
);
#else
test_cond
(
respcode
==
REDIS_ERR
&&
c
->
err
==
REDIS_ERR_TIMEOUT
);
#endif
redisFree
(
c
);
redisFree
(
c
);
}
}
...
@@ -716,6 +1045,18 @@ static void test_invalid_timeout_errors(struct config config) {
...
@@ -716,6 +1045,18 @@ static void test_invalid_timeout_errors(struct config config) {
redisFree
(
c
);
redisFree
(
c
);
}
}
/* Wrap malloc to abort on failure so OOM checks don't make the test logic
* harder to follow. */
void
*
hi_malloc_safe
(
size_t
size
)
{
void
*
ptr
=
hi_malloc
(
size
);
if
(
ptr
==
NULL
)
{
fprintf
(
stderr
,
"Error: Out of memory
\n
"
);
exit
(
-
1
);
}
return
ptr
;
}
static
void
test_throughput
(
struct
config
config
)
{
static
void
test_throughput
(
struct
config
config
)
{
redisContext
*
c
=
do_connect
(
config
);
redisContext
*
c
=
do_connect
(
config
);
redisReply
**
replies
;
redisReply
**
replies
;
...
@@ -727,7 +1068,7 @@ static void test_throughput(struct config config) {
...
@@ -727,7 +1068,7 @@ static void test_throughput(struct config config) {
freeReplyObject
(
redisCommand
(
c
,
"LPUSH mylist foo"
));
freeReplyObject
(
redisCommand
(
c
,
"LPUSH mylist foo"
));
num
=
1000
;
num
=
1000
;
replies
=
malloc
(
sizeof
(
redisReply
*
)
*
num
);
replies
=
hi_
malloc
_safe
(
sizeof
(
redisReply
*
)
*
num
);
t1
=
usec
();
t1
=
usec
();
for
(
i
=
0
;
i
<
num
;
i
++
)
{
for
(
i
=
0
;
i
<
num
;
i
++
)
{
replies
[
i
]
=
redisCommand
(
c
,
"PING"
);
replies
[
i
]
=
redisCommand
(
c
,
"PING"
);
...
@@ -735,10 +1076,10 @@ static void test_throughput(struct config config) {
...
@@ -735,10 +1076,10 @@ static void test_throughput(struct config config) {
}
}
t2
=
usec
();
t2
=
usec
();
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
free
(
replies
);
hi_
free
(
replies
);
printf
(
"
\t
(%dx PING: %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
printf
(
"
\t
(%dx PING: %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
replies
=
malloc
(
sizeof
(
redisReply
*
)
*
num
);
replies
=
hi_
malloc
_safe
(
sizeof
(
redisReply
*
)
*
num
);
t1
=
usec
();
t1
=
usec
();
for
(
i
=
0
;
i
<
num
;
i
++
)
{
for
(
i
=
0
;
i
<
num
;
i
++
)
{
replies
[
i
]
=
redisCommand
(
c
,
"LRANGE mylist 0 499"
);
replies
[
i
]
=
redisCommand
(
c
,
"LRANGE mylist 0 499"
);
...
@@ -747,10 +1088,10 @@ static void test_throughput(struct config config) {
...
@@ -747,10 +1088,10 @@ static void test_throughput(struct config config) {
}
}
t2
=
usec
();
t2
=
usec
();
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
free
(
replies
);
hi_
free
(
replies
);
printf
(
"
\t
(%dx LRANGE with 500 elements: %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
printf
(
"
\t
(%dx LRANGE with 500 elements: %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
replies
=
malloc
(
sizeof
(
redisReply
*
)
*
num
);
replies
=
hi_
malloc
_safe
(
sizeof
(
redisReply
*
)
*
num
);
t1
=
usec
();
t1
=
usec
();
for
(
i
=
0
;
i
<
num
;
i
++
)
{
for
(
i
=
0
;
i
<
num
;
i
++
)
{
replies
[
i
]
=
redisCommand
(
c
,
"INCRBY incrkey %d"
,
1000000
);
replies
[
i
]
=
redisCommand
(
c
,
"INCRBY incrkey %d"
,
1000000
);
...
@@ -758,11 +1099,11 @@ static void test_throughput(struct config config) {
...
@@ -758,11 +1099,11 @@ static void test_throughput(struct config config) {
}
}
t2
=
usec
();
t2
=
usec
();
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
free
(
replies
);
hi_
free
(
replies
);
printf
(
"
\t
(%dx INCRBY: %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
printf
(
"
\t
(%dx INCRBY: %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
num
=
10000
;
num
=
10000
;
replies
=
malloc
(
sizeof
(
redisReply
*
)
*
num
);
replies
=
hi_
malloc
_safe
(
sizeof
(
redisReply
*
)
*
num
);
for
(
i
=
0
;
i
<
num
;
i
++
)
for
(
i
=
0
;
i
<
num
;
i
++
)
redisAppendCommand
(
c
,
"PING"
);
redisAppendCommand
(
c
,
"PING"
);
t1
=
usec
();
t1
=
usec
();
...
@@ -772,10 +1113,10 @@ static void test_throughput(struct config config) {
...
@@ -772,10 +1113,10 @@ static void test_throughput(struct config config) {
}
}
t2
=
usec
();
t2
=
usec
();
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
free
(
replies
);
hi_
free
(
replies
);
printf
(
"
\t
(%dx PING (pipelined): %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
printf
(
"
\t
(%dx PING (pipelined): %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
replies
=
malloc
(
sizeof
(
redisReply
*
)
*
num
);
replies
=
hi_
malloc
_safe
(
sizeof
(
redisReply
*
)
*
num
);
for
(
i
=
0
;
i
<
num
;
i
++
)
for
(
i
=
0
;
i
<
num
;
i
++
)
redisAppendCommand
(
c
,
"LRANGE mylist 0 499"
);
redisAppendCommand
(
c
,
"LRANGE mylist 0 499"
);
t1
=
usec
();
t1
=
usec
();
...
@@ -786,10 +1127,10 @@ static void test_throughput(struct config config) {
...
@@ -786,10 +1127,10 @@ static void test_throughput(struct config config) {
}
}
t2
=
usec
();
t2
=
usec
();
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
free
(
replies
);
hi_
free
(
replies
);
printf
(
"
\t
(%dx LRANGE with 500 elements (pipelined): %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
printf
(
"
\t
(%dx LRANGE with 500 elements (pipelined): %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
replies
=
malloc
(
sizeof
(
redisReply
*
)
*
num
);
replies
=
hi_
malloc
_safe
(
sizeof
(
redisReply
*
)
*
num
);
for
(
i
=
0
;
i
<
num
;
i
++
)
for
(
i
=
0
;
i
<
num
;
i
++
)
redisAppendCommand
(
c
,
"INCRBY incrkey %d"
,
1000000
);
redisAppendCommand
(
c
,
"INCRBY incrkey %d"
,
1000000
);
t1
=
usec
();
t1
=
usec
();
...
@@ -799,7 +1140,7 @@ static void test_throughput(struct config config) {
...
@@ -799,7 +1140,7 @@ static void test_throughput(struct config config) {
}
}
t2
=
usec
();
t2
=
usec
();
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
for
(
i
=
0
;
i
<
num
;
i
++
)
freeReplyObject
(
replies
[
i
]);
free
(
replies
);
hi_
free
(
replies
);
printf
(
"
\t
(%dx INCRBY (pipelined): %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
printf
(
"
\t
(%dx INCRBY (pipelined): %.3fs)
\n
"
,
num
,
(
t2
-
t1
)
/
1000000
.
0
);
disconnect
(
c
,
0
);
disconnect
(
c
,
0
);
...
@@ -916,9 +1257,8 @@ int main(int argc, char **argv) {
...
@@ -916,9 +1257,8 @@ int main(int argc, char **argv) {
};
};
int
throughput
=
1
;
int
throughput
=
1
;
int
test_inherit_fd
=
1
;
int
test_inherit_fd
=
1
;
int
skips_as_fails
=
0
;
/* Ignore broken pipe signal (for I/O error tests). */
int
test_unix_socket
;
signal
(
SIGPIPE
,
SIG_IGN
);
/* Parse command line options. */
/* Parse command line options. */
argv
++
;
argc
--
;
argv
++
;
argc
--
;
...
@@ -936,6 +1276,8 @@ int main(int argc, char **argv) {
...
@@ -936,6 +1276,8 @@ int main(int argc, char **argv) {
throughput
=
0
;
throughput
=
0
;
}
else
if
(
argc
>=
1
&&
!
strcmp
(
argv
[
0
],
"--skip-inherit-fd"
))
{
}
else
if
(
argc
>=
1
&&
!
strcmp
(
argv
[
0
],
"--skip-inherit-fd"
))
{
test_inherit_fd
=
0
;
test_inherit_fd
=
0
;
}
else
if
(
argc
>=
1
&&
!
strcmp
(
argv
[
0
],
"--skips-as-fails"
))
{
skips_as_fails
=
1
;
#ifdef HIREDIS_TEST_SSL
#ifdef HIREDIS_TEST_SSL
}
else
if
(
argc
>=
2
&&
!
strcmp
(
argv
[
0
],
"--ssl-port"
))
{
}
else
if
(
argc
>=
2
&&
!
strcmp
(
argv
[
0
],
"--ssl-port"
))
{
argv
++
;
argc
--
;
argv
++
;
argc
--
;
...
@@ -960,6 +1302,19 @@ int main(int argc, char **argv) {
...
@@ -960,6 +1302,19 @@ int main(int argc, char **argv) {
argv
++
;
argc
--
;
argv
++
;
argc
--
;
}
}
#ifndef _WIN32
/* Ignore broken pipe signal (for I/O error tests). */
signal
(
SIGPIPE
,
SIG_IGN
);
test_unix_socket
=
access
(
cfg
.
unix_sock
.
path
,
F_OK
)
==
0
;
#else
/* Unix sockets don't exist in Windows */
test_unix_socket
=
0
;
#endif
test_allocator_injection
();
test_format_commands
();
test_format_commands
();
test_reply_reader
();
test_reply_reader
();
test_blocking_connection_errors
();
test_blocking_connection_errors
();
...
@@ -974,15 +1329,25 @@ int main(int argc, char **argv) {
...
@@ -974,15 +1329,25 @@ int main(int argc, char **argv) {
test_append_formatted_commands
(
cfg
);
test_append_formatted_commands
(
cfg
);
if
(
throughput
)
test_throughput
(
cfg
);
if
(
throughput
)
test_throughput
(
cfg
);
printf
(
"
\n
Testing against Unix socket connection (%s):
\n
"
,
cfg
.
unix_sock
.
path
);
printf
(
"
\n
Testing against Unix socket connection (%s): "
,
cfg
.
unix_sock
.
path
);
cfg
.
type
=
CONN_UNIX
;
if
(
test_unix_socket
)
{
test_blocking_connection
(
cfg
);
printf
(
"
\n
"
);
test_blocking_connection_timeouts
(
cfg
);
cfg
.
type
=
CONN_UNIX
;
test_blocking_io_errors
(
cfg
);
test_blocking_connection
(
cfg
);
if
(
throughput
)
test_throughput
(
cfg
);
test_blocking_connection_timeouts
(
cfg
);
test_blocking_io_errors
(
cfg
);
if
(
throughput
)
test_throughput
(
cfg
);
}
else
{
test_skipped
();
}
#ifdef HIREDIS_TEST_SSL
#ifdef HIREDIS_TEST_SSL
if
(
cfg
.
ssl
.
port
&&
cfg
.
ssl
.
host
)
{
if
(
cfg
.
ssl
.
port
&&
cfg
.
ssl
.
host
)
{
redisInitOpenSSL
();
_ssl_ctx
=
redisCreateSSLContext
(
cfg
.
ssl
.
ca_cert
,
NULL
,
cfg
.
ssl
.
cert
,
cfg
.
ssl
.
key
,
NULL
,
NULL
);
assert
(
_ssl_ctx
!=
NULL
);
printf
(
"
\n
Testing against SSL connection (%s:%d):
\n
"
,
cfg
.
ssl
.
host
,
cfg
.
ssl
.
port
);
printf
(
"
\n
Testing against SSL connection (%s:%d):
\n
"
,
cfg
.
ssl
.
host
,
cfg
.
ssl
.
port
);
cfg
.
type
=
CONN_SSL
;
cfg
.
type
=
CONN_SSL
;
...
@@ -992,21 +1357,31 @@ int main(int argc, char **argv) {
...
@@ -992,21 +1357,31 @@ int main(int argc, char **argv) {
test_invalid_timeout_errors
(
cfg
);
test_invalid_timeout_errors
(
cfg
);
test_append_formatted_commands
(
cfg
);
test_append_formatted_commands
(
cfg
);
if
(
throughput
)
test_throughput
(
cfg
);
if
(
throughput
)
test_throughput
(
cfg
);
redisFreeSSLContext
(
_ssl_ctx
);
_ssl_ctx
=
NULL
;
}
}
#endif
#endif
if
(
test_inherit_fd
)
{
if
(
test_inherit_fd
)
{
printf
(
"
\n
Testing against inherited fd (%s):
\n
"
,
cfg
.
unix_sock
.
path
);
printf
(
"
\n
Testing against inherited fd (%s): "
,
cfg
.
unix_sock
.
path
);
cfg
.
type
=
CONN_FD
;
if
(
test_unix_socket
)
{
test_blocking_connection
(
cfg
);
printf
(
"
\n
"
);
cfg
.
type
=
CONN_FD
;
test_blocking_connection
(
cfg
);
}
else
{
test_skipped
();
}
}
}
if
(
fails
||
(
skips_as_fails
&&
skips
))
{
if
(
fails
)
{
printf
(
"*** %d TESTS FAILED ***
\n
"
,
fails
);
printf
(
"*** %d TESTS FAILED ***
\n
"
,
fails
);
if
(
skips
)
{
printf
(
"*** %d TESTS SKIPPED ***
\n
"
,
skips
);
}
return
1
;
return
1
;
}
}
printf
(
"ALL TESTS PASSED
\n
"
);
printf
(
"ALL TESTS PASSED
(%d skipped)
\n
"
,
skips
);
return
0
;
return
0
;
}
}
deps/hiredis/test.sh
View file @
3f073b1d
...
@@ -4,7 +4,9 @@ REDIS_SERVER=${REDIS_SERVER:-redis-server}
...
@@ -4,7 +4,9 @@ REDIS_SERVER=${REDIS_SERVER:-redis-server}
REDIS_PORT
=
${
REDIS_PORT
:-
56379
}
REDIS_PORT
=
${
REDIS_PORT
:-
56379
}
REDIS_SSL_PORT
=
${
REDIS_SSL_PORT
:-
56443
}
REDIS_SSL_PORT
=
${
REDIS_SSL_PORT
:-
56443
}
TEST_SSL
=
${
TEST_SSL
:-
0
}
TEST_SSL
=
${
TEST_SSL
:-
0
}
SKIPS_AS_FAILS
=
${
SKIPS_AS_FAILS
-
:0
}
SSL_TEST_ARGS
=
SSL_TEST_ARGS
=
SKIPS_ARG
=
tmpdir
=
$(
mktemp
-d
)
tmpdir
=
$(
mktemp
-d
)
PID_FILE
=
${
tmpdir
}
/hiredis-test-redis.pid
PID_FILE
=
${
tmpdir
}
/hiredis-test-redis.pid
...
@@ -67,4 +69,10 @@ fi
...
@@ -67,4 +69,10 @@ fi
cat
${
tmpdir
}
/redis.conf
cat
${
tmpdir
}
/redis.conf
${
REDIS_SERVER
}
${
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
}
# Wait until we detect the unix socket
while
[
!
-S
"
${
SOCK_FILE
}
"
]
;
do
sleep
1
;
done
# Treat skips as failures if directed
[
"
$SKIPS_AS_FAILS
"
=
1
]
&&
SKIPS_ARG
=
"--skips-as-fails"
${
TEST_PREFIX
:-}
./hiredis-test
-h
127.0.0.1
-p
${
REDIS_PORT
}
-s
${
SOCK_FILE
}
${
SSL_TEST_ARGS
}
${
SKIPS_ARG
}
src/redis-cli.c
View file @
3f073b1d
...
@@ -234,6 +234,7 @@ static struct config {
...
@@ -234,6 +234,7 @@ static struct config {
int
askpass
;
int
askpass
;
char
*
user
;
char
*
user
;
int
output
;
/* output mode, see OUTPUT_* defines */
int
output
;
/* output mode, see OUTPUT_* defines */
int
push_output
;
/* Should we display spontaneous PUSH replies */
sds
mb_delim
;
sds
mb_delim
;
char
prompt
[
128
];
char
prompt
[
128
];
char
*
eval
;
char
*
eval
;
...
@@ -267,6 +268,8 @@ static long getLongInfoField(char *info, char *field);
...
@@ -267,6 +268,8 @@ static long getLongInfoField(char *info, char *field);
* Utility functions
* Utility functions
*--------------------------------------------------------------------------- */
*--------------------------------------------------------------------------- */
static
void
cliPushHandler
(
void
*
,
void
*
);
uint16_t
crc16
(
const
char
*
buf
,
int
len
);
uint16_t
crc16
(
const
char
*
buf
,
int
len
);
static
long
long
ustime
(
void
)
{
static
long
long
ustime
(
void
)
{
...
@@ -873,8 +876,8 @@ static int cliConnect(int flags) {
...
@@ -873,8 +876,8 @@ static int cliConnect(int flags) {
const
char
*
err
=
NULL
;
const
char
*
err
=
NULL
;
if
(
cliSecureConnection
(
context
,
&
err
)
==
REDIS_ERR
&&
err
)
{
if
(
cliSecureConnection
(
context
,
&
err
)
==
REDIS_ERR
&&
err
)
{
fprintf
(
stderr
,
"Could not negotiate a TLS connection: %s
\n
"
,
err
);
fprintf
(
stderr
,
"Could not negotiate a TLS connection: %s
\n
"
,
err
);
context
=
NULL
;
redisFree
(
context
);
redisFree
(
context
);
context
=
NULL
;
return
REDIS_ERR
;
return
REDIS_ERR
;
}
}
}
}
...
@@ -909,6 +912,12 @@ static int cliConnect(int flags) {
...
@@ -909,6 +912,12 @@ static int cliConnect(int flags) {
if
(
cliSwitchProto
()
!=
REDIS_OK
)
if
(
cliSwitchProto
()
!=
REDIS_OK
)
return
REDIS_ERR
;
return
REDIS_ERR
;
}
}
/* Set a PUSH handler if configured to do so. */
if
(
config
.
push_output
)
{
redisSetPushCallback
(
context
,
cliPushHandler
);
}
return
REDIS_OK
;
return
REDIS_OK
;
}
}
...
@@ -917,6 +926,31 @@ static void cliPrintContextError(void) {
...
@@ -917,6 +926,31 @@ static void cliPrintContextError(void) {
fprintf
(
stderr
,
"Error: %s
\n
"
,
context
->
errstr
);
fprintf
(
stderr
,
"Error: %s
\n
"
,
context
->
errstr
);
}
}
static
int
isInvalidateReply
(
redisReply
*
reply
)
{
return
reply
->
type
==
REDIS_REPLY_PUSH
&&
reply
->
elements
==
2
&&
reply
->
element
[
0
]
->
type
==
REDIS_REPLY_STRING
&&
!
strncmp
(
reply
->
element
[
0
]
->
str
,
"invalidate"
,
10
)
&&
reply
->
element
[
1
]
->
type
==
REDIS_REPLY_ARRAY
;
}
/* Special display handler for RESP3 'invalidate' messages.
* This function does not validate the reply, so it should
* already be confirmed correct */
static
sds
cliFormatInvalidateTTY
(
redisReply
*
r
)
{
sds
out
=
sdsnew
(
"-> invalidate: "
);
for
(
size_t
i
=
0
;
i
<
r
->
element
[
1
]
->
elements
;
i
++
)
{
redisReply
*
key
=
r
->
element
[
1
]
->
element
[
i
];
assert
(
key
->
type
==
REDIS_REPLY_STRING
);
out
=
sdscatfmt
(
out
,
"'%s'"
,
key
->
str
,
key
->
len
);
if
(
i
<
r
->
element
[
1
]
->
elements
-
1
)
out
=
sdscatlen
(
out
,
", "
,
2
);
}
return
sdscatlen
(
out
,
"
\n
"
,
1
);
}
static
sds
cliFormatReplyTTY
(
redisReply
*
r
,
char
*
prefix
)
{
static
sds
cliFormatReplyTTY
(
redisReply
*
r
,
char
*
prefix
)
{
sds
out
=
sdsempty
();
sds
out
=
sdsempty
();
switch
(
r
->
type
)
{
switch
(
r
->
type
)
{
...
@@ -955,6 +989,7 @@ static sds cliFormatReplyTTY(redisReply *r, char *prefix) {
...
@@ -955,6 +989,7 @@ static sds cliFormatReplyTTY(redisReply *r, char *prefix) {
case
REDIS_REPLY_ARRAY
:
case
REDIS_REPLY_ARRAY
:
case
REDIS_REPLY_MAP
:
case
REDIS_REPLY_MAP
:
case
REDIS_REPLY_SET
:
case
REDIS_REPLY_SET
:
case
REDIS_REPLY_PUSH
:
if
(
r
->
elements
==
0
)
{
if
(
r
->
elements
==
0
)
{
if
(
r
->
type
==
REDIS_REPLY_ARRAY
)
if
(
r
->
type
==
REDIS_REPLY_ARRAY
)
out
=
sdscat
(
out
,
"(empty array)
\n
"
);
out
=
sdscat
(
out
,
"(empty array)
\n
"
);
...
@@ -962,6 +997,8 @@ static sds cliFormatReplyTTY(redisReply *r, char *prefix) {
...
@@ -962,6 +997,8 @@ static sds cliFormatReplyTTY(redisReply *r, char *prefix) {
out
=
sdscat
(
out
,
"(empty hash)
\n
"
);
out
=
sdscat
(
out
,
"(empty hash)
\n
"
);
else
if
(
r
->
type
==
REDIS_REPLY_SET
)
else
if
(
r
->
type
==
REDIS_REPLY_SET
)
out
=
sdscat
(
out
,
"(empty set)
\n
"
);
out
=
sdscat
(
out
,
"(empty set)
\n
"
);
else
if
(
r
->
type
==
REDIS_REPLY_PUSH
)
out
=
sdscat
(
out
,
"(empty push)
\n
"
);
else
else
out
=
sdscat
(
out
,
"(empty aggregate type)
\n
"
);
out
=
sdscat
(
out
,
"(empty aggregate type)
\n
"
);
}
else
{
}
else
{
...
@@ -1113,6 +1150,7 @@ static sds cliFormatReplyRaw(redisReply *r) {
...
@@ -1113,6 +1150,7 @@ static sds cliFormatReplyRaw(redisReply *r) {
out
=
sdscatprintf
(
out
,
"%s"
,
r
->
str
);
out
=
sdscatprintf
(
out
,
"%s"
,
r
->
str
);
break
;
break
;
case
REDIS_REPLY_ARRAY
:
case
REDIS_REPLY_ARRAY
:
case
REDIS_REPLY_PUSH
:
for
(
i
=
0
;
i
<
r
->
elements
;
i
++
)
{
for
(
i
=
0
;
i
<
r
->
elements
;
i
++
)
{
if
(
i
>
0
)
out
=
sdscat
(
out
,
config
.
mb_delim
);
if
(
i
>
0
)
out
=
sdscat
(
out
,
config
.
mb_delim
);
tmp
=
cliFormatReplyRaw
(
r
->
element
[
i
]);
tmp
=
cliFormatReplyRaw
(
r
->
element
[
i
]);
...
@@ -1169,6 +1207,7 @@ static sds cliFormatReplyCSV(redisReply *r) {
...
@@ -1169,6 +1207,7 @@ static sds cliFormatReplyCSV(redisReply *r) {
out
=
sdscat
(
out
,
r
->
integer
?
"true"
:
"false"
);
out
=
sdscat
(
out
,
r
->
integer
?
"true"
:
"false"
);
break
;
break
;
case
REDIS_REPLY_ARRAY
:
case
REDIS_REPLY_ARRAY
:
case
REDIS_REPLY_PUSH
:
case
REDIS_REPLY_MAP
:
/* CSV has no map type, just output flat list. */
case
REDIS_REPLY_MAP
:
/* CSV has no map type, just output flat list. */
for
(
i
=
0
;
i
<
r
->
elements
;
i
++
)
{
for
(
i
=
0
;
i
<
r
->
elements
;
i
++
)
{
sds
tmp
=
cliFormatReplyCSV
(
r
->
element
[
i
]);
sds
tmp
=
cliFormatReplyCSV
(
r
->
element
[
i
]);
...
@@ -1184,6 +1223,45 @@ static sds cliFormatReplyCSV(redisReply *r) {
...
@@ -1184,6 +1223,45 @@ static sds cliFormatReplyCSV(redisReply *r) {
return
out
;
return
out
;
}
}
/* Generate reply strings in various output modes */
static
sds
cliFormatReply
(
redisReply
*
reply
,
int
mode
,
int
verbatim
)
{
sds
out
;
if
(
verbatim
)
{
out
=
cliFormatReplyRaw
(
reply
);
}
else
if
(
mode
==
OUTPUT_STANDARD
)
{
out
=
cliFormatReplyTTY
(
reply
,
""
);
}
else
if
(
mode
==
OUTPUT_RAW
)
{
out
=
cliFormatReplyRaw
(
reply
);
out
=
sdscatlen
(
out
,
"
\n
"
,
1
);
}
else
if
(
mode
==
OUTPUT_CSV
)
{
out
=
cliFormatReplyCSV
(
reply
);
out
=
sdscatlen
(
out
,
"
\n
"
,
1
);
}
else
{
fprintf
(
stderr
,
"Error: Unknown output encoding %d
\n
"
,
mode
);
exit
(
1
);
}
return
out
;
}
/* Output any spontaneous PUSH reply we receive */
static
void
cliPushHandler
(
void
*
privdata
,
void
*
reply
)
{
UNUSED
(
privdata
);
sds
out
;
if
(
config
.
output
==
OUTPUT_STANDARD
&&
isInvalidateReply
(
reply
))
{
out
=
cliFormatInvalidateTTY
(
reply
);
}
else
{
out
=
cliFormatReply
(
reply
,
config
.
output
,
0
);
}
fwrite
(
out
,
sdslen
(
out
),
1
,
stdout
);
freeReplyObject
(
reply
);
sdsfree
(
out
);
}
static
int
cliReadReply
(
int
output_raw_strings
)
{
static
int
cliReadReply
(
int
output_raw_strings
)
{
void
*
_reply
;
void
*
_reply
;
redisReply
*
reply
;
redisReply
*
reply
;
...
@@ -1244,19 +1322,7 @@ static int cliReadReply(int output_raw_strings) {
...
@@ -1244,19 +1322,7 @@ static int cliReadReply(int output_raw_strings) {
}
}
if
(
output
)
{
if
(
output
)
{
if
(
output_raw_strings
)
{
out
=
cliFormatReply
(
reply
,
config
.
output
,
output_raw_strings
);
out
=
cliFormatReplyRaw
(
reply
);
}
else
{
if
(
config
.
output
==
OUTPUT_RAW
)
{
out
=
cliFormatReplyRaw
(
reply
);
out
=
sdscat
(
out
,
"
\n
"
);
}
else
if
(
config
.
output
==
OUTPUT_STANDARD
)
{
out
=
cliFormatReplyTTY
(
reply
,
""
);
}
else
if
(
config
.
output
==
OUTPUT_CSV
)
{
out
=
cliFormatReplyCSV
(
reply
);
out
=
sdscat
(
out
,
"
\n
"
);
}
}
fwrite
(
out
,
sdslen
(
out
),
1
,
stdout
);
fwrite
(
out
,
sdslen
(
out
),
1
,
stdout
);
sdsfree
(
out
);
sdsfree
(
out
);
}
}
...
@@ -1346,6 +1412,10 @@ static int cliSendCommand(int argc, char **argv, long repeat) {
...
@@ -1346,6 +1412,10 @@ static int cliSendCommand(int argc, char **argv, long repeat) {
if
(
config
.
pubsub_mode
)
{
if
(
config
.
pubsub_mode
)
{
if
(
config
.
output
!=
OUTPUT_RAW
)
if
(
config
.
output
!=
OUTPUT_RAW
)
printf
(
"Reading messages... (press Ctrl-C to quit)
\n
"
);
printf
(
"Reading messages... (press Ctrl-C to quit)
\n
"
);
/* Unset our default PUSH handler so this works in RESP2/RESP3 */
redisSetPushCallback
(
context
,
NULL
);
while
(
1
)
{
while
(
1
)
{
if
(
cliReadReply
(
output_raw
)
!=
REDIS_OK
)
exit
(
1
);
if
(
cliReadReply
(
output_raw
)
!=
REDIS_OK
)
exit
(
1
);
}
}
...
@@ -1626,6 +1696,16 @@ static int parseOptions(int argc, char **argv) {
...
@@ -1626,6 +1696,16 @@ static int parseOptions(int argc, char **argv) {
exit
(
0
);
exit
(
0
);
}
else
if
(
!
strcmp
(
argv
[
i
],
"-3"
))
{
}
else
if
(
!
strcmp
(
argv
[
i
],
"-3"
))
{
config
.
resp3
=
1
;
config
.
resp3
=
1
;
}
else
if
(
!
strcmp
(
argv
[
i
],
"--show-pushes"
)
&&
!
lastarg
)
{
char
*
argval
=
argv
[
++
i
];
if
(
!
strncasecmp
(
argval
,
"n"
,
1
))
{
config
.
push_output
=
0
;
}
else
if
(
!
strncasecmp
(
argval
,
"y"
,
1
))
{
config
.
push_output
=
1
;
}
else
{
fprintf
(
stderr
,
"Unknown --show-pushes value '%s' "
"(valid: '[y]es', '[n]o')
\n
"
,
argval
);
}
}
else
if
(
CLUSTER_MANAGER_MODE
()
&&
argv
[
i
][
0
]
!=
'-'
)
{
}
else
if
(
CLUSTER_MANAGER_MODE
()
&&
argv
[
i
][
0
]
!=
'-'
)
{
if
(
config
.
cluster_manager_command
.
argc
==
0
)
{
if
(
config
.
cluster_manager_command
.
argc
==
0
)
{
int
j
=
i
+
1
;
int
j
=
i
+
1
;
...
@@ -1734,6 +1814,8 @@ static void usage(void) {
...
@@ -1734,6 +1814,8 @@ static void usage(void) {
" not a tty).
\n
"
" not a tty).
\n
"
" --no-raw Force formatted output even when STDOUT is not a tty.
\n
"
" --no-raw Force formatted output even when STDOUT is not a tty.
\n
"
" --csv Output in CSV format.
\n
"
" --csv Output in CSV format.
\n
"
" --show-pushes <yn> Whether to print RESP3 PUSH messages. Enabled by default when
\n
"
" STDOUT is a tty but can be overriden with --show-pushes no.
\n
"
" --stat Print rolling stats about server: mem, clients, ...
\n
"
" --stat Print rolling stats about server: mem, clients, ...
\n
"
" --latency Enter a special mode continuously sampling latency.
\n
"
" --latency Enter a special mode continuously sampling latency.
\n
"
" If you use this mode in an interactive session it runs
\n
"
" If you use this mode in an interactive session it runs
\n
"
...
@@ -8063,10 +8145,13 @@ int main(int argc, char **argv) {
...
@@ -8063,10 +8145,13 @@ int main(int argc, char **argv) {
spectrum_palette
=
spectrum_palette_color
;
spectrum_palette
=
spectrum_palette_color
;
spectrum_palette_size
=
spectrum_palette_color_size
;
spectrum_palette_size
=
spectrum_palette_color_size
;
if
(
!
isatty
(
fileno
(
stdout
))
&&
(
getenv
(
"FAKETTY"
)
==
NULL
))
if
(
!
isatty
(
fileno
(
stdout
))
&&
(
getenv
(
"FAKETTY"
)
==
NULL
))
{
config
.
output
=
OUTPUT_RAW
;
config
.
output
=
OUTPUT_RAW
;
else
config
.
push_output
=
0
;
}
else
{
config
.
output
=
OUTPUT_STANDARD
;
config
.
output
=
OUTPUT_STANDARD
;
config
.
push_output
=
1
;
}
config
.
mb_delim
=
sdsnew
(
"
\n
"
);
config
.
mb_delim
=
sdsnew
(
"
\n
"
);
firstarg
=
parseOptions
(
argc
,
argv
);
firstarg
=
parseOptions
(
argc
,
argv
);
...
...
Prev
1
2
3
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment