redis-client 0.2.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile +1 -2
- data/Gemfile.lock +2 -3
- data/README.md +66 -3
- data/Rakefile +43 -23
- data/lib/redis_client/command_builder.rb +83 -0
- data/lib/redis_client/config.rb +9 -48
- data/lib/redis_client/connection_mixin.rb +38 -0
- data/lib/redis_client/decorator.rb +84 -0
- data/lib/redis_client/pooled.rb +38 -30
- data/lib/redis_client/ruby_connection/buffered_io.rb +153 -0
- data/lib/redis_client/{resp3.rb → ruby_connection/resp3.rb} +0 -26
- data/lib/redis_client/{connection.rb → ruby_connection.rb} +26 -31
- data/lib/redis_client/version.rb +1 -1
- data/lib/redis_client.rb +162 -36
- data/redis-client.gemspec +2 -4
- metadata +12 -59
- data/.rubocop.yml +0 -190
- data/ext/redis_client/hiredis/export.clang +0 -2
- data/ext/redis_client/hiredis/export.gcc +0 -7
- data/ext/redis_client/hiredis/extconf.rb +0 -62
- data/ext/redis_client/hiredis/hiredis_connection.c +0 -708
- data/ext/redis_client/hiredis/vendor/.gitignore +0 -9
- data/ext/redis_client/hiredis/vendor/.travis.yml +0 -131
- data/ext/redis_client/hiredis/vendor/CHANGELOG.md +0 -364
- data/ext/redis_client/hiredis/vendor/CMakeLists.txt +0 -165
- data/ext/redis_client/hiredis/vendor/COPYING +0 -29
- data/ext/redis_client/hiredis/vendor/Makefile +0 -308
- data/ext/redis_client/hiredis/vendor/README.md +0 -664
- data/ext/redis_client/hiredis/vendor/adapters/ae.h +0 -130
- data/ext/redis_client/hiredis/vendor/adapters/glib.h +0 -156
- data/ext/redis_client/hiredis/vendor/adapters/ivykis.h +0 -84
- data/ext/redis_client/hiredis/vendor/adapters/libev.h +0 -179
- data/ext/redis_client/hiredis/vendor/adapters/libevent.h +0 -175
- data/ext/redis_client/hiredis/vendor/adapters/libuv.h +0 -117
- data/ext/redis_client/hiredis/vendor/adapters/macosx.h +0 -115
- data/ext/redis_client/hiredis/vendor/adapters/qt.h +0 -135
- data/ext/redis_client/hiredis/vendor/alloc.c +0 -86
- data/ext/redis_client/hiredis/vendor/alloc.h +0 -91
- data/ext/redis_client/hiredis/vendor/appveyor.yml +0 -24
- data/ext/redis_client/hiredis/vendor/async.c +0 -887
- data/ext/redis_client/hiredis/vendor/async.h +0 -147
- data/ext/redis_client/hiredis/vendor/async_private.h +0 -75
- data/ext/redis_client/hiredis/vendor/dict.c +0 -352
- data/ext/redis_client/hiredis/vendor/dict.h +0 -126
- data/ext/redis_client/hiredis/vendor/fmacros.h +0 -12
- data/ext/redis_client/hiredis/vendor/hiredis-config.cmake.in +0 -13
- data/ext/redis_client/hiredis/vendor/hiredis.c +0 -1174
- data/ext/redis_client/hiredis/vendor/hiredis.h +0 -336
- data/ext/redis_client/hiredis/vendor/hiredis.pc.in +0 -12
- data/ext/redis_client/hiredis/vendor/hiredis_ssl-config.cmake.in +0 -13
- data/ext/redis_client/hiredis/vendor/hiredis_ssl.h +0 -157
- data/ext/redis_client/hiredis/vendor/hiredis_ssl.pc.in +0 -12
- data/ext/redis_client/hiredis/vendor/net.c +0 -612
- data/ext/redis_client/hiredis/vendor/net.h +0 -56
- data/ext/redis_client/hiredis/vendor/read.c +0 -739
- data/ext/redis_client/hiredis/vendor/read.h +0 -129
- data/ext/redis_client/hiredis/vendor/sds.c +0 -1289
- data/ext/redis_client/hiredis/vendor/sds.h +0 -278
- data/ext/redis_client/hiredis/vendor/sdsalloc.h +0 -44
- data/ext/redis_client/hiredis/vendor/sockcompat.c +0 -248
- data/ext/redis_client/hiredis/vendor/sockcompat.h +0 -92
- data/ext/redis_client/hiredis/vendor/ssl.c +0 -544
- data/ext/redis_client/hiredis/vendor/test.c +0 -1401
- data/ext/redis_client/hiredis/vendor/test.sh +0 -78
- data/ext/redis_client/hiredis/vendor/win32.h +0 -56
- data/lib/redis_client/buffered_io.rb +0 -151
- data/lib/redis_client/hiredis_connection.rb +0 -80
@@ -1,664 +0,0 @@
|
|
1
|
-
[![Build Status](https://travis-ci.org/redis/hiredis.png)](https://travis-ci.org/redis/hiredis)
|
2
|
-
|
3
|
-
**This Readme reflects the latest changed in the master branch. See [v1.0.0](https://github.com/redis/hiredis/tree/v1.0.0) for the Readme and documentation for the latest release ([API/ABI history](https://abi-laboratory.pro/?view=timeline&l=hiredis)).**
|
4
|
-
|
5
|
-
# HIREDIS
|
6
|
-
|
7
|
-
Hiredis is a minimalistic C client library for the [Redis](http://redis.io/) database.
|
8
|
-
|
9
|
-
It is minimalistic because it just adds minimal support for the protocol, but
|
10
|
-
at the same time it uses a high level printf-alike API in order to make it
|
11
|
-
much higher level than otherwise suggested by its minimal code base and the
|
12
|
-
lack of explicit bindings for every Redis command.
|
13
|
-
|
14
|
-
Apart from supporting sending commands and receiving replies, it comes with
|
15
|
-
a reply parser that is decoupled from the I/O layer. It
|
16
|
-
is a stream parser designed for easy reusability, which can for instance be used
|
17
|
-
in higher level language bindings for efficient reply parsing.
|
18
|
-
|
19
|
-
Hiredis only supports the binary-safe Redis protocol, so you can use it with any
|
20
|
-
Redis version >= 1.2.0.
|
21
|
-
|
22
|
-
The library comes with multiple APIs. There is the
|
23
|
-
*synchronous API*, the *asynchronous API* and the *reply parsing API*.
|
24
|
-
|
25
|
-
## Upgrading to `1.0.2`
|
26
|
-
|
27
|
-
<span style="color:red">NOTE: v1.0.1 erroneously bumped SONAME, which is why it is skipped here.</span>
|
28
|
-
|
29
|
-
Version 1.0.2 is simply 1.0.0 with a fix for [CVE-2021-32765](https://github.com/redis/hiredis/security/advisories/GHSA-hfm9-39pp-55p2). They are otherwise identical.
|
30
|
-
|
31
|
-
## Upgrading to `1.0.0`
|
32
|
-
|
33
|
-
Version 1.0.0 marks the first stable release of Hiredis.
|
34
|
-
It includes some minor breaking changes, mostly to make the exposed API more uniform and self-explanatory.
|
35
|
-
It also bundles the updated `sds` library, to sync up with upstream and Redis.
|
36
|
-
For code changes see the [Changelog](CHANGELOG.md).
|
37
|
-
|
38
|
-
_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._
|
39
|
-
|
40
|
-
## IMPORTANT: Breaking changes from `0.14.1` -> `1.0.0`
|
41
|
-
|
42
|
-
* `redisContext` has two additional members (`free_privdata`, and `privctx`).
|
43
|
-
* `redisOptions.timeout` has been renamed to `redisOptions.connect_timeout`, and we've added `redisOptions.command_timeout`.
|
44
|
-
* `redisReplyObjectFunctions.createArray` now takes `size_t` instead of `int` for its length parameter.
|
45
|
-
|
46
|
-
## IMPORTANT: Breaking changes when upgrading from 0.13.x -> 0.14.x
|
47
|
-
|
48
|
-
Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now
|
49
|
-
protocol errors. This is consistent with the RESP specification. On 32-bit
|
50
|
-
platforms, the upper bound is lowered to `SIZE_MAX`.
|
51
|
-
|
52
|
-
Change `redisReply.len` to `size_t`, as it denotes the the size of a string
|
53
|
-
|
54
|
-
User code should compare this to `size_t` values as well. If it was used to
|
55
|
-
compare to other values, casting might be necessary or can be removed, if
|
56
|
-
casting was applied before.
|
57
|
-
|
58
|
-
## Upgrading from `<0.9.0`
|
59
|
-
|
60
|
-
Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing
|
61
|
-
code using hiredis should not be a big pain. The key thing to keep in mind when
|
62
|
-
upgrading is that hiredis >= 0.9.0 uses a `redisContext*` to keep state, in contrast to
|
63
|
-
the stateless 0.0.1 that only has a file descriptor to work with.
|
64
|
-
|
65
|
-
## Synchronous API
|
66
|
-
|
67
|
-
To consume the synchronous API, there are only a few function calls that need to be introduced:
|
68
|
-
|
69
|
-
```c
|
70
|
-
redisContext *redisConnect(const char *ip, int port);
|
71
|
-
void *redisCommand(redisContext *c, const char *format, ...);
|
72
|
-
void freeReplyObject(void *reply);
|
73
|
-
```
|
74
|
-
|
75
|
-
### Connecting
|
76
|
-
|
77
|
-
The function `redisConnect` is used to create a so-called `redisContext`. The
|
78
|
-
context is where Hiredis holds state for a connection. The `redisContext`
|
79
|
-
struct has an integer `err` field that is non-zero when the connection is in
|
80
|
-
an error state. The field `errstr` will contain a string with a description of
|
81
|
-
the error. More information on errors can be found in the **Errors** section.
|
82
|
-
After trying to connect to Redis using `redisConnect` you should
|
83
|
-
check the `err` field to see if establishing the connection was successful:
|
84
|
-
```c
|
85
|
-
redisContext *c = redisConnect("127.0.0.1", 6379);
|
86
|
-
if (c == NULL || c->err) {
|
87
|
-
if (c) {
|
88
|
-
printf("Error: %s\n", c->errstr);
|
89
|
-
// handle error
|
90
|
-
} else {
|
91
|
-
printf("Can't allocate redis context\n");
|
92
|
-
}
|
93
|
-
}
|
94
|
-
```
|
95
|
-
|
96
|
-
*Note: A `redisContext` is not thread-safe.*
|
97
|
-
|
98
|
-
### Sending commands
|
99
|
-
|
100
|
-
There are several ways to issue commands to Redis. The first that will be introduced is
|
101
|
-
`redisCommand`. This function takes a format similar to printf. In the simplest form,
|
102
|
-
it is used like this:
|
103
|
-
```c
|
104
|
-
reply = redisCommand(context, "SET foo bar");
|
105
|
-
```
|
106
|
-
|
107
|
-
The specifier `%s` interpolates a string in the command, and uses `strlen` to
|
108
|
-
determine the length of the string:
|
109
|
-
```c
|
110
|
-
reply = redisCommand(context, "SET foo %s", value);
|
111
|
-
```
|
112
|
-
When you need to pass binary safe strings in a command, the `%b` specifier can be
|
113
|
-
used. Together with a pointer to the string, it requires a `size_t` length argument
|
114
|
-
of the string:
|
115
|
-
```c
|
116
|
-
reply = redisCommand(context, "SET foo %b", value, (size_t) valuelen);
|
117
|
-
```
|
118
|
-
Internally, Hiredis splits the command in different arguments and will
|
119
|
-
convert it to the protocol used to communicate with Redis.
|
120
|
-
One or more spaces separates arguments, so you can use the specifiers
|
121
|
-
anywhere in an argument:
|
122
|
-
```c
|
123
|
-
reply = redisCommand(context, "SET key:%s %s", myid, value);
|
124
|
-
```
|
125
|
-
|
126
|
-
### Using replies
|
127
|
-
|
128
|
-
The return value of `redisCommand` holds a reply when the command was
|
129
|
-
successfully executed. When an error occurs, the return value is `NULL` and
|
130
|
-
the `err` field in the context will be set (see section on **Errors**).
|
131
|
-
Once an error is returned the context cannot be reused and you should set up
|
132
|
-
a new connection.
|
133
|
-
|
134
|
-
The standard replies that `redisCommand` are of the type `redisReply`. The
|
135
|
-
`type` field in the `redisReply` should be used to test what kind of reply
|
136
|
-
was received:
|
137
|
-
|
138
|
-
### RESP2
|
139
|
-
|
140
|
-
* **`REDIS_REPLY_STATUS`**:
|
141
|
-
* The command replied with a status reply. The status string can be accessed using `reply->str`.
|
142
|
-
The length of this string can be accessed using `reply->len`.
|
143
|
-
|
144
|
-
* **`REDIS_REPLY_ERROR`**:
|
145
|
-
* The command replied with an error. The error string can be accessed identical to `REDIS_REPLY_STATUS`.
|
146
|
-
|
147
|
-
* **`REDIS_REPLY_INTEGER`**:
|
148
|
-
* The command replied with an integer. The integer value can be accessed using the
|
149
|
-
`reply->integer` field of type `long long`.
|
150
|
-
|
151
|
-
* **`REDIS_REPLY_NIL`**:
|
152
|
-
* The command replied with a **nil** object. There is no data to access.
|
153
|
-
|
154
|
-
* **`REDIS_REPLY_STRING`**:
|
155
|
-
* A bulk (string) reply. The value of the reply can be accessed using `reply->str`.
|
156
|
-
The length of this string can be accessed using `reply->len`.
|
157
|
-
|
158
|
-
* **`REDIS_REPLY_ARRAY`**:
|
159
|
-
* A multi bulk reply. The number of elements in the multi bulk reply is stored in
|
160
|
-
`reply->elements`. Every element in the multi bulk reply is a `redisReply` object as well
|
161
|
-
and can be accessed via `reply->element[..index..]`.
|
162
|
-
Redis may reply with nested arrays but this is fully supported.
|
163
|
-
|
164
|
-
### RESP3
|
165
|
-
|
166
|
-
Hiredis also supports every new `RESP3` data type which are as follows. For more information about the protocol see the `RESP3` [specification.](https://github.com/antirez/RESP3/blob/master/spec.md)
|
167
|
-
|
168
|
-
* **`REDIS_REPLY_DOUBLE`**:
|
169
|
-
* The command replied with a double-precision floating point number.
|
170
|
-
The value is stored as a string in the `str` member, and can be converted with `strtod` or similar.
|
171
|
-
|
172
|
-
* **`REDIS_REPLY_BOOL`**:
|
173
|
-
* A boolean true/false reply.
|
174
|
-
The value is stored in the `integer` member and will be either `0` or `1`.
|
175
|
-
|
176
|
-
* **`REDIS_REPLY_MAP`**:
|
177
|
-
* An array with the added invariant that there will always be an even number of elements.
|
178
|
-
The MAP is functionally equivelant to `REDIS_REPLY_ARRAY` except for the previously mentioned invariant.
|
179
|
-
|
180
|
-
* **`REDIS_REPLY_SET`**:
|
181
|
-
* An array response where each entry is unique.
|
182
|
-
Like the MAP type, the data is identical to an array response except there are no duplicate values.
|
183
|
-
|
184
|
-
* **`REDIS_REPLY_PUSH`**:
|
185
|
-
* An array that can be generated spontaneously by Redis.
|
186
|
-
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.
|
187
|
-
|
188
|
-
* **`REDIS_REPLY_ATTR`**:
|
189
|
-
* An array structurally identical to a `MAP` but intended as meta-data about a reply.
|
190
|
-
_As of Redis 6.0.6 this reply type is not used in Redis_
|
191
|
-
|
192
|
-
* **`REDIS_REPLY_BIGNUM`**:
|
193
|
-
* A string representing an arbitrarily large signed or unsigned integer value.
|
194
|
-
The number will be encoded as a string in the `str` member of `redisReply`.
|
195
|
-
|
196
|
-
* **`REDIS_REPLY_VERB`**:
|
197
|
-
* A verbatim string, intended to be presented to the user without modification.
|
198
|
-
The string payload is stored in the `str` memeber, and type data is stored in the `vtype` member (e.g. `txt` for raw text or `md` for markdown).
|
199
|
-
|
200
|
-
Replies should be freed using the `freeReplyObject()` function.
|
201
|
-
Note that this function will take care of freeing sub-reply objects
|
202
|
-
contained in arrays and nested arrays, so there is no need for the user to
|
203
|
-
free the sub replies (it is actually harmful and will corrupt the memory).
|
204
|
-
|
205
|
-
**Important:** the current version of hiredis (1.0.0) frees replies when the
|
206
|
-
asynchronous API is used. This means you should not call `freeReplyObject` when
|
207
|
-
you use this API. The reply is cleaned up by hiredis _after_ the callback
|
208
|
-
returns. We may introduce a flag to make this configurable in future versions of the library.
|
209
|
-
|
210
|
-
### Cleaning up
|
211
|
-
|
212
|
-
To disconnect and free the context the following function can be used:
|
213
|
-
```c
|
214
|
-
void redisFree(redisContext *c);
|
215
|
-
```
|
216
|
-
This function immediately closes the socket and then frees the allocations done in
|
217
|
-
creating the context.
|
218
|
-
|
219
|
-
### Sending commands (cont'd)
|
220
|
-
|
221
|
-
Together with `redisCommand`, the function `redisCommandArgv` can be used to issue commands.
|
222
|
-
It has the following prototype:
|
223
|
-
```c
|
224
|
-
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
|
225
|
-
```
|
226
|
-
It takes the number of arguments `argc`, an array of strings `argv` and the lengths of the
|
227
|
-
arguments `argvlen`. For convenience, `argvlen` may be set to `NULL` and the function will
|
228
|
-
use `strlen(3)` on every argument to determine its length. Obviously, when any of the arguments
|
229
|
-
need to be binary safe, the entire array of lengths `argvlen` should be provided.
|
230
|
-
|
231
|
-
The return value has the same semantic as `redisCommand`.
|
232
|
-
|
233
|
-
### Pipelining
|
234
|
-
|
235
|
-
To explain how Hiredis supports pipelining in a blocking connection, there needs to be
|
236
|
-
understanding of the internal execution flow.
|
237
|
-
|
238
|
-
When any of the functions in the `redisCommand` family is called, Hiredis first formats the
|
239
|
-
command according to the Redis protocol. The formatted command is then put in the output buffer
|
240
|
-
of the context. This output buffer is dynamic, so it can hold any number of commands.
|
241
|
-
After the command is put in the output buffer, `redisGetReply` is called. This function has the
|
242
|
-
following two execution paths:
|
243
|
-
|
244
|
-
1. The input buffer is non-empty:
|
245
|
-
* Try to parse a single reply from the input buffer and return it
|
246
|
-
* If no reply could be parsed, continue at *2*
|
247
|
-
2. The input buffer is empty:
|
248
|
-
* Write the **entire** output buffer to the socket
|
249
|
-
* Read from the socket until a single reply could be parsed
|
250
|
-
|
251
|
-
The function `redisGetReply` is exported as part of the Hiredis API and can be used when a reply
|
252
|
-
is expected on the socket. To pipeline commands, the only things that needs to be done is
|
253
|
-
filling up the output buffer. For this cause, two commands can be used that are identical
|
254
|
-
to the `redisCommand` family, apart from not returning a reply:
|
255
|
-
```c
|
256
|
-
void redisAppendCommand(redisContext *c, const char *format, ...);
|
257
|
-
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
|
258
|
-
```
|
259
|
-
After calling either function one or more times, `redisGetReply` can be used to receive the
|
260
|
-
subsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where
|
261
|
-
the latter means an error occurred while reading a reply. Just as with the other commands,
|
262
|
-
the `err` field in the context can be used to find out what the cause of this error is.
|
263
|
-
|
264
|
-
The following examples shows a simple pipeline (resulting in only a single call to `write(2)` and
|
265
|
-
a single call to `read(2)`):
|
266
|
-
```c
|
267
|
-
redisReply *reply;
|
268
|
-
redisAppendCommand(context,"SET foo bar");
|
269
|
-
redisAppendCommand(context,"GET foo");
|
270
|
-
redisGetReply(context,(void *)&reply); // reply for SET
|
271
|
-
freeReplyObject(reply);
|
272
|
-
redisGetReply(context,(void *)&reply); // reply for GET
|
273
|
-
freeReplyObject(reply);
|
274
|
-
```
|
275
|
-
This API can also be used to implement a blocking subscriber:
|
276
|
-
```c
|
277
|
-
reply = redisCommand(context,"SUBSCRIBE foo");
|
278
|
-
freeReplyObject(reply);
|
279
|
-
while(redisGetReply(context,(void *)&reply) == REDIS_OK) {
|
280
|
-
// consume message
|
281
|
-
freeReplyObject(reply);
|
282
|
-
}
|
283
|
-
```
|
284
|
-
### Errors
|
285
|
-
|
286
|
-
When a function call is not successful, depending on the function either `NULL` or `REDIS_ERR` is
|
287
|
-
returned. The `err` field inside the context will be non-zero and set to one of the
|
288
|
-
following constants:
|
289
|
-
|
290
|
-
* **`REDIS_ERR_IO`**:
|
291
|
-
There was an I/O error while creating the connection, trying to write
|
292
|
-
to the socket or read from the socket. If you included `errno.h` in your
|
293
|
-
application, you can use the global `errno` variable to find out what is
|
294
|
-
wrong.
|
295
|
-
|
296
|
-
* **`REDIS_ERR_EOF`**:
|
297
|
-
The server closed the connection which resulted in an empty read.
|
298
|
-
|
299
|
-
* **`REDIS_ERR_PROTOCOL`**:
|
300
|
-
There was an error while parsing the protocol.
|
301
|
-
|
302
|
-
* **`REDIS_ERR_OTHER`**:
|
303
|
-
Any other error. Currently, it is only used when a specified hostname to connect
|
304
|
-
to cannot be resolved.
|
305
|
-
|
306
|
-
In every case, the `errstr` field in the context will be set to hold a string representation
|
307
|
-
of the error.
|
308
|
-
|
309
|
-
## Asynchronous API
|
310
|
-
|
311
|
-
Hiredis comes with an asynchronous API that works easily with any event library.
|
312
|
-
Examples are bundled that show using Hiredis with [libev](http://software.schmorp.de/pkg/libev.html)
|
313
|
-
and [libevent](http://monkey.org/~provos/libevent/).
|
314
|
-
|
315
|
-
### Connecting
|
316
|
-
|
317
|
-
The function `redisAsyncConnect` can be used to establish a non-blocking connection to
|
318
|
-
Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field
|
319
|
-
should be checked after creation to see if there were errors creating the connection.
|
320
|
-
Because the connection that will be created is non-blocking, the kernel is not able to
|
321
|
-
instantly return if the specified host and port is able to accept a connection.
|
322
|
-
|
323
|
-
*Note: A `redisAsyncContext` is not thread-safe.*
|
324
|
-
|
325
|
-
```c
|
326
|
-
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
327
|
-
if (c->err) {
|
328
|
-
printf("Error: %s\n", c->errstr);
|
329
|
-
// handle error
|
330
|
-
}
|
331
|
-
```
|
332
|
-
|
333
|
-
The asynchronous context can hold a disconnect callback function that is called when the
|
334
|
-
connection is disconnected (either because of an error or per user request). This function should
|
335
|
-
have the following prototype:
|
336
|
-
```c
|
337
|
-
void(const redisAsyncContext *c, int status);
|
338
|
-
```
|
339
|
-
On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the
|
340
|
-
user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err`
|
341
|
-
field in the context can be accessed to find out the cause of the error.
|
342
|
-
|
343
|
-
The context object is always freed after the disconnect callback fired. When a reconnect is needed,
|
344
|
-
the disconnect callback is a good point to do so.
|
345
|
-
|
346
|
-
Setting the disconnect callback can only be done once per context. For subsequent calls it will
|
347
|
-
return `REDIS_ERR`. The function to set the disconnect callback has the following prototype:
|
348
|
-
```c
|
349
|
-
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
|
350
|
-
```
|
351
|
-
`ac->data` may be used to pass user data to this callback, the same can be done for redisConnectCallback.
|
352
|
-
### Sending commands and their callbacks
|
353
|
-
|
354
|
-
In an asynchronous context, commands are automatically pipelined due to the nature of an event loop.
|
355
|
-
Therefore, unlike the synchronous API, there is only a single way to send commands.
|
356
|
-
Because commands are sent to Redis asynchronously, issuing a command requires a callback function
|
357
|
-
that is called when the reply is received. Reply callbacks should have the following prototype:
|
358
|
-
```c
|
359
|
-
void(redisAsyncContext *c, void *reply, void *privdata);
|
360
|
-
```
|
361
|
-
The `privdata` argument can be used to curry arbitrary data to the callback from the point where
|
362
|
-
the command is initially queued for execution.
|
363
|
-
|
364
|
-
The functions that can be used to issue commands in an asynchronous context are:
|
365
|
-
```c
|
366
|
-
int redisAsyncCommand(
|
367
|
-
redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
|
368
|
-
const char *format, ...);
|
369
|
-
int redisAsyncCommandArgv(
|
370
|
-
redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
|
371
|
-
int argc, const char **argv, const size_t *argvlen);
|
372
|
-
```
|
373
|
-
Both functions work like their blocking counterparts. The return value is `REDIS_OK` when the command
|
374
|
-
was successfully added to the output buffer and `REDIS_ERR` otherwise. Example: when the connection
|
375
|
-
is being disconnected per user-request, no new commands may be added to the output buffer and `REDIS_ERR` is
|
376
|
-
returned on calls to the `redisAsyncCommand` family.
|
377
|
-
|
378
|
-
If the reply for a command with a `NULL` callback is read, it is immediately freed. When the callback
|
379
|
-
for a command is non-`NULL`, the memory is freed immediately following the callback: the reply is only
|
380
|
-
valid for the duration of the callback.
|
381
|
-
|
382
|
-
All pending callbacks are called with a `NULL` reply when the context encountered an error.
|
383
|
-
|
384
|
-
### Disconnecting
|
385
|
-
|
386
|
-
An asynchronous connection can be terminated using:
|
387
|
-
```c
|
388
|
-
void redisAsyncDisconnect(redisAsyncContext *ac);
|
389
|
-
```
|
390
|
-
When this function is called, the connection is **not** immediately terminated. Instead, new
|
391
|
-
commands are no longer accepted and the connection is only terminated when all pending commands
|
392
|
-
have been written to the socket, their respective replies have been read and their respective
|
393
|
-
callbacks have been executed. After this, the disconnection callback is executed with the
|
394
|
-
`REDIS_OK` status and the context object is freed.
|
395
|
-
|
396
|
-
### Hooking it up to event library *X*
|
397
|
-
|
398
|
-
There are a few hooks that need to be set on the context object after it is created.
|
399
|
-
See the `adapters/` directory for bindings to *libev* and *libevent*.
|
400
|
-
|
401
|
-
## Reply parsing API
|
402
|
-
|
403
|
-
Hiredis comes with a reply parsing API that makes it easy for writing higher
|
404
|
-
level language bindings.
|
405
|
-
|
406
|
-
The reply parsing API consists of the following functions:
|
407
|
-
```c
|
408
|
-
redisReader *redisReaderCreate(void);
|
409
|
-
void redisReaderFree(redisReader *reader);
|
410
|
-
int redisReaderFeed(redisReader *reader, const char *buf, size_t len);
|
411
|
-
int redisReaderGetReply(redisReader *reader, void **reply);
|
412
|
-
```
|
413
|
-
The same set of functions are used internally by hiredis when creating a
|
414
|
-
normal Redis context, the above API just exposes it to the user for a direct
|
415
|
-
usage.
|
416
|
-
|
417
|
-
### Usage
|
418
|
-
|
419
|
-
The function `redisReaderCreate` creates a `redisReader` structure that holds a
|
420
|
-
buffer with unparsed data and state for the protocol parser.
|
421
|
-
|
422
|
-
Incoming data -- most likely from a socket -- can be placed in the internal
|
423
|
-
buffer of the `redisReader` using `redisReaderFeed`. This function will make a
|
424
|
-
copy of the buffer pointed to by `buf` for `len` bytes. This data is parsed
|
425
|
-
when `redisReaderGetReply` is called. This function returns an integer status
|
426
|
-
and a reply object (as described above) via `void **reply`. The returned status
|
427
|
-
can be either `REDIS_OK` or `REDIS_ERR`, where the latter means something went
|
428
|
-
wrong (either a protocol error, or an out of memory error).
|
429
|
-
|
430
|
-
The parser limits the level of nesting for multi bulk payloads to 7. If the
|
431
|
-
multi bulk nesting level is higher than this, the parser returns an error.
|
432
|
-
|
433
|
-
### Customizing replies
|
434
|
-
|
435
|
-
The function `redisReaderGetReply` creates `redisReply` and makes the function
|
436
|
-
argument `reply` point to the created `redisReply` variable. For instance, if
|
437
|
-
the response of type `REDIS_REPLY_STATUS` then the `str` field of `redisReply`
|
438
|
-
will hold the status as a vanilla C string. However, the functions that are
|
439
|
-
responsible for creating instances of the `redisReply` can be customized by
|
440
|
-
setting the `fn` field on the `redisReader` struct. This should be done
|
441
|
-
immediately after creating the `redisReader`.
|
442
|
-
|
443
|
-
For example, [hiredis-rb](https://github.com/pietern/hiredis-rb/blob/master/ext/hiredis_ext/reader.c)
|
444
|
-
uses customized reply object functions to create Ruby objects.
|
445
|
-
|
446
|
-
### Reader max buffer
|
447
|
-
|
448
|
-
Both when using the Reader API directly or when using it indirectly via a
|
449
|
-
normal Redis context, the redisReader structure uses a buffer in order to
|
450
|
-
accumulate data from the server.
|
451
|
-
Usually this buffer is destroyed when it is empty and is larger than 16
|
452
|
-
KiB in order to avoid wasting memory in unused buffers
|
453
|
-
|
454
|
-
However when working with very big payloads destroying the buffer may slow
|
455
|
-
down performances considerably, so it is possible to modify the max size of
|
456
|
-
an idle buffer changing the value of the `maxbuf` field of the reader structure
|
457
|
-
to the desired value. The special value of 0 means that there is no maximum
|
458
|
-
value for an idle buffer, so the buffer will never get freed.
|
459
|
-
|
460
|
-
For instance if you have a normal Redis context you can set the maximum idle
|
461
|
-
buffer to zero (unlimited) just with:
|
462
|
-
```c
|
463
|
-
context->reader->maxbuf = 0;
|
464
|
-
```
|
465
|
-
This should be done only in order to maximize performances when working with
|
466
|
-
large payloads. The context should be set back to `REDIS_READER_MAX_BUF` again
|
467
|
-
as soon as possible in order to prevent allocation of useless memory.
|
468
|
-
|
469
|
-
### Reader max array elements
|
470
|
-
|
471
|
-
By default the hiredis reply parser sets the maximum number of multi-bulk elements
|
472
|
-
to 2^32 - 1 or 4,294,967,295 entries. If you need to process multi-bulk replies
|
473
|
-
with more than this many elements you can set the value higher or to zero, meaning
|
474
|
-
unlimited with:
|
475
|
-
```c
|
476
|
-
context->reader->maxelements = 0;
|
477
|
-
```
|
478
|
-
|
479
|
-
## SSL/TLS Support
|
480
|
-
|
481
|
-
### Building
|
482
|
-
|
483
|
-
SSL/TLS support is not built by default and requires an explicit flag:
|
484
|
-
|
485
|
-
make USE_SSL=1
|
486
|
-
|
487
|
-
This requires OpenSSL development package (e.g. including header files to be
|
488
|
-
available.
|
489
|
-
|
490
|
-
When enabled, SSL/TLS support is built into extra `libhiredis_ssl.a` and
|
491
|
-
`libhiredis_ssl.so` static/dynamic libraries. This leaves the original libraries
|
492
|
-
unaffected so no additional dependencies are introduced.
|
493
|
-
|
494
|
-
### Using it
|
495
|
-
|
496
|
-
First, you'll need to make sure you include the SSL header file:
|
497
|
-
|
498
|
-
```c
|
499
|
-
#include "hiredis.h"
|
500
|
-
#include "hiredis_ssl.h"
|
501
|
-
```
|
502
|
-
|
503
|
-
You will also need to link against `libhiredis_ssl`, **in addition** to
|
504
|
-
`libhiredis` and add `-lssl -lcrypto` to satisfy its dependencies.
|
505
|
-
|
506
|
-
Hiredis implements SSL/TLS on top of its normal `redisContext` or
|
507
|
-
`redisAsyncContext`, so you will need to establish a connection first and then
|
508
|
-
initiate an SSL/TLS handshake.
|
509
|
-
|
510
|
-
#### Hiredis OpenSSL Wrappers
|
511
|
-
|
512
|
-
Before Hiredis can negotiate an SSL/TLS connection, it is necessary to
|
513
|
-
initialize OpenSSL and create a context. You can do that in two ways:
|
514
|
-
|
515
|
-
1. Work directly with the OpenSSL API to initialize the library's global context
|
516
|
-
and create `SSL_CTX *` and `SSL *` contexts. With an `SSL *` object you can
|
517
|
-
call `redisInitiateSSL()`.
|
518
|
-
2. Work with a set of Hiredis-provided wrappers around OpenSSL, create a
|
519
|
-
`redisSSLContext` object to hold configuration and use
|
520
|
-
`redisInitiateSSLWithContext()` to initiate the SSL/TLS handshake.
|
521
|
-
|
522
|
-
```c
|
523
|
-
/* An Hiredis SSL context. It holds SSL configuration and can be reused across
|
524
|
-
* many contexts.
|
525
|
-
*/
|
526
|
-
redisSSLContext *ssl;
|
527
|
-
|
528
|
-
/* An error variable to indicate what went wrong, if the context fails to
|
529
|
-
* initialize.
|
530
|
-
*/
|
531
|
-
redisSSLContextError ssl_error;
|
532
|
-
|
533
|
-
/* Initialize global OpenSSL state.
|
534
|
-
*
|
535
|
-
* You should call this only once when your app initializes, and only if
|
536
|
-
* you don't explicitly or implicitly initialize OpenSSL it elsewhere.
|
537
|
-
*/
|
538
|
-
redisInitOpenSSL();
|
539
|
-
|
540
|
-
/* Create SSL context */
|
541
|
-
ssl = redisCreateSSLContext(
|
542
|
-
"cacertbundle.crt", /* File name of trusted CA/ca bundle file, optional */
|
543
|
-
"/path/to/certs", /* Path of trusted certificates, optional */
|
544
|
-
"client_cert.pem", /* File name of client certificate file, optional */
|
545
|
-
"client_key.pem", /* File name of client private key, optional */
|
546
|
-
"redis.mydomain.com", /* Server name to request (SNI), optional */
|
547
|
-
&ssl_error
|
548
|
-
) != REDIS_OK) {
|
549
|
-
printf("SSL error: %s\n", redisSSLContextGetError(ssl_error);
|
550
|
-
/* Abort... */
|
551
|
-
}
|
552
|
-
|
553
|
-
/* Create Redis context and establish connection */
|
554
|
-
c = redisConnect("localhost", 6443);
|
555
|
-
if (c == NULL || c->err) {
|
556
|
-
/* Handle error and abort... */
|
557
|
-
}
|
558
|
-
|
559
|
-
/* Negotiate SSL/TLS */
|
560
|
-
if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) {
|
561
|
-
/* Handle error, in c->err / c->errstr */
|
562
|
-
}
|
563
|
-
```
|
564
|
-
|
565
|
-
## RESP3 PUSH replies
|
566
|
-
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.
|
567
|
-
|
568
|
-
### Default behavior
|
569
|
-
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`.
|
570
|
-
|
571
|
-
### Custom PUSH handler prototypes
|
572
|
-
The callback prototypes differ between `redisContext` and `redisAsyncContext`.
|
573
|
-
|
574
|
-
#### redisContext
|
575
|
-
```c
|
576
|
-
void my_push_handler(void *privdata, void *reply) {
|
577
|
-
/* Handle the reply */
|
578
|
-
|
579
|
-
/* Note: We need to free the reply in our custom handler for
|
580
|
-
blocking contexts. This lets us keep the reply if
|
581
|
-
we want. */
|
582
|
-
freeReplyObject(reply);
|
583
|
-
}
|
584
|
-
```
|
585
|
-
|
586
|
-
#### redisAsyncContext
|
587
|
-
```c
|
588
|
-
void my_async_push_handler(redisAsyncContext *ac, void *reply) {
|
589
|
-
/* Handle the reply */
|
590
|
-
|
591
|
-
/* Note: Because async hiredis always frees replies, you should
|
592
|
-
not call freeReplyObject in an async push callback. */
|
593
|
-
}
|
594
|
-
```
|
595
|
-
|
596
|
-
### Installing a custom handler
|
597
|
-
There are two ways to set your own PUSH handlers.
|
598
|
-
|
599
|
-
1. Set `push_cb` or `async_push_cb` in the `redisOptions` struct and connect with `redisConnectWithOptions` or `redisAsyncConnectWithOptions`.
|
600
|
-
```c
|
601
|
-
redisOptions = {0};
|
602
|
-
REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
|
603
|
-
options->push_cb = my_push_handler;
|
604
|
-
redisContext *context = redisConnectWithOptions(&options);
|
605
|
-
```
|
606
|
-
2. Call `redisSetPushCallback` or `redisAsyncSetPushCallback` on a connected context.
|
607
|
-
```c
|
608
|
-
redisContext *context = redisConnect("127.0.0.1", 6379);
|
609
|
-
redisSetPushCallback(context, my_push_handler);
|
610
|
-
```
|
611
|
-
|
612
|
-
_Note `redisSetPushCallback` and `redisAsyncSetPushCallback` both return any currently configured handler, making it easy to override and then return to the old value._
|
613
|
-
|
614
|
-
### Specifying no handler
|
615
|
-
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.
|
616
|
-
1. Set the `REDIS_OPT_NO_PUSH_AUTOFREE` flag in `redisOptions` and leave the callback function pointer `NULL`.
|
617
|
-
```c
|
618
|
-
redisOptions = {0};
|
619
|
-
REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
|
620
|
-
options->options |= REDIS_OPT_NO_PUSH_AUTOFREE;
|
621
|
-
redisContext *context = redisConnectWithOptions(&options);
|
622
|
-
```
|
623
|
-
3. Call `redisSetPushCallback` with `NULL` once connected.
|
624
|
-
```c
|
625
|
-
redisContext *context = redisConnect("127.0.0.1", 6379);
|
626
|
-
redisSetPushCallback(context, NULL);
|
627
|
-
```
|
628
|
-
|
629
|
-
_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)._
|
630
|
-
|
631
|
-
## Allocator injection
|
632
|
-
|
633
|
-
Hiredis uses a pass-thru structure of function pointers defined in [alloc.h](https://github.com/redis/hiredis/blob/f5d25850/alloc.h#L41) that contain the currently configured allocation and deallocation functions. By default they just point to libc (`malloc`, `calloc`, `realloc`, etc).
|
634
|
-
|
635
|
-
### Overriding
|
636
|
-
|
637
|
-
One can override the allocators like so:
|
638
|
-
|
639
|
-
```c
|
640
|
-
hiredisAllocFuncs myfuncs = {
|
641
|
-
.mallocFn = my_malloc,
|
642
|
-
.callocFn = my_calloc,
|
643
|
-
.reallocFn = my_realloc,
|
644
|
-
.strdupFn = my_strdup,
|
645
|
-
.freeFn = my_free,
|
646
|
-
};
|
647
|
-
|
648
|
-
// Override allocators (function returns current allocators if needed)
|
649
|
-
hiredisAllocFuncs orig = hiredisSetAllocators(&myfuncs);
|
650
|
-
```
|
651
|
-
|
652
|
-
To reset the allocators to their default libc function simply call:
|
653
|
-
|
654
|
-
```c
|
655
|
-
hiredisResetAllocators();
|
656
|
-
```
|
657
|
-
|
658
|
-
## AUTHORS
|
659
|
-
|
660
|
-
Salvatore Sanfilippo (antirez at gmail),\
|
661
|
-
Pieter Noordhuis (pcnoordhuis at gmail)\
|
662
|
-
Michael Grunder (michael dot grunder at gmail)
|
663
|
-
|
664
|
-
_Hiredis is released under the BSD license._
|