Code Monkey home page Code Monkey logo

hiredis's Introduction

Build Status

This Readme reflects the latest changed in the master branch. See v1.0.0 for the Readme and documentation for the latest release (API/ABI history).

HIREDIS

Hiredis is a minimalistic C client library for the Redis database.

It is minimalistic because it just adds minimal support for the protocol, but at the same time it uses a high level printf-alike API in order to make it much higher level than otherwise suggested by its minimal code base and the lack of explicit bindings for every Redis command.

Apart from supporting sending commands and receiving replies, it comes with a reply parser that is decoupled from the I/O layer. It is a stream parser designed for easy reusability, which can for instance be used in higher level language bindings for efficient reply parsing.

Hiredis only supports the binary-safe Redis protocol, so you can use it with any Redis version >= 1.2.0.

The library comes with multiple APIs. There is the synchronous API, the asynchronous API and the reply parsing API.

Upgrading to > 1.2.0 (PRERELEASE)

  • After v1.2.0 we modified how we invoke poll(2) to wait for connections to complete, such that we will now retry the call if it is interrupted by a signal until:

    a) The connection succeeds or fails. b) The overall connection timeout is reached.

    In previous versions, an interrupted poll(2) call would cause the connection to fail with c->err set to REDIS_ERR_IO and c->errstr set to poll(2): Interrupted system call.

Upgrading to 1.1.0

Almost all users will simply need to recompile their applications against the newer version of hiredis.

NOTE: Hiredis can now return nan in addition to -inf and inf in a REDIS_REPLY_DOUBLE. Applications that deal with RESP3 doubles should make sure to account for this.

Upgrading to 1.0.2

NOTE: v1.0.1 erroneously bumped SONAME, which is why it is skipped here.

Version 1.0.2 is simply 1.0.0 with a fix for CVE-2021-32765. They are otherwise identical.

Upgrading to 1.0.0

Version 1.0.0 marks the first stable release of Hiredis. It includes some minor breaking changes, mostly to make the exposed API more uniform and self-explanatory. It also bundles the updated sds library, to sync up with upstream and Redis. For code changes see the Changelog.

Note: As described below, a few member names have been changed but most applications should be able to upgrade with minor code changes and recompiling.

IMPORTANT: Breaking changes from 0.14.1 -> 1.0.0

  • redisContext has two additional members (free_privdata, and privctx).
  • redisOptions.timeout has been renamed to redisOptions.connect_timeout, and we've added redisOptions.command_timeout.
  • redisReplyObjectFunctions.createArray now takes size_t instead of int for its length parameter.

IMPORTANT: Breaking changes when upgrading from 0.13.x -> 0.14.x

Bulk and multi-bulk lengths less than -1 or greater than LLONG_MAX are now protocol errors. This is consistent with the RESP specification. On 32-bit platforms, the upper bound is lowered to SIZE_MAX.

Change redisReply.len to size_t, as it denotes the the size of a string

User code should compare this to size_t values as well. If it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before.

Upgrading from <0.9.0

Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing code using hiredis should not be a big pain. The key thing to keep in mind when upgrading is that hiredis >= 0.9.0 uses a redisContext* to keep state, in contrast to the stateless 0.0.1 that only has a file descriptor to work with.

Synchronous API

To consume the synchronous API, there are only a few function calls that need to be introduced:

redisContext *redisConnect(const char *ip, int port);
void *redisCommand(redisContext *c, const char *format, ...);
void freeReplyObject(void *reply);

Connecting

The function redisConnect is used to create a so-called redisContext. The context is where Hiredis holds state for a connection. The redisContext struct has an integer err field that is non-zero when the connection is in an error state. The field errstr will contain a string with a description of the error. More information on errors can be found in the Errors section. After trying to connect to Redis using redisConnect you should check the err field to see if establishing the connection was successful:

redisContext *c = redisConnect("127.0.0.1", 6379);
if (c == NULL || c->err) {
    if (c) {
        printf("Error: %s\n", c->errstr);
        // handle error
    } else {
        printf("Can't allocate redis context\n");
    }
}

One can also use redisConnectWithOptions which takes a redisOptions argument that can be configured with endpoint information as well as many different flags to change how the redisContext will be configured.

redisOptions opt = {0};

/* One can set the endpoint with one of our helper macros */
if (tcp) {
    REDIS_OPTIONS_SET_TCP(&opt, "localhost", 6379);
} else {
    REDIS_OPTIONS_SET_UNIX(&opt, "/tmp/redis.sock");
}

/* And privdata can be specified with another helper */
REDIS_OPTIONS_SET_PRIVDATA(&opt, myPrivData, myPrivDataDtor);

/* Finally various options may be set via the `options` member, as described below */
opt->options |= REDIS_OPT_PREFER_IPV4;

If a connection is lost, int redisReconnect(redisContext *c) can be used to restore the connection using the same endpoint and options as the given context.

Configurable redisOptions flags

There are several flags you may set in the redisOptions struct to change default behavior. You can specify the flags via the redisOptions->options member.

Flag Description
REDIS_OPT_NONBLOCK Tells hiredis to make a non-blocking connection.
REDIS_OPT_REUSEADDR Tells hiredis to set the SO_REUSEADDR socket option
REDIS_OPT_PREFER_IPV4
REDIS_OPT_PREFER_IPV6
REDIS_OPT_PREFER_IP_UNSPEC
Informs hiredis to either prefer IPv4 or IPv6 when invoking getaddrinfo. REDIS_OPT_PREFER_IP_UNSPEC will cause hiredis to specify AF_UNSPEC in the getaddrinfo call, which means both IPv4 and IPv6 addresses will be searched simultaneously.
Hiredis prefers IPv4 by default.
REDIS_OPT_NO_PUSH_AUTOFREE Tells hiredis to not install the default RESP3 PUSH handler (which just intercepts and frees the replies). This is useful in situations where you want to process these messages in-band.
REDIS_OPT_NOAUTOFREEREPLIES ASYNC: tells hiredis not to automatically invoke freeReplyObject after executing the reply callback.
REDIS_OPT_NOAUTOFREE ASYNC: Tells hiredis not to automatically free the redisAsyncContext on connection/communication failure, but only if the user makes an explicit call to redisAsyncDisconnect or redisAsyncFree

Note: A redisContext is not thread-safe.

Other configuration using socket options

The following socket options are applied directly to the underlying socket. The values are not stored in the redisContext, so they are not automatically applied when reconnecting using redisReconnect(). These functions return REDIS_OK on success. On failure, REDIS_ERR is returned and the underlying connection is closed.

To configure these for an asynchronous context (see Asynchronous API below), use ac->c to get the redisContext out of an asyncRedisContext.

int redisEnableKeepAlive(redisContext *c);
int redisEnableKeepAliveWithInterval(redisContext *c, int interval);

Enables TCP keepalive by setting the following socket options (with some variations depending on OS):

  • SO_KEEPALIVE;
  • TCP_KEEPALIVE or TCP_KEEPIDLE, value configurable using the interval parameter, default 15 seconds;
  • TCP_KEEPINTVL set to 1/3 of interval;
  • TCP_KEEPCNT set to 3.
int redisSetTcpUserTimeout(redisContext *c, unsigned int timeout);

Set the TCP_USER_TIMEOUT Linux-specific socket option which is as described in the tcp man page:

When the value is greater than 0, it specifies the maximum amount of time in milliseconds that trans mitted data may remain unacknowledged before TCP will forcibly close the corresponding connection and return ETIMEDOUT to the application. If the option value is specified as 0, TCP will use the system default.

Sending commands

There are several ways to issue commands to Redis. The first that will be introduced is redisCommand. This function takes a format similar to printf. In the simplest form, it is used like this:

reply = redisCommand(context, "SET foo bar");

The specifier %s interpolates a string in the command, and uses strlen to determine the length of the string:

reply = redisCommand(context, "SET foo %s", value);

When you need to pass binary safe strings in a command, the %b specifier can be used. Together with a pointer to the string, it requires a size_t length argument of the string:

reply = redisCommand(context, "SET foo %b", value, (size_t) valuelen);

Internally, Hiredis splits the command in different arguments and will convert it to the protocol used to communicate with Redis. One or more spaces separates arguments, so you can use the specifiers anywhere in an argument:

reply = redisCommand(context, "SET key:%s %s", myid, value);

Using replies

The return value of redisCommand holds a reply when the command was successfully executed. When an error occurs, the return value is NULL and the err field in the context will be set (see section on Errors). Once an error is returned the context cannot be reused and you should set up a new connection.

The standard replies that redisCommand are of the type redisReply. The type field in the redisReply should be used to test what kind of reply was received:

RESP2

  • REDIS_REPLY_STATUS:

    • The command replied with a status reply. The status string can be accessed using reply->str. The length of this string can be accessed using reply->len.
  • REDIS_REPLY_ERROR:

    • The command replied with an error. The error string can be accessed identical to REDIS_REPLY_STATUS.
  • REDIS_REPLY_INTEGER:

    • The command replied with an integer. The integer value can be accessed using the reply->integer field of type long long.
  • REDIS_REPLY_NIL:

    • The command replied with a nil object. There is no data to access.
  • REDIS_REPLY_STRING:

    • A bulk (string) reply. The value of the reply can be accessed using reply->str. The length of this string can be accessed using reply->len.
  • REDIS_REPLY_ARRAY:

    • A multi bulk reply. The number of elements in the multi bulk reply is stored in reply->elements. Every element in the multi bulk reply is a redisReply object as well and can be accessed via reply->element[..index..]. Redis may reply with nested arrays but this is fully supported.

RESP3

Hiredis also supports every new RESP3 data type which are as follows. For more information about the protocol see the RESP3 specification.

  • REDIS_REPLY_DOUBLE:

    • The command replied with a double-precision floating point number. The value is stored as a string in the str member, and can be converted with strtod or similar.
  • REDIS_REPLY_BOOL:

    • A boolean true/false reply. The value is stored in the integer member and will be either 0 or 1.
  • REDIS_REPLY_MAP:

    • An array with the added invariant that there will always be an even number of elements. The MAP is functionally equivalent to REDIS_REPLY_ARRAY except for the previously mentioned invariant.
  • REDIS_REPLY_SET:

    • An array response where each entry is unique. Like the MAP type, the data is identical to an array response except there are no duplicate values.
  • REDIS_REPLY_PUSH:

    • An array that can be generated spontaneously by Redis. This array response will always contain at least two subelements. The first contains the type of PUSH message (e.g. message, or invalidate), and the second being a sub-array with the PUSH payload itself.
  • REDIS_REPLY_ATTR:

    • An array structurally identical to a MAP but intended as meta-data about a reply. As of Redis 6.0.6 this reply type is not used in Redis
  • REDIS_REPLY_BIGNUM:

    • A string representing an arbitrarily large signed or unsigned integer value. The number will be encoded as a string in the str member of redisReply.
  • REDIS_REPLY_VERB:

    • A verbatim string, intended to be presented to the user without modification. The string payload is stored in the str member, and type data is stored in the vtype member (e.g. txt for raw text or md for markdown).

Replies should be freed using the freeReplyObject() function. Note that this function will take care of freeing sub-reply objects contained in arrays and nested arrays, so there is no need for the user to free the sub replies (it is actually harmful and will corrupt the memory).

Important: the current version of hiredis (1.0.0) frees replies when the asynchronous API is used. This means you should not call freeReplyObject when you use this API. The reply is cleaned up by hiredis after the callback returns. We may introduce a flag to make this configurable in future versions of the library.

Cleaning up

To disconnect and free the context the following function can be used:

void redisFree(redisContext *c);

This function immediately closes the socket and then frees the allocations done in creating the context.

Sending commands (continued)

Together with redisCommand, the function redisCommandArgv can be used to issue commands. It has the following prototype:

void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);

It takes the number of arguments argc, an array of strings argv and the lengths of the arguments argvlen. For convenience, argvlen may be set to NULL and the function will use strlen(3) on every argument to determine its length. Obviously, when any of the arguments need to be binary safe, the entire array of lengths argvlen should be provided.

The return value has the same semantic as redisCommand.

Pipelining

To explain how Hiredis supports pipelining in a blocking connection, there needs to be understanding of the internal execution flow.

When any of the functions in the redisCommand family is called, Hiredis first formats the command according to the Redis protocol. The formatted command is then put in the output buffer of the context. This output buffer is dynamic, so it can hold any number of commands. After the command is put in the output buffer, redisGetReply is called. This function has the following two execution paths:

  1. The input buffer is non-empty:
    • Try to parse a single reply from the input buffer and return it
    • If no reply could be parsed, continue at 2
  2. The input buffer is empty:
    • Write the entire output buffer to the socket
    • Read from the socket until a single reply could be parsed

The function redisGetReply is exported as part of the Hiredis API and can be used when a reply is expected on the socket. To pipeline commands, the only thing that needs to be done is filling up the output buffer. For this cause, two commands can be used that are identical to the redisCommand family, apart from not returning a reply:

void redisAppendCommand(redisContext *c, const char *format, ...);
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);

After calling either function one or more times, redisGetReply can be used to receive the subsequent replies. The return value for this function is either REDIS_OK or REDIS_ERR, where the latter means an error occurred while reading a reply. Just as with the other commands, the err field in the context can be used to find out what the cause of this error is.

The following examples shows a simple pipeline (resulting in only a single call to write(2) and a single call to read(2)):

redisReply *reply;
redisAppendCommand(context,"SET foo bar");
redisAppendCommand(context,"GET foo");
redisGetReply(context,(void**)&reply); // reply for SET
freeReplyObject(reply);
redisGetReply(context,(void**)&reply); // reply for GET
freeReplyObject(reply);

This API can also be used to implement a blocking subscriber:

reply = redisCommand(context,"SUBSCRIBE foo");
freeReplyObject(reply);
while(redisGetReply(context,(void *)&reply) == REDIS_OK) {
    // consume message
    freeReplyObject(reply);
}

Errors

When a function call is not successful, depending on the function either NULL or REDIS_ERR is returned. The err field inside the context will be non-zero and set to one of the following constants:

  • REDIS_ERR_IO: There was an I/O error while creating the connection, trying to write to the socket or read from the socket. If you included errno.h in your application, you can use the global errno variable to find out what is wrong.

  • REDIS_ERR_EOF: The server closed the connection which resulted in an empty read.

  • REDIS_ERR_PROTOCOL: There was an error while parsing the protocol.

  • REDIS_ERR_OTHER: Any other error. Currently, it is only used when a specified hostname to connect to cannot be resolved.

In every case, the errstr field in the context will be set to hold a string representation of the error.

Asynchronous API

Hiredis comes with an asynchronous API that works easily with any event library. Examples are bundled that show using Hiredis with libev and libevent.

Connecting

The function redisAsyncConnect can be used to establish a non-blocking connection to Redis. It returns a pointer to the newly created redisAsyncContext struct. The err field should be checked after creation to see if there were errors creating the connection. Because the connection that will be created is non-blocking, the kernel is not able to instantly return if the specified host and port is able to accept a connection. In case of error, it is the caller's responsibility to free the context using redisAsyncFree()

Note: A redisAsyncContext is not thread-safe.

An application function creating a connection might look like this:

void appConnect(myAppData *appData)
{
    redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
    if (c->err) {
        printf("Error: %s\n", c->errstr);
        // handle error
        redisAsyncFree(c);
        c = NULL;
    } else {
        appData->context = c;
        appData->connecting = 1;
        c->data = appData; /* store application pointer for the callbacks */
        redisAsyncSetConnectCallback(c, appOnConnect);
        redisAsyncSetDisconnectCallback(c, appOnDisconnect);
    }
}

The asynchronous context should hold a connect callback function that is called when the connection attempt completes, either successfully or with an error. It can also hold a disconnect callback function that is called when the connection is disconnected (either because of an error or per user request). Both callbacks should have the following prototype:

void(const redisAsyncContext *c, int status);

On a connect, the status argument is set to REDIS_OK if the connection attempt succeeded. In this case, the context is ready to accept commands. If it is called with REDIS_ERR then the connection attempt failed. The err field in the context can be accessed to find out the cause of the error. After a failed connection attempt, the context object is automatically freed by the library after calling the connect callback. This may be a good point to create a new context and retry the connection.

On a disconnect, the status argument is set to REDIS_OK when disconnection was initiated by the user, or REDIS_ERR when the disconnection was caused by an error. When it is REDIS_ERR, the err field in the context can be accessed to find out the cause of the error.

The context object is always freed after the disconnect callback fired. When a reconnect is needed, the disconnect callback is a good point to do so.

Setting the connect or disconnect callbacks can only be done once per context. For subsequent calls the api will return REDIS_ERR. The function to set the callbacks have the following prototype:

/* Alternatively you can use redisAsyncSetConnectCallbackNC which will be passed a non-const
   redisAsyncContext* on invocation (e.g. allowing writes to the privdata member). */
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);

ac->data may be used to pass user data to both callbacks. A typical implementation might look something like this:

void appOnConnect(redisAsyncContext *c, int status)
{
    myAppData *appData = (myAppData*)c->data; /* get my application specific context*/
    appData->connecting = 0;
    if (status == REDIS_OK) {
        appData->connected = 1;
    } else {
        appData->connected = 0;
        appData->err = c->err;
        appData->context = NULL; /* avoid stale pointer when callback returns */
    }
    appAttemptReconnect();
}

void appOnDisconnect(redisAsyncContext *c, int status)
{
    myAppData *appData = (myAppData*)c->data; /* get my application specific context*/
    appData->connected = 0;
    appData->err = c->err;
    appData->context = NULL; /* avoid stale pointer when callback returns */
    if (status == REDIS_OK) {
        appNotifyDisconnectCompleted(mydata);
    } else {
        appNotifyUnexpectedDisconnect(mydata);
        appAttemptReconnect();
    }
}

Sending commands and their callbacks

In an asynchronous context, commands are automatically pipelined due to the nature of an event loop. Therefore, unlike the synchronous API, there is only a single way to send commands. Because commands are sent to Redis asynchronously, issuing a command requires a callback function that is called when the reply is received. Reply callbacks should have the following prototype:

void(redisAsyncContext *c, void *reply, void *privdata);

The privdata argument can be used to curry arbitrary data to the callback from the point where the command is initially queued for execution.

The functions that can be used to issue commands in an asynchronous context are:

int redisAsyncCommand(
  redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
  const char *format, ...);
int redisAsyncCommandArgv(
  redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
  int argc, const char **argv, const size_t *argvlen);

Both functions work like their blocking counterparts. The return value is REDIS_OK when the command was successfully added to the output buffer and REDIS_ERR otherwise. Example: when the connection is being disconnected per user-request, no new commands may be added to the output buffer and REDIS_ERR is returned on calls to the redisAsyncCommand family.

If the reply for a command with a NULL callback is read, it is immediately freed. When the callback for a command is non-NULL, the memory is freed immediately following the callback: the reply is only valid for the duration of the callback.

All pending callbacks are called with a NULL reply when the context encountered an error.

For every command issued, with the exception of SUBSCRIBE and PSUBSCRIBE, the callback is called exactly once. Even if the context object id disconnected or deleted, every pending callback will be called with a NULL reply.

For SUBSCRIBE and PSUBSCRIBE, the callbacks may be called repeatedly until an unsubscribe message arrives. This will be the last invocation of the callback. In case of error, the callbacks may receive a final NULL reply instead.

Disconnecting

An asynchronous connection can be terminated using:

void redisAsyncDisconnect(redisAsyncContext *ac);

When this function is called, the connection is not immediately terminated. Instead, new commands are no longer accepted and the connection is only terminated when all pending commands have been written to the socket, their respective replies have been read and their respective callbacks have been executed. After this, the disconnection callback is executed with the REDIS_OK status and the context object is freed.

The connection can be forcefully disconnected using

void redisAsyncFree(redisAsyncContext *ac);

In this case, nothing more is written to the socket, all pending callbacks are called with a NULL reply and the disconnection callback is called with REDIS_OK, after which the context object is freed.

Hooking it up to event library X

There are a few hooks that need to be set on the context object after it is created. See the adapters/ directory for bindings to libev and libevent.

Reply parsing API

Hiredis comes with a reply parsing API that makes it easy for writing higher level language bindings.

The reply parsing API consists of the following functions:

redisReader *redisReaderCreate(void);
void redisReaderFree(redisReader *reader);
int redisReaderFeed(redisReader *reader, const char *buf, size_t len);
int redisReaderGetReply(redisReader *reader, void **reply);

The same set of functions are used internally by hiredis when creating a normal Redis context, the above API just exposes it to the user for a direct usage.

Usage

The function redisReaderCreate creates a redisReader structure that holds a buffer with unparsed data and state for the protocol parser.

Incoming data -- most likely from a socket -- can be placed in the internal buffer of the redisReader using redisReaderFeed. This function will make a copy of the buffer pointed to by buf for len bytes. This data is parsed when redisReaderGetReply is called. This function returns an integer status and a reply object (as described above) via void **reply. The returned status can be either REDIS_OK or REDIS_ERR, where the latter means something went wrong (either a protocol error, or an out of memory error).

The parser limits the level of nesting for multi bulk payloads to 7. If the multi bulk nesting level is higher than this, the parser returns an error.

Customizing replies

The function redisReaderGetReply creates redisReply and makes the function argument reply point to the created redisReply variable. For instance, if the response of type REDIS_REPLY_STATUS then the str field of redisReply will hold the status as a vanilla C string. However, the functions that are responsible for creating instances of the redisReply can be customized by setting the fn field on the redisReader struct. This should be done immediately after creating the redisReader.

For example, hiredis-rb uses customized reply object functions to create Ruby objects.

Reader max buffer

Both when using the Reader API directly or when using it indirectly via a normal Redis context, the redisReader structure uses a buffer in order to accumulate data from the server. Usually this buffer is destroyed when it is empty and is larger than 16 KiB in order to avoid wasting memory in unused buffers

However when working with very big payloads destroying the buffer may slow down performances considerably, so it is possible to modify the max size of an idle buffer changing the value of the maxbuf field of the reader structure to the desired value. The special value of 0 means that there is no maximum value for an idle buffer, so the buffer will never get freed.

For instance if you have a normal Redis context you can set the maximum idle buffer to zero (unlimited) just with:

context->reader->maxbuf = 0;

This should be done only in order to maximize performances when working with large payloads. The context should be set back to REDIS_READER_MAX_BUF again as soon as possible in order to prevent allocation of useless memory.

Reader max array elements

By default the hiredis reply parser sets the maximum number of multi-bulk elements to 2^32 - 1 or 4,294,967,295 entries. If you need to process multi-bulk replies with more than this many elements you can set the value higher or to zero, meaning unlimited with:

context->reader->maxelements = 0;

SSL/TLS Support

Building

SSL/TLS support is not built by default and requires an explicit flag:

make USE_SSL=1

This requires OpenSSL development package (e.g. including header files to be available.

When enabled, SSL/TLS support is built into extra libhiredis_ssl.a and libhiredis_ssl.so static/dynamic libraries. This leaves the original libraries unaffected so no additional dependencies are introduced.

Using it

First, you'll need to make sure you include the SSL header file:

#include <hiredis/hiredis.h>
#include <hiredis/hiredis_ssl.h>

You will also need to link against libhiredis_ssl, in addition to libhiredis and add -lssl -lcrypto to satisfy its dependencies.

Hiredis implements SSL/TLS on top of its normal redisContext or redisAsyncContext, so you will need to establish a connection first and then initiate an SSL/TLS handshake.

Hiredis OpenSSL Wrappers

Before Hiredis can negotiate an SSL/TLS connection, it is necessary to initialize OpenSSL and create a context. You can do that in two ways:

  1. Work directly with the OpenSSL API to initialize the library's global context and create SSL_CTX * and SSL * contexts. With an SSL * object you can call redisInitiateSSL().
  2. Work with a set of Hiredis-provided wrappers around OpenSSL, create a redisSSLContext object to hold configuration and use redisInitiateSSLWithContext() to initiate the SSL/TLS handshake.
/* An Hiredis SSL context. It holds SSL configuration and can be reused across
 * many contexts.
 */
redisSSLContext *ssl_context;

/* An error variable to indicate what went wrong, if the context fails to
 * initialize.
 */
redisSSLContextError ssl_error = REDIS_SSL_CTX_NONE;

/* Initialize global OpenSSL state.
 *
 * You should call this only once when your app initializes, and only if
 * you don't explicitly or implicitly initialize OpenSSL it elsewhere.
 */
redisInitOpenSSL();

/* Create SSL context */
ssl_context = redisCreateSSLContext(
    "cacertbundle.crt",     /* File name of trusted CA/ca bundle file, optional */
    "/path/to/certs",       /* Path of trusted certificates, optional */
    "client_cert.pem",      /* File name of client certificate file, optional */
    "client_key.pem",       /* File name of client private key, optional */
    "redis.mydomain.com",   /* Server name to request (SNI), optional */
    &ssl_error);

if(ssl_context == NULL || ssl_error != REDIS_SSL_CTX_NONE) {
    /* Handle error and abort... */
    /* e.g.
    printf("SSL error: %s\n",
        (ssl_error != REDIS_SSL_CTX_NONE) ?
            redisSSLContextGetError(ssl_error) : "Unknown error");
    // Abort
    */
}

/* Create Redis context and establish connection */
c = redisConnect("localhost", 6443);
if (c == NULL || c->err) {
    /* Handle error and abort... */
}

/* Negotiate SSL/TLS */
if (redisInitiateSSLWithContext(c, ssl_context) != REDIS_OK) {
    /* Handle error, in c->err / c->errstr */
}

RESP3 PUSH replies

Redis 6.0 introduced PUSH replies with the reply-type >. These messages are generated spontaneously and can arrive at any time, so must be handled using callbacks.

Default behavior

Hiredis installs handlers on redisContext and redisAsyncContext by default, which will intercept and free any PUSH replies detected. This means existing code will work as-is after upgrading to Redis 6 and switching to RESP3.

Custom PUSH handler prototypes

The callback prototypes differ between redisContext and redisAsyncContext.

redisContext

void my_push_handler(void *privdata, void *reply) {
    /* Handle the reply */

    /* Note: We need to free the reply in our custom handler for
             blocking contexts.  This lets us keep the reply if
             we want. */
    freeReplyObject(reply);
}

redisAsyncContext

void my_async_push_handler(redisAsyncContext *ac, void *reply) {
    /* Handle the reply */

    /* Note:  Because async hiredis always frees replies, you should
              not call freeReplyObject in an async push callback. */
}

Installing a custom handler

There are two ways to set your own PUSH handlers.

  1. Set push_cb or async_push_cb in the redisOptions struct and connect with redisConnectWithOptions or redisAsyncConnectWithOptions.

    redisOptions = {0};
    REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
    options->push_cb = my_push_handler;
    redisContext *context = redisConnectWithOptions(&options);
  2. Call redisSetPushCallback or redisAsyncSetPushCallback on a connected context.

    redisContext *context = redisConnect("127.0.0.1", 6379);
    redisSetPushCallback(context, my_push_handler);

    Note redisSetPushCallback and redisAsyncSetPushCallback both return any currently configured handler, making it easy to override and then return to the old value.

Specifying no handler

If you have a unique use-case where you don't want hiredis to automatically intercept and free PUSH replies, you will want to configure no handler at all. This can be done in two ways.

  1. Set the REDIS_OPT_NO_PUSH_AUTOFREE flag in redisOptions and leave the callback function pointer NULL.

    redisOptions = {0};
    REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
    options->options |= REDIS_OPT_NO_PUSH_AUTOFREE;
    redisContext *context = redisConnectWithOptions(&options);
  2. Call redisSetPushCallback with NULL once connected.

    redisContext *context = redisConnect("127.0.0.1", 6379);
    redisSetPushCallback(context, NULL);

    Note: With no handler configured, calls to redisCommand may generate more than one reply, so this strategy is only applicable when there's some kind of blocking redisGetReply() loop (e.g. MONITOR or SUBSCRIBE workloads).

Allocator injection

Hiredis uses a pass-thru structure of function pointers defined in alloc.h that contain the currently configured allocation and deallocation functions. By default they just point to libc (malloc, calloc, realloc, etc).

Overriding

One can override the allocators like so:

hiredisAllocFuncs myfuncs = {
    .mallocFn = my_malloc,
    .callocFn = my_calloc,
    .reallocFn = my_realloc,
    .strdupFn = my_strdup,
    .freeFn = my_free,
};

// Override allocators (function returns current allocators if needed)
hiredisAllocFuncs orig = hiredisSetAllocators(&myfuncs);

To reset the allocators to their default libc function simply call:

hiredisResetAllocators();

AUTHORS

Salvatore Sanfilippo (antirez at gmail),
Pieter Noordhuis (pcnoordhuis at gmail)
Michael Grunder (michael dot grunder at gmail)

Hiredis is released under the BSD license.

hiredis's People

Contributors

alexsmith1612 avatar antirez avatar badboy avatar bjosv avatar charsyam avatar chayim avatar dragonation avatar erikdubbelboer avatar gahr avatar geoffgarside avatar hyjin avatar justinbrewer avatar kristjanvalur avatar masariello avatar mattsta avatar mbitsnbites avatar michael-grunder avatar mnunberg avatar nanxiao avatar not-a-robot[bot] avatar pietern avatar priteau avatar ryandesign avatar sukkaw avatar sundb avatar thomaslee avatar tnm avatar valentinogeron avatar yossigo avatar zuiderkwast avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hiredis's Issues

exit() on OOM

Hi

In a few places, if hiredis gets out of memory, it calls exit()

This is unlikely, however since hiredis is a library, I'd like it if program control is dictated by the caller.

I think functions that OOM should just return failure to the user. Alternatively, the user at init time can supply a pointer to his own OOM function to be called.

I'd prefer the first approach, and don't mind implementing it if all's in consensus.

Thoughts welcome.

Memory leak in sdssplitlen()

Hello,

There is a potential memory leak in sdsplitlen(), at about line 285 of sds.c :

 if (seplen < 1 || len < 0 || tokens == NULL) return NULL;

When tokens is not NULL (malloc succeeded), this leaks the data allocated.

Gaél.

The usage of freeReplyObject in async callback

This causes spontaneous app crashes in random places - there are really no any clue of where memory corrupts. I've spent whole day before reached to freeReplyObject in my async callback handler and only after that looked into your code - reply is freed internally using freeObject. This is not mentioned anywhere in docs. Docs just states:

Replies should be freed using the freeReplyObject() function.

without any notification of how dramatic this can be in an async callback. So please update docs appropriately.

redisFormatCommand with conditional arguments

redisFormatCommand does not allow me to do something like this:

redisFormatCommand(&cmd, "HMSET %b x %b%s%b", key, key_size, data1, data1_size, data2_size ? " y " : "", data2, data2_size);

It is generating the following command:

*4
$5
HMSET
$3
key
$1
x
$7
d1 y d2

If data2_size != 0 I want the command to be like this:

*6
$5
HMSET
$3
key
$1
x
$2
d1
$1
y
$2
d2

On the other hand, if data2_size == 0, I want the command to be like this:

*4
$5
HMSET
$3
key
$1
x
$2
d1

Is it possible? Currently I'm sending the "y" field with null value, but whis is not memory efficient, since my application dont care about fields with empty values.

redisFormatCommand(&cmd, "HMSET %b x %b y %b", key, key_size, data1, data1_size, data2, data2_size);

The above call works, but wastes memory.

pkgconfig/cmake files

The hiredis package would benefit immensely from including pkgconfig/cmake files for compatibility with autotools. These files will probaly need to be generated during the install process.

Hiredis treats string with multiple fields as a single element

I'm calling the pipelined functions of Hiredis 0.9.2 directly from C. The Redis command that I am attempting to assemble looks like this:

 HMGET udp:4b e d c b a a b c d e

I should get an array reply with 10 elements. I can confirm this using redis-cli.

When I call:

 redisAppendCommand(handle,"HMGET udp:%02x e d c b a a b c d e", hexval);

I get the correct response of 10 elements.

When I try to pre-assemble the hash fields that I want in a string, then pass the string to redisAppendCommand:

 snprintf(buf, 25, "e d c b a a b c d e");
 redisAppendCommand(handle,"HMGET udp:%02x %s", hexval, buf);

I get an array response with a single element.

Looking at the output buffer, the first case (working) yields:

 0x16aec18 "*12\\r\\n$5\\r\\nHMGET\\r\\n$6\\r\\nudp:4b\\r\\n$1\\r\\ne\\r\\n$1\\r\\nd\\r\\n$1\\r\\nc\\r\\n$1\\r\\nb\\r\\n$1\\r\\na\\r\\n$1\\r\\na\\r\\n$1\\r\\nb\\r\\n$1\\r\\nc\\r\\n$1\\r\\nd\\r\\n$1\\r\\ne\\r\\n"

The second (not working) case shows:

 0x16eb578 "*3\\r\\n$5\\r\\nHMGET\\r\\n$6\\r\\nudp:4b\\r\\n$19\\r\\ne d c b a a b c d e\\r\\n"

It appears that Hiredis treats the string as a single element. Is this expected behavior? If so, what formatting should I insert in the string to force Hiredis to recognize it as multiple hash fields?

Cheers,

Dean

Possible memory leak on zero sized array return

Forgive me if I am confused here, as my low level C skills are a bit rusty, but I believe there is a possible memory leak in createArrayObject, when allocating for a return with zero elements.

in createArrayObject, the allocation happens like this:

r->element = calloc(elements,sizeof(redisReply*));

If memory serves me, calloc may still give you a pointer even in the case of size = 0, as it does on my system (Ubuntu)

Then, in freeReplyObject, the deallocation (for an array), happens like so:

case REDIS_REPLY_ARRAY:
    if (r->elements > 0 && r->element != NULL) {
        for (j = 0; j < r->elements; j++)
            if (r->element[j] != NULL)
                freeReplyObject(r->element[j]);
        free(r->element);
    }
    break;

So it appears, that it is only calling free(r->element), if elements>0, but I think that needs to be called even if calloc was called with a zero size, or at least if calloc returns a non null pointer, no matter the value of elements. Perhaps this instead?

case REDIS_REPLY_ARRAY:
    if (r->elements > 0 && r->element != NULL) {
        for (j = 0; j < r->elements; j++)
            if (r->element[j] != NULL)
                freeReplyObject(r->element[j]);
    }
    if(r->element != NULL) free(r->element);
    break;

I noticed this, because valgrind complains about the allocation, and in a test program, also notices this issue:

int main(void) {
    int *ptr = (int*)calloc(0, sizeof(int));
    // free(ptr);  /* uncomment this line and leak is not detected */
}

I apologize if I am missing something, and it must be said that Redis and hiredis are amazing. Truly great stuff! :)

Can't build on macosx 10.7

Hi! I'm getting trouble installing package on Leon.
....
Running setup.py install for hiredis
building 'hiredis' library
/Developer/usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -O2 -fwrapv -DNDEBUG -g -O3 -Wall -Wstrict-prototypes -Ivendor/hiredis -c vendor/hiredis/hiredis.c -o build/temp.macosx-10.7-x86_64-2.7/vendor/hiredis/hiredis.o
vendor/hiredis/hiredis.c:691:21: error: second argument to 'va_arg' is of incomplete type 'void'
va_arg(ap,void);
^~~~~~~~~~~~~~~
/Developer/usr/bin/../lib/clang/3.0/include/stdarg.h:35:50: note: instantiated from:
#define va_arg(ap, type) builtin_va_arg(ap, type)
^
vendor/hiredis/hiredis.c:691:31: note: instantiated from:
va_arg(ap,void);
^~~~
1 error generated.
error: command '/Developer/usr/bin/clang' failed with exit status 1
Complete output from command /Users/demon/Work/sportlook.ru/env/bin/python -c "import setuptools;__file
='/Users/demon/Work/sportlook.ru/env/build/hiredis/setup.py';exec(compile(open(file).read().replace('\r\n', '\n'), file, 'exec'))" install --single-version-externally-managed --record /var/folders/s5/3z_j0lyx3r9__09nfn7vb7wh0000gn/T/pip-4asypi-record/install-record.txt --install-headers /Users/demon/Work/sportlook.ru/env/bin/../include/site/python2.7:
running install

running build

running build_py

creating build

creating build/lib.macosx-10.7-x86_64-2.7

creating build/lib.macosx-10.7-x86_64-2.7/hiredis

copying hiredis/init.py -> build/lib.macosx-10.7-x86_64-2.7/hiredis

copying hiredis/version.py -> build/lib.macosx-10.7-x86_64-2.7/hiredis

running build_clib

building 'hiredis' library

creating build/temp.macosx-10.7-x86_64-2.7

creating build/temp.macosx-10.7-x86_64-2.7/vendor

creating build/temp.macosx-10.7-x86_64-2.7/vendor/hiredis

/Developer/usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -O2 -fwrapv -DNDEBUG -g -O3 -Wall -Wstrict-prototypes -Ivendor/hiredis -c vendor/hiredis/hiredis.c -o build/temp.macosx-10.7-x86_64-2.7/vendor/hiredis/hiredis.o

vendor/hiredis/hiredis.c:691:21: error: second argument to 'va_arg' is of incomplete type 'void'

                va_arg(ap,void);

                ^~~~~~~~~~~~~~~

/Developer/usr/bin/../lib/clang/3.0/include/stdarg.h:35:50: note: instantiated from:

define va_arg(ap, type) __builtin_va_arg(ap, type)

                                             ^

vendor/hiredis/hiredis.c:691:31: note: instantiated from:

                va_arg(ap,void);

                          ^~~~

1 error generated.

error: command '/Developer/usr/bin/clang' failed with exit status 1


Command /Users/demon/Work/sportlook.ru/env/bin/python -c "import setuptools;file='/Users/demon/Work/sportlook.ru/env/build/hiredis/setup.py';exec(compile(open(file).read().replace('\r\n', '\n'), file, 'exec'))" install --single-version-externally-managed --record /var/folders/s5/3z_j0lyx3r9__09nfn7vb7wh0000gn/T/pip-4asypi-record/install-record.txt --install-headers /Users/demon/Work/sportlook.ru/env/bin/../include/site/python2.7 failed with error code 1
Storing complete log in /Users/demon/.pip/pip.log

double free or corruption

My system does more than 100 million redis HGETs a day. It does that like this:

getReply(const char* format, ...)
{
va_start(ap, format);
redisvCommand(c, format, ap);
va_end(ap);
}

getReply("HGET %b %s", key, keyLen, field);

But once or twice a day I get some errors, like these ones:

*** glibc detected *** /usr/sbin/httpd: free(): invalid pointer: 0x00000039f4f53a30 ***
======= Backtrace: =========
/lib64/libc.so.6[0x39f4c7245f]
/lib64/libc.so.6(cfree+0x4b)[0x39f4c728bb]
/usr/local/lib/libhiredis.so.0.10(redisFree+0x24)[0x2b97f92c0d64]

*** glibc detected *** /usr/sbin/httpd: double free or corruption (!prev): 0x000000001e616160 ***
======= Backtrace: =========
/lib64/libc.so.6[0x3b2467245f]
/lib64/libc.so.6(cfree+0x4b)[0x3b246728bb]
/usr/local/lib/libhiredis.so.0.10(redisReaderFree+0x38)[0x2ad219afec18]
/usr/local/lib/libhiredis.so.0.10(redisFree+0x35)[0x2ad219afed75]

*** glibc detected *** /usr/sbin/httpd: double free or corruption (fasttop): 0x0000000011053ef0 ***
======= Backtrace: =========
/lib64/libc.so.6[0x39f4c7245f]
/lib64/libc.so.6(cfree+0x4b)[0x39f4c728bb]
/usr/local/lib/libhiredis.so.0.10(redisBufferWrite+0xca)[0x2b97f92c105a]
/usr/local/lib/libhiredis.so.0.10(redisGetReply+0xc2)[0x2b97f92c3172]
/usr/local/lib/libhiredis.so.0.10(redisvCommand+0x2f)[0x2b97f92c31bf]

Any clue about what is happening?
Im using the latest hiredis code (hiredis-v0.10.0-3).

difficulty building on aix - cannot locate struct timeval

receive the following message:

In file included from hiredis/hiredis.c:39:
hiredis/hiredis.h:175: warning: 'struct timeval' declared inside parameter list
hiredis/hiredis.h:175: warning: its scope is only this definition or declaration, which is probably not what you want

I am using gcc 4.4 on aix 5.3.

make network I/O non-blocking

Have you considered making the read/writes non-blocking?

I'd love to be able to use this in a C-based project but it cannot block. I know Redis is fast but still... :)

Something as simple as select(2) would work for readability/writability testing since there's just the one client socket.

Thoughts?

C++ Wrapper on hiredis

hi salvatore,

i'm working on a c++ wrapper class upon hiredis (using stl and boost).
i had to make some typecast in the headerfiles of hiredis and leave some compiler options
(new commandline: gcc -fPIC -Wall -Wwrite-strings -O2 -c ...)
do you think this could become problematic?

regards

kappe

BRPOPLPUSH Segmentation fault on server disconnect

When a server closes when the BRPOPLPUSH command is executed, and the server is closed, it will exit with an Segmentation Fault, I think this should be handled more discretely.

reply = (redisReply*)redisCommand(m_external->m_redis_context, "BRPOPLPUSH queuenotexistinga queuealsonotexistingb 0");
printf("Will never come here");

After you've come at this point, just close the server.

redisFormatCommand unable to parse empty strings

From test.c:

test("Format command with %%b and an empty string: ");
len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"",0);

The above works, but the following dont:

test("Format command with %%b and an empty string: ");
len = redisFormatCommand(&cmd,"SET %b %b","",0,"foo",3);

This way the test fails. Looks like an empty string can only be placed at the end of the command line.

Build on 64-bit machine

Please find a patch below to build hiredis on 64-bit machines where the PREFIX is /usr/lib64 instead of just /usr/lib.

diff -up antirez-hiredis-0fbfa45/Makefile.64 antirez-hiredis-0fbfa45/Makefile
--- antirez-hiredis-0fbfa45/Makefile.64 2011-04-19 14:05:46.585438998 +0530
+++ antirez-hiredis-0fbfa45/Makefile 2011-04-19 14:07:28.776439002 +0530
@@ -38,7 +38,11 @@ DEBUG?= -g -ggdb

PREFIX?= /usr/local
INSTALL_INC= $(PREFIX)/include/hiredis
+ifeq ($(ENABLE_64BIT),1)
+INSTALL_LIB= $(PREFIX)/lib64
+else
INSTALL_LIB= $(PREFIX)/lib
+endif
INSTALL= cp -a

all: ${DYLIBNAME} ${BINS}

add private data to redisAsyncSetConnectCallback() and redisAsyncSetDisconnectCallback()

Is it possible to pass a pointer to some private data to redisAsyncSetConnectCallback() and redisAsyncSetDisconnectCallback(), so that my callback is called with this pointer?

If not, is it possible to implement it in future versions of hiredis?

I have multiple async connections and some related data to each connection. But I have no ideia which one has been connected or disconnected. Therefore, I cant link a connection to its data at connection / disconnection.

I know I can implement some fancy code that discovers which data is the right one through the connection's file descriptor, but it seems way to complex for my needs.

Repeated failures of redisAsyncCommandArgv with libev SIGSEGVs

Program received signal SIGSEGV, Segmentation fault.                                              
0x0000000000401f68 in redisLibevAddWrite (privdata=0x71) at /home/tyler/source/lookout/git/       cloudpushd/reddy/..//hiredis/adapters/libev.h:56
56          struct ev_loop *loop = e->loop;
(gdb) bt
#0  0x0000000000401f68 in redisLibevAddWrite (privdata=0x71) at /home/tyler/source/lookout/git/   cloudpushd/reddy/..//hiredis/adapters/libev.h:56
#1  0x00000000004053ed in __redisAsyncCommand (ac=<value optimized out>, fn=<value optimized      out>, privdata=<value optimized out>, cmd=<value optimized out>, len=<value optimized out>) at async. c:520
#2  0x0000000000405539 in redisAsyncCommandArgv (ac=<value optimized out>, fn=Unhandled dwarf     expression opcode 0x0
) at async.c:549
#3  0x0000000000402687 in reddyRequeueableCommand (context=0x614810, commands=0x614280, count=1,  callback=0, data=0x0, internal=true) at reddy.c:130
#4  0x000000000040331a in reconnect (loop=0x613930, watcher=0x614820, revents=256) at reddy.c:64
#5  0x000000000040aa7b in ev_invoke_pending (loop=<value optimized out>) at ev.c:2117
#6  0x000000000040b443 in ev_run (loop=<value optimized out>, flags=<value optimized out>) at ev. c:2483
#7  0x00000000004025bb in ev_loop (loop=0x613930, flags=0) at /home/tyler/source/lookout/git/     cloudpushd/reddy/../libev//ev.h:810
#8  0x000000000040257f in main (argc=1, argv=0x7fffffffca48) at test.c:71
(gdb) quit

I'm working on wrapping hiredis with some reconnect/requeue logic for our application specific needs. Inside of the reconnect ev_timer we will create a new char** and try to execute a PING command. In the case where the server is remaining down and hasn't come back up, we will immediately receive a REDIS_ERR from the redisAsyncCommandArgv, free the char** and exit.

After some number of these reconnect loop round-trips, the program will SIGSEGV with the above backtrace.

Somehow switching to a call to redisAsyncCommand has "solved the issue" insofar that I've gone for almost 15 minutes with numerous failed PING commands and no SIGSEGVs

Can't compile on Mac OS X

Whenever I try to compile on Mac OS X (10.6.5) I get the following output from make:

libtool -dynamic -o libhiredis.dylib -lm -g -ggdb  - net.o hiredis.o sds.o async.o
libtool: unrecognized option `-dynamic'
libtool: Try `libtool --help' for more information.
make: *** [libhiredis.dylib] Error 1

I have the latest hiredis from source, and the latest libtool from source as well. I have also tried from both hiredis tags, with the same result.

Missing license and copyright statements in some file

Some files are missing the usual copyright and license headers: adapters/*.h, example*.c, test.c. While it's not a serious issue, having those headers helps downstream distributors to assure correct copyright and licensing information for their packages. Would you mind adding them?

redisvFormatCommand relies on undefined behavior of va_arg(3)

Newer builds of the LLVM clang compiler will vomit on this statement:

    clang -std=c99 -pedantic -c -O3 -fPIC  -Wall -W -Wstrict-prototypes -Wwrite-strings -g -ggdb hiredis.c
    hiredis.c:799:21: error: second argument to 'va_arg' is of incomplete type 'void'
                        va_arg(ap,void);
                        ^~~~~~~~~~~~~~~
    /usr/bin/../lib64/clang/3.0/include/stdarg.h:35:50: note: instantiated from:
    #define va_arg(ap, type)    __builtin_va_arg(ap, type)
                                                    ^
    hiredis.c:799:31: note: instantiated from:
                        va_arg(ap,void);
                                ^~~~
    1 error generated.

I'm not sure what a proper resolution would be, changing the type to void * obviously corrects it, but that will only perform as expected provided all the args on the va_list are sized equal to sizeof(void *).

I'm not sure what the appropriate fix for this is at this moment.

Segfault when the redis server shutdown

If I use a while code to execute a command,then shutdown the redis server,

while (1) {
/**
how can i make sure the connect is alive? i found there no function to check
*/
reply = redisCommand(c,"PING");
printf("PING: %s\n", reply->str);
freeReplyObject(reply);
sleep(10);
}

structure redisReply alignment

If you reorder the members of the structure redisReply, it is possible to reduce its size. Like this:

typedef struct redisReply {
int type; /* REDIS_REPLY_* /
long long integer; /
The integer when type is REDIS_REPLY_INTEGER /
int len; /
Length of string /
char *str; /
Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING /
size_t elements; /
number of elements, for REDIS_REPLY_ARRAY _/
struct redisReply *_element; /* elements vector for REDIS_REPLY_ARRAY */
} redisReply;

typedef struct redisReply_new {
int type; /* REDIS_REPLY_* /
int len; /
Length of string /
size_t elements; /
number of elements, for REDIS_REPLY_ARRAY /
long long integer; /
The integer when type is REDIS_REPLY_INTEGER /
char *str; /
Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING _/
struct redisReply *_element; /* elements vector for REDIS_REPLY_ARRAY */
} redisReplyNew;

Testing:

printf("sizeof(redisReply): %ld\n", sizeof(redisReply));
printf("sizeof(redisReplyNew): %ld\n", sizeof(redisReplyNew));

The output:

sizeof(redisReply): 48
sizeof(redisReplyNew): 40

Obs.: I have a 64 bit system.

Memory leaks and memory safe code.

Hi all!

I have using hiredis with libev in my project written in C++. Well, I have observing memory leaks when sending to Redis PING-commands only. I have edit hiredis.c and async.c files, correct some errors and add memory-safe code. So, diff-files listing:

async.c.diff
46,47d45
< if (key == NULL)
< return 0;
52,53d49
< if (src == NULL)
< return NULL;
56,57d51
< if (dup == NULL)
< return NULL;
63,64d56
< if (key1 == NULL || key2== NULL)
< return 0;
75,76d66
< if (key == NULL)
< return;
82,83d71
< if (val == NULL)
< return;
86d73
< val = NULL;
100,101d86
< if (ac == NULL)
< return NULL;
135,136d119
< if (ac == NULL)
< return;
143,144d125
< if (ip == NULL)
< return NULL;
146,147d126
< if (c == NULL)
< return NULL;
149,150d127
< if (ac == NULL)
< return NULL;
156,157d132
< if (path == NULL)
< return NULL;
159,160d133
< if (c == NULL)
< return NULL;
162,163d134
< if (ac == NULL)
< return NULL;
169,170d139
< if (ac == NULL || fn == NULL)
< return REDIS_ERR;
184,185d152
< if (ac == NULL || fn == NULL)
< return REDIS_ERR;
195,196d161
< if (list == NULL || source == NULL)
< return REDIS_ERR;
201,202d165
< if (cb == NULL)
< return REDIS_ERR;
218,219d180
< if (list == NULL || target == NULL)
< return REDIS_ERR;
236,237d196
< if (ac == NULL || cb == NULL)
< return;
248,249d206
< if (ac != NULL)
< return;
298,299d254
< if (ac == NULL)
< return;
308,309d262
< if (ac == NULL)
< return;
336,337d288
< if (ac == NULL)
< return;
345,346d295
< if (ac == NULL || reply == NULL || dstcb == NULL)
< return REDIS_ERR;
394,395d342
< if (ac == NULL)
< return;
463,464d409
< if (ac == NULL)
< return;
477,478d421
< if (ac == NULL)
< return;
506,507d448
< if (start == NULL || str == NULL || len == NULL)
< return NULL;
525,526d465
< if (ac == NULL || fn == NULL || cmd == NULL)
< return REDIS_ERR;
587,588d525
< if (ac == NULL || fn == NULL || format == NULL)
< return -1;
599,600d535
< if (ac == NULL || fn == NULL || format == NULL)
< return -1;
610,611d544
< if (ac == NULL || fn == NULL)
< return -1;

hiredis.c.diff
62c62

< redisReply *r = calloc(1,sizeof(redisReply));

redisReply *r = calloc(1,sizeof(*r));

73,74d72
< if (reply == NULL)
< return;
87d84
< r->element = NULL;
93c90

< if (r->str != NULL) {

    if (r->str != NULL)

95,96d91
< r->str = NULL;
< }
100d94
< r = NULL;
104,105d97
< if (task == NULL || str == NULL)
< return NULL;
138,139d129
< if (task == NULL)
< return NULL;
147c137

< r->element = calloc(elements,sizeof(redisReply));

    r->element = calloc(elements,sizeof(redisReply*));

165,166d154
< if (task == NULL)
< return NULL;
184,185d171
< if (task == NULL)
< return NULL;
201,202d186
< if (r == NULL || str == NULL)
< return;
223c207

< len = len < (strlen(r->errstr)-1) ? len : (strlen(r->errstr)-1);

len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1);

229,230d212
< if (buf == NULL)
< return 0;
255,256d236
< if (r == NULL)
< return;
266,267d245
< if (r == NULL)
< return;
272,273d249
< if (r == NULL)
< return NULL;
285,286d260
< if (s == NULL)
< return NULL;
315,316d288
< if (s == NULL)
< return 0;
344,345d315
< if (r == NULL || _len == NULL)
< return NULL;
361,362d330
< if (r == NULL)
< return;
388,389d355
< if (r == NULL)
< return REDIS_ERR;
424,425d389
< if (r == NULL)
< return REDIS_ERR;
479,480d442
< if (r == NULL)
< return REDIS_ERR;
546,547d507
< if (r == NULL)
< return REDIS_ERR;
609d568
< r = NULL;
618,619d576
< if (r == NULL)
< return;
625d581
< r = NULL;
629,630d584
< if (r == NULL || buf == NULL)
< return REDIS_ERR;
663,664d616
< if (r == NULL)
< return REDIS_ERR;
894d845
< curargv = NULL;
902d852
< curargv = NULL;
904c854

< if (curarg != NULL) {

if (curarg != NULL)

906,907d855
< curarg = NULL;
< }
911c859

< if (cmd != NULL) {

if (cmd != NULL)

913,914d860
< cmd = NULL;
< }
932,933d877
< if (target == NULL || format == NULL)
< return 0;
948,949d891
< if (target == NULL || argv == NULL || argvlen == NULL)
< return 0;
984,985d925
< if (c == NULL)
< return;
991c931

< len = len < (strlen(c->errstr)-1) ? len : (strlen(c->errstr)-1);

    len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1);

997c937

< strerror_r(errno,c->errstr,strlen(c->errstr));

    strerror_r(errno,c->errstr,sizeof(c->errstr));

1016,1017d955
< if (c == NULL)
< return;
1025d962
< c = NULL;
1032,1033d968
< if (ip == NULL)
< return NULL;
1035,1036d969
< if (c == NULL)
< return NULL;
1043,1044d975
< if (ip == NULL)
< return NULL;
1046,1047d976
< if (c == NULL)
< return NULL;
1054,1055d982
< if (ip == NULL)
< return NULL;
1057,1058d983
< if (c == NULL)
< return NULL;
1065,1066d989
< if (path == NULL)
< return NULL;
1074,1075d996
< if (path == NULL)
< return NULL;
1083,1084d1003
< if (path == NULL)
< return NULL;
1093,1094d1011
< if (c == NULL)
< return REDIS_ERR;
1106,1107d1022
< if (c == NULL)
< return REDIS_ERR;
1145,1146d1059
< if (c == NULL || done == NULL)
< return REDIS_ERR;
1178,1179d1090
< if (c == NULL || reply == NULL)
< return REDIS_ERR;
1188,1189d1098
< if (c == NULL || reply == NULL)
< return REDIS_ERR;
1227,1228d1135
< if (c == NULL || cmd == NULL)
< return REDIS_ERR;
1242,1243d1148
< if (c == NULL || format == NULL)
< return REDIS_ERR;
1255d1159
< cmd = NULL;
1260d1163
< cmd = NULL;
1265,1266d1167
< if (c == NULL || format == NULL)
< return REDIS_ERR;
1277,1278d1177
< if (c == NULL || argv == NULL || argvlen == NULL)
< return REDIS_ERR;
1290d1188
< cmd = NULL;
1295d1192
< cmd = NULL;
1311,1312d1207
< if (c == NULL)
< return NULL;
1324,1325d1218
< if (c == NULL || format == NULL)
< return NULL;
1332,1333d1224
< if (c == NULL || format == NULL)
< return NULL;
1343,1344d1233
< if (c == NULL || argv == NULL || argvlen == NULL)
< return NULL;

And another thing. When I try to free PING-command reply, my program core dumped because memory is clean already but pointers is not NULL. Please, fix this.

Thanks!

Best regards,

Andrey Shestakov

memory leak when dealing with integer response ?

With this simple code :

#include <hiredis.h>

int main(void) {
  int i = 0;
  int fd;
  redisConnect(&fd, "127.0.0.1", 6379);
  for (i = 0 ; i < 1000 ; i++) {
    redisReply *coucou = redisCommand(fd, "%s %s", "SCARD", "foo");
    freeReplyObject(coucou);
  }
}

I get a memory leak with valgrind :

valgrind --leak-check=full ./a.out

==22961== 19,000 bytes in 1,000 blocks are definitely lost in loss record 1 of 1
==22961==    at 0x4A0776F: realloc (vg_replace_malloc.c:429)
==22961==    by 0x4C36A7F: sdsMakeRoomFor (sds.c:108)
==22961==    by 0x4C36E88: sdscatlen (sds.c:123)
==22961==    by 0x4C3624B: redisReadLine (hiredis.c:111)
==22961==    by 0x4C36356: redisReadReply (hiredis.c:125)
==22961==    by 0x4C36757: redisCommand (hiredis.c:297)
==22961==    by 0x4006AF: main (toto.c:8)

With a string reply, I don't have this.

Maybe I'm missing something ? Or is there a real memory leak in hiredis ?

Segmentation fault on disconnect

I'm trying to test how hiredis work when it disconnects. I tried this code but it throws a segmentation fault.

redisContext *c;
redisReply *reply;

c = redisConnect((char*)"127.0.0.1", 6379);
if (c->err) {
    printf("Connection error: %s\n", c->errstr);
    exit(1);
}

/* PING server */
redisAppendCommand(c,"QUIT");
redisAppendCommand(c,"PING");
redisGetReply(c,&reply);
freeReplyObject(reply);
redisGetReply(c,&reply);
printf("PING: %s\n", reply->str);
freeReplyObject(reply);

Pipelining using multiple context handles

Hi,

I'm using Hiredis and pipelining to improve throughput (a lot!) over traditional blocking calls, without the complexity of going fully async. With the delay of Cluster, I'm looking at coarse-level sharding by "service" to alleviate potential scaling pitfalls in the near term, with the goal of leveraging Cluster when it is available.

I have a series of paired functions, one pair per "service", as follows:

PipelineServiceA(handle1);
PipelineServiceB(handle1);
PipelineServiceC(handle1);
ParseReplyServiceA(handle1);
ParseReplyServiceB(handle1);
ParseReplyServiceC(handle1);

The Pipeline functions use redisAppendCommand() and the ParseReply functions call redisGetReply(). It works great so far when using a common context (i.e. single server handle), as I can fill up the send buffer with all of my queries, the fire it off and sort out the responses in one round trip.

If I wanted to allocate a dedicated Redis instance for each "service" in an attempt to add some coarse sharding, I can do this relatively painlessly by simply passing the dedicated handle to each function.

PipelineServiceA(handle1);
PipelineServiceB(handle2);
PipelineServiceC(handle3);
ParseReplyServiceA(handle1);
ParseReplyServiceB(handle2);
ParseReplyServiceC(handle3);

The problem is that Hiredis doesn't send the output buffer until redisGetReply() is called, so instead of 3 sets of grouped commands being sent out at nearly the same time, followed by parsing of the responses quickly in series, the effective performance becomes nearly:

PipelineServiceA(handle1);
ParseReplyServiceA(handle1);
PipelineServiceB(handle2);
ParseReplyServiceB(handle2);
PipelineServiceC(handle3);
ParseReplyServiceC(handle3);

The send for each handle doesn't happen until the first redisGetReply() is called in a ParseReply function, so they become 3 serial, blocking requests instead of being close to the original (single context) implementation.

It seems like Hiredis would benefit from a non-blocking SendOutputBuffer type of command in order to support "sharded pipelining".

Buffer overflow in redisAsyncInitialize

Proposed fix:

static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
    redisAsyncContext *ac = realloc(c,sizeof(redisAsyncContext));
    /* Set all bytes in the async part of the context to 0 */
 //   memset(ac+sizeof(redisContext),0,sizeof(redisAsyncContext)-sizeof(redisContext));
    return ac;
}

Hiredis does not compile on Windows

With Visual Studio 2008, I can't get it to compile.

  • On "va_arg(ap,void);", the compiler complains about "error: C2070: 'void': illegal sizeof operand" and "error: C2100: illegal indirection"
  • sys/time.h include on Windows should be just time.h
  • there is no unistd.h on Windows
  • stdarg.h should be included in hiredis.c for va_* functions

empty strings should be allowed with %b

Empty strings cause the command builder to create an invalid request.

    /* Set a key using binary safe API */
    reply = redisCommand(c,"SET %b %b", "foo", 3, "", 0);
    printf("SET (binary API): %s\n", reply->str);
    freeReplyObject(reply);

Responds with:

SET (binary API): ERR wrong number of arguments for 'set' command

The following command is generated and sent:

*2
$3
SET
$3
foo

This issue was discovered whilst porting phpredis to a hiredis backend; a unit test failed.

hiredis.h is not compatible with C89 and C++98

When included in file, compiled with --std=c89 --Wall --pedantic --Werror, hiredis.h produces this error:

lib/hiredis/hiredis.h:92: error: ISO C90 does not support ‘long long’
lib/hiredis/hiredis.h:111: error: ISO C90 does not support ‘long long’

Similar error appears when compiling with --std=c++98 (other flags as above).

lib/hiredis/hiredis.h:92: error: ISO C++ 1998 does not support ‘long long’
lib/hiredis/hiredis.h:111: error: ISO C++ 1998 does not support ‘long long’

I did not try to compile whole hiredis (yet).

$ gcc --version
gcc (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Binary-safe arguments crash

Works fine:
redisReply *reply;
reply = (redisReply *)redisCommand(c,
"GETRANGE %s %s %s",
key.data(),
//key.length(),
s1.data(),
//s1.length(),
s2.data()
//s2.length()
);

Crashes:
redisReply *reply;
reply = (redisReply *)redisCommand(c,
"GETRANGE %b %b %b",
key.data(),
key.length(),
s1.data(),
s1.length(),
s2.data(),
s2.length()
);

Error connectRedis: Cannot assign requested address

Hi, I doing a aplication that read lines for a file and publish data to redis database. This is a 'while' that never end. The aplication runs until about 45 seconds and then the connection to redis fails with this messages:
Error connectRedis: Cannot assign requested address

when i check with
netstat -vatnl | grep 6399 ( 6399 is my number port for redis)
...
tcp 0 0 127.0.0.1:38690 127.0.0.1:6399 TIME_WAIT
tcp 0 0 127.0.0.1:35986 127.0.0.1:6399 TIME_WAIT
tcp 0 0 127.0.0.1:37470 127.0.0.1:6399 TIME_WAIT
tcp 0 0 127.0.0.1:37378 127.0.0.1:6399 TIME_WAIT
tcp 0 0 127.0.0.1:59754 127.0.0.1:6399 TIME_WAIT
tcp 0 0 127.0.0.1:46141 127.0.0.1:6399 TIME_WAIT
tcp 0 0 127.0.0.1:33984 127.0.0.1:6399 TIME_WAIT
tcp 0 0 127.0.0.1:37933 127.0.0.1:6399 TIME_WAIT
tcp 0 0 127.0.0.1:53850 127.0.0.1:6399 TIME_WAIT
tcp 0 0 127.0.0.1:53522 127.0.0.1:6399 TIME_WAIT
...

there are 28232 port open connected to redis port 6399, but a close the connection to redis after publish data.
This is my code where connect redis fails.

int guardarDBRedis(datos* head){
int r=1;
redisContext* swer = connectRedis(); // <--------------------------------------- FAILS HERE

if(hanSolicitadoDatos(swer,head->id)==0){
redisFree(swer);
return 0;
}
int command;
void * re;
canal* c=head->datos->primero;
char* publicar = (char_)malloc(sizeof(char)_256);
sprintf(publicar,"publish idObservar.%s %s|%d|%.1f|%d|%.1f",head->id,head->datos->hora,c->num,c->promVelocidad,c->next->num,c->next->promVelocidad);
re = redisCommand(swer,publicar);

redisReply reply =(redisReply)re;
if(reply->type==REDIS_REPLY_ERROR){
printf("error en guardarDBRedis redisReply: %s\n",reply->str);
free(publicar);
pthread_exit(NULL);
}

freeReplyObject(re);
redisFree(swer);

free(publicar);

return r;
}

and this is my method to connect to redis

redisContext* connectRedis(){

redisContext *c = redisConnect("127.0.0.1", 6399);
if (c->err) {
printf("Error connectRedis: %s codigo:%d\n", c->errstr,c->err);
return NULL;
}

return c;
}

No local rpath settings

When packaging software, the installed executables will obtain the library paths from the environment. Hence, it is not recommended to use a local rpath. The patch below removes hard-coding of the same, and also creates versioned shared libraries.

diff -up antirez-hiredis-0fbfa45/Makefile.fix antirez-hiredis-0fbfa45/Makefile
--- antirez-hiredis-0fbfa45/Makefile.fix 2011-04-19 13:06:43.214439002 +0530
+++ antirez-hiredis-0fbfa45/Makefile 2011-04-19 13:12:52.137439004 +0530
@@ -27,9 +27,12 @@ else ifeq ($(uname_S),Darwin)
else
CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wwrite-strings $(ARCH) $(PROF)
CCLINK?=-lm -pthread

  • LDFLAGS?=-L. -Wl,-rpath,.
  • LDFLAGS?=-L.
    DYLIBNAME?=libhiredis.so
  • DYLIB_MAKE_CMD?=gcc -shared -Wl,-soname,${DYLIBNAME} -o ${DYLIBNAME} ${OBJ}
  • VERSION?=1
  • DYLIB_MAKE_CMD?=gcc -shared -Wl,-soname,${DYLIBNAME}.${VERSION} -o ${DYLIBNAME}.${VERSION}.0 ${OBJ}
  • DYLIB_LINK_NAME?=ln -sf ${DYLIBNAME}.${VERSION}.0 ${DYLIBNAME}
  • DYLIB_LINK_VERSION?=ln -sf ${DYLIBNAME}.${VERSION}.0 ${DYLIBNAME}.1
    STLIBNAME?=libhiredis.a
    STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
    endif
    @@ -53,6 +56,8 @@ test.o: test.c hiredis.h

${DYLIBNAME}: ${OBJ}
${DYLIB_MAKE_CMD}

  • ${DYLIB_LINK_NAME}
  • ${DYLIB_LINK_VERSION}

${STLIBNAME}: ${OBJ}
${STLIB_MAKE_CMD}

uninitialised value inside redisvFormatCommand()

Looks like redisFormatCommand() supports just 2 binary strings (%b). If you use de third binary string something goes wrong inside redisFormatCommand().

Inside test.c there is this code:

test("Format command with %%b string interpolation: ");
len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"b\0r",3);

I have changed it to this (added 1 more binary string):

test("Format command with %%b string interpolation: ");
len = redisFormatCommand(&cmd,"SET %b %b %b","foo",3,"b\0r",3,"\t\n\r\t",4);

The above code causes an uninitialised value here:

        case 'b':
            arg = va_arg(ap,char*);
            size = va_arg(ap,size_t);
            if (size > 0)
                current = sdscatlen(current,arg,size);
            interpolated = 1;
            break;

For some reason, "size" (in "size = va_arg(ap,size_t);") becomes uninitialised.

I have discovered this problem by running de modified test.c inside valgrind (changed Makefile to -O0 also), which gererated the following error:

==30230== Conditional jump or move depends on uninitialised value(s)
==30230== at 0x50B4B17: redisvFormatCommand (hiredis.c:633)
==30230== by 0x50B50C8: redisFormatCommand (hiredis.c:744)
==30230== by 0x40147A: test_format_commands (test.c:58)
==30230== by 0x40316E: main (test.c:484)

I ran valgrind like this:

valgrind --leak-check=full --leak-resolution=high ./hiredis-test

This problem is not allowing me to use HMSET to set several fileds at once. I had to call HSET many times or use %s instead of %b (which is not efficient, since I already know the size of the string and I dont want another strlen()).

Reply Parse API example for multi bulk

Can someone please show me example code on how to parse multi bulk response.
Reply I need parse looks like this:

+OK
+QUEUED
+QUEUED
*2
*6
$3
pop
$5
28710
$4
posx
$6
191920
$4
posy
$5
66992
*6
$3
pop
$5
21046
$4
posx
$6
191968
$4
posy
$5
66752

Thanks in advance!! I tried everything. :(

some problem about hredis c client using

hi,
I got some problem about c client using when i was using function as redisCommand(c,'get key') ,the key is exists.At the beginning all goes right,but when the function was called fast some problem come.

This is my code:

CFKeyValueDB *ptclient;
void CFKeyValueDB::InitializeKeyValueDB(){
......
}

string CFKeyValueDB::get(string key){
redisReply *reply = (redisReply *)redisCommand(c,"GET %s",(char *)key.c_str());
try{
if(reply !=NULL){
string ret = reply->str;
freeReplyObject(reply) ;
LOG("get %s %s",(char *)key.c_str(),(char *)ret.c_str());
return ret;
}
}
catch(...){
LOG("get %s catch exception",(char *)key.c_str());
}
LOG("get %s fail",(char *)key.c_str());
return "",

}

this is the using code:

while(true){
string account = "test0001";
string key = "account@" + account;
ptclient->get(key);
}

In another thread i also call redisCommand(c,"ping")
exception:
the correct output is " get account@test0001 1"
but sometimes the output is " get account@test0001 PONG "
sometimes the output is "get account@test0001 get exception"

when i debug the client i found something:
at the file hiredis.c
function:redisReaderGetReply(redisReader r,void *_reply)
at the last of the function:
/ Emit a reply when there is one. */
if(r->ridx == -1){
if(reply != NULL)
*reply = r->reply;
r->reply = NULL;
}
"_reply = r->reply;" was not called,
r->reply = NULL; called.

My client package is antriez-hiredis-7bc9f54. redis-server version:2.4.1.

test exit with segmentation fault

On OSX 10.6:

$ hiredis/ make
cc -c -std=c99 -pedantic -O2 -fPIC -Wall -W -Wwrite-strings   -arch i386 -arch x86_64 -g -ggdb   net.c
cc -c -std=c99 -pedantic -O2 -fPIC -Wall -W -Wwrite-strings   -arch i386 -arch x86_64 -g -ggdb   hiredis.c
cc -c -std=c99 -pedantic -O2 -fPIC -Wall -W -Wwrite-strings   -arch i386 -arch x86_64 -g -ggdb   sds.c
cc -c -std=c99 -pedantic -O2 -fPIC -Wall -W -Wwrite-strings   -arch i386 -arch x86_64 -g -ggdb   async.c
libtool -dynamic -o libhiredis.dylib -lm -g -ggdb  - net.o hiredis.o sds.o async.o
cc -c -std=c99 -pedantic -O2 -fPIC -Wall -W -Wwrite-strings   -arch i386 -arch x86_64 -g -ggdb   example.c
cc -o hiredis-example -std=c99 -pedantic -O2 -fPIC -Wall -W -Wwrite-strings   -lm -pthread   -g -ggdb  -L. -lhiredis -Wl,-rpath,. example.o
cc -c -std=c99 -pedantic -O2 -fPIC -Wall -W -Wwrite-strings   -arch i386 -arch x86_64 -g -ggdb   test.c
cc -o hiredis-test -std=c99 -pedantic -O2 -fPIC -Wall -W -Wwrite-strings   -lm -pthread   -g -ggdb  -L. -lhiredis -Wl,-rpath,. test.o
$ hiredis/ make test
./hiredis-test
##01 Format command without interpolation: PASSED
##02 Format command with %s string interpolation: PASSED
##03 Format command with %b string interpolation: PASSED
##04 Format command by passing argc/argv without lengths: PASSED
##05 Format command by passing argc/argv with lengths: PASSED
make: *** [test] Segmentation fault

Remove use of exit()

Since, hiredis is a client library that will be used by other applications, it shouldn't invoke exit.

It would be better to return an error or simply a non-zero value that the application can check for.

redisCommand function parameters problem

long long int id = 1234ll;

for 4 items:

  1. redisCommand(context, "SET FOO %lld", id);
  2. redisCommand(context, "SET FOO:%lld:name Jason", id);
  3. redisCommand(context, "SET FOO:%d:name %s", 1234, "Jason");
  4. redisCommand(context, "SET FOO:%lld:name %s", id, "Jason");

1 2 and 3 are pass, but 4 failed. Is this a problem? Or how can I resolve it.

hiredis install error with npm 1.0.1rc9

I get this error when i install hiredis

[email protected] preinstall /root/node-v0.4.7/node_modules/hiredis
node-waf configure build

Traceback (most recent call last):
File "/usr/local/bin/node-waf", line 16, in
Scripting.prepare(t, os.getcwd(), VERSION, wafdir)
File "/usr/local/bin/../lib/node/wafadmin/Scripting.py", line 145, in prepare
prepare_impl(t, cwd, ver, wafdir)
File "/usr/local/bin/../lib/node/wafadmin/Scripting.py", line 35, in prepare_impl
lst = os.listdir(cwd)
OSError: [Errno 13] Permission denied: '/root/node-v0.4.7/node_modules/hiredis'
npm ERR! error installing [email protected] Error: [email protected] preinstall: node-waf configure build
npm ERR! error installing [email protected] sh "-c" "node-waf configure build" failed with 1
npm ERR! error installing [email protected] at ChildProcess. (/usr/local/lib/node_modules/npm/lib/utils/exec.js:49:20)
npm ERR! error installing [email protected] at ChildProcess.emit (events.js:67:17)
npm ERR! error installing [email protected] at ChildProcess.onexit (child_process.js:192:12)
npm ERR! [email protected] preinstall: node-waf configure build
npm ERR! sh "-c" "node-waf configure build" failed with 1
npm ERR!
npm ERR! Failed at the [email protected] preinstall script.
npm ERR! This is most likely a problem with the hiredis package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! node-waf configure build
npm ERR! You can get their info via:
npm ERR! npm owner ls hiredis
npm ERR! There is likely additional logging output above.
npm ERR!
npm ERR! System Linux 2.6.18-194.26.1.el5.028stab079.2
npm ERR! command "node" "/usr/local/bin/npm" "install" "hiredis"
npm ERR!
npm ERR! Additional logging details can be found in:
npm ERR! /root/node-v0.4.7/npm-debug.log
npm not ok

Build warning

lib/hiredis/fmacros.h:4: warning: "_BSD_SOURCE" redefined
/usr/include/features.h:182: note: this is the location of the previous definition

Probably an #ifndef is in order.

P.S.

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 10.10
Release:    10.10
Codename:   maverick

$ gcc --version
gcc (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Failed test: Returns error when host cannot be resolved

The test Returns error when host cannot be resolved (currently #30 in my list) fails on comparing c->errstr since it differs between os:es:

  • test.c check: Can't resolve: "idontexist.local"
  • on linux: Name or service not known
  • os x lion: nodename nor servname provided, or not known

Not sure this should turn into a list of different error strings, so perhaps sticking to checking c->err is suffice?

Compilation error on FreeBSD 8.1 amd64

gcc on FreeBSD don't known how to use 'signal' subroutine without signal.h included in source.

Patch bellow:

---- BEGIN ----
Files antirez-hiredis-3be1d6e.orig/hiredis-test and antirez-hiredis-3be1d6e/hiredis-test differ
diff -ruN antirez-hiredis-3be1d6e.orig/test.c antirez-hiredis-3be1d6e/test.c
--- antirez-hiredis-3be1d6e.orig/test.c 2010-11-03 11:50:46.000000000 +0100
+++ antirez-hiredis-3be1d6e/test.c 2010-11-21 17:06:00.257564584 +0100
@@ -5,6 +5,9 @@
#include <sys/time.h>
#include <assert.h>
#include <unistd.h>
+#ifdef FreeBSD
+#include <signal.h>
+#endif

#include "hiredis.h"

Files antirez-hiredis-3be1d6e.orig/test.o and antirez-hiredis-3be1d6e/test.o differ
---- END ----

Segfault when using timed-out unix socket handle

In my application, I check all of my Hiredis handles before using them if they have been dormant for awhile. This allows me to catch and restore connections that may have timed out. It's been working well thus far. It's never required in production, but it helps prevent issues for us in development and staging when things are slow.

I recently upgraded from Hiredis v.0.10.0 to v.0.10.1, and now seem to be getting segfaults when sending on my one Redis handle that uses a unix socket (presumably after its timed out on the server side). The TCP/IP-based handles seem to do fine still. It's a simple blocking ping. Call stack follows.

write ()] ../sysdeps/unix/syscall-template.S:82
redisBufferWrite (c=0x12999a0, done=0x7f6c063eb52c)] hiredis.c:1113
redisGetReply (c=0x12999a0, reply=0x7f6c063eb568)] hiredis.c:1156
__redisBlockForReply ()] hiredis.c:1257
redisvCommand (c=0x12999a0, format=, ap=)] hiredis.c:1267
redisCommand (c=0xe, format=0x129f4c8 "*1\r\n$4\r\nPING\r\n")] hiredis.c:1274

Let me know what other details would be helpful.

Cheers,

Dean

SUBSCRIBE in an asynchronous context

Hello,

I've tried to use the SUBSCRIBE command with an asynchronous adapter, but it doesn't seem to be supported.
The main problem is with the __redisPushCallback, __redisShiftCallback functions: the SUBSCRIBE command first sends a message confirming the subscription to the channel, which consumes the redisCallback object. A write to the channel will try to shift the next redisCallback object, which doesn't exist: this leads to an assertion failure in async.c:217.

I have tried to re-add the redisCallback by making __redisPushCallback public instead of static. This seems to work, but I don't think I should be calling this private function.

What do you think? Is there a better way to do this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.