hiredis-client 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +5 -0
  3. data/ext/redis_client/hiredis/export.clang +2 -0
  4. data/ext/redis_client/hiredis/export.gcc +7 -0
  5. data/ext/redis_client/hiredis/extconf.rb +69 -0
  6. data/ext/redis_client/hiredis/hiredis_connection.c +708 -0
  7. data/ext/redis_client/hiredis/vendor/.gitignore +9 -0
  8. data/ext/redis_client/hiredis/vendor/.travis.yml +131 -0
  9. data/ext/redis_client/hiredis/vendor/CHANGELOG.md +364 -0
  10. data/ext/redis_client/hiredis/vendor/CMakeLists.txt +165 -0
  11. data/ext/redis_client/hiredis/vendor/COPYING +29 -0
  12. data/ext/redis_client/hiredis/vendor/Makefile +308 -0
  13. data/ext/redis_client/hiredis/vendor/README.md +664 -0
  14. data/ext/redis_client/hiredis/vendor/adapters/ae.h +130 -0
  15. data/ext/redis_client/hiredis/vendor/adapters/glib.h +156 -0
  16. data/ext/redis_client/hiredis/vendor/adapters/ivykis.h +84 -0
  17. data/ext/redis_client/hiredis/vendor/adapters/libev.h +179 -0
  18. data/ext/redis_client/hiredis/vendor/adapters/libevent.h +175 -0
  19. data/ext/redis_client/hiredis/vendor/adapters/libuv.h +117 -0
  20. data/ext/redis_client/hiredis/vendor/adapters/macosx.h +115 -0
  21. data/ext/redis_client/hiredis/vendor/adapters/qt.h +135 -0
  22. data/ext/redis_client/hiredis/vendor/alloc.c +86 -0
  23. data/ext/redis_client/hiredis/vendor/alloc.h +91 -0
  24. data/ext/redis_client/hiredis/vendor/appveyor.yml +24 -0
  25. data/ext/redis_client/hiredis/vendor/async.c +887 -0
  26. data/ext/redis_client/hiredis/vendor/async.h +147 -0
  27. data/ext/redis_client/hiredis/vendor/async_private.h +75 -0
  28. data/ext/redis_client/hiredis/vendor/dict.c +352 -0
  29. data/ext/redis_client/hiredis/vendor/dict.h +126 -0
  30. data/ext/redis_client/hiredis/vendor/fmacros.h +12 -0
  31. data/ext/redis_client/hiredis/vendor/hiredis-config.cmake.in +13 -0
  32. data/ext/redis_client/hiredis/vendor/hiredis.c +1174 -0
  33. data/ext/redis_client/hiredis/vendor/hiredis.h +336 -0
  34. data/ext/redis_client/hiredis/vendor/hiredis.pc.in +12 -0
  35. data/ext/redis_client/hiredis/vendor/hiredis_ssl-config.cmake.in +13 -0
  36. data/ext/redis_client/hiredis/vendor/hiredis_ssl.h +157 -0
  37. data/ext/redis_client/hiredis/vendor/hiredis_ssl.pc.in +12 -0
  38. data/ext/redis_client/hiredis/vendor/net.c +612 -0
  39. data/ext/redis_client/hiredis/vendor/net.h +56 -0
  40. data/ext/redis_client/hiredis/vendor/read.c +739 -0
  41. data/ext/redis_client/hiredis/vendor/read.h +129 -0
  42. data/ext/redis_client/hiredis/vendor/sds.c +1289 -0
  43. data/ext/redis_client/hiredis/vendor/sds.h +278 -0
  44. data/ext/redis_client/hiredis/vendor/sdsalloc.h +44 -0
  45. data/ext/redis_client/hiredis/vendor/sockcompat.c +248 -0
  46. data/ext/redis_client/hiredis/vendor/sockcompat.h +92 -0
  47. data/ext/redis_client/hiredis/vendor/ssl.c +544 -0
  48. data/ext/redis_client/hiredis/vendor/test.c +1401 -0
  49. data/ext/redis_client/hiredis/vendor/test.sh +78 -0
  50. data/ext/redis_client/hiredis/vendor/win32.h +56 -0
  51. data/hiredis-client.gemspec +33 -0
  52. data/lib/hiredis-client.rb +11 -0
  53. data/lib/redis_client/hiredis_connection.rb +94 -0
  54. metadata +114 -0
@@ -0,0 +1,544 @@
1
+ /*
2
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
3
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
4
+ * Copyright (c) 2019, Redis Labs
5
+ *
6
+ * All rights reserved.
7
+ *
8
+ * Redistribution and use in source and binary forms, with or without
9
+ * modification, are permitted provided that the following conditions are met:
10
+ *
11
+ * * Redistributions of source code must retain the above copyright notice,
12
+ * this list of conditions and the following disclaimer.
13
+ * * Redistributions in binary form must reproduce the above copyright
14
+ * notice, this list of conditions and the following disclaimer in the
15
+ * documentation and/or other materials provided with the distribution.
16
+ * * Neither the name of Redis nor the names of its contributors may be used
17
+ * to endorse or promote products derived from this software without
18
+ * specific prior written permission.
19
+ *
20
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ * POSSIBILITY OF SUCH DAMAGE.
31
+ */
32
+
33
+ #include "hiredis.h"
34
+ #include "async.h"
35
+
36
+ #include <assert.h>
37
+ #include <errno.h>
38
+ #include <string.h>
39
+ #ifdef _WIN32
40
+ #include <windows.h>
41
+ #else
42
+ #include <pthread.h>
43
+ #endif
44
+
45
+ #include <openssl/ssl.h>
46
+ #include <openssl/err.h>
47
+
48
+ #include "win32.h"
49
+ #include "async_private.h"
50
+ #include "hiredis_ssl.h"
51
+
52
+ void __redisSetError(redisContext *c, int type, const char *str);
53
+
54
+ struct redisSSLContext {
55
+ /* Associated OpenSSL SSL_CTX as created by redisCreateSSLContext() */
56
+ SSL_CTX *ssl_ctx;
57
+
58
+ /* Requested SNI, or NULL */
59
+ char *server_name;
60
+ };
61
+
62
+ /* Forward declaration */
63
+ redisContextFuncs redisContextSSLFuncs;
64
+
65
+ /**
66
+ * OpenSSL global initialization and locking handling callbacks.
67
+ * Note that this is only required for OpenSSL < 1.1.0.
68
+ */
69
+
70
+ #if OPENSSL_VERSION_NUMBER < 0x10100000L
71
+ #define HIREDIS_USE_CRYPTO_LOCKS
72
+ #endif
73
+
74
+ #ifdef HIREDIS_USE_CRYPTO_LOCKS
75
+ #ifdef _WIN32
76
+ typedef CRITICAL_SECTION sslLockType;
77
+ static void sslLockInit(sslLockType* l) {
78
+ InitializeCriticalSection(l);
79
+ }
80
+ static void sslLockAcquire(sslLockType* l) {
81
+ EnterCriticalSection(l);
82
+ }
83
+ static void sslLockRelease(sslLockType* l) {
84
+ LeaveCriticalSection(l);
85
+ }
86
+ #else
87
+ typedef pthread_mutex_t sslLockType;
88
+ static void sslLockInit(sslLockType *l) {
89
+ pthread_mutex_init(l, NULL);
90
+ }
91
+ static void sslLockAcquire(sslLockType *l) {
92
+ pthread_mutex_lock(l);
93
+ }
94
+ static void sslLockRelease(sslLockType *l) {
95
+ pthread_mutex_unlock(l);
96
+ }
97
+ #endif
98
+
99
+ static sslLockType* ossl_locks;
100
+
101
+ static void opensslDoLock(int mode, int lkid, const char *f, int line) {
102
+ sslLockType *l = ossl_locks + lkid;
103
+
104
+ if (mode & CRYPTO_LOCK) {
105
+ sslLockAcquire(l);
106
+ } else {
107
+ sslLockRelease(l);
108
+ }
109
+
110
+ (void)f;
111
+ (void)line;
112
+ }
113
+
114
+ static int initOpensslLocks(void) {
115
+ unsigned ii, nlocks;
116
+ if (CRYPTO_get_locking_callback() != NULL) {
117
+ /* Someone already set the callback before us. Don't destroy it! */
118
+ return REDIS_OK;
119
+ }
120
+ nlocks = CRYPTO_num_locks();
121
+ ossl_locks = hi_malloc(sizeof(*ossl_locks) * nlocks);
122
+ if (ossl_locks == NULL)
123
+ return REDIS_ERR;
124
+
125
+ for (ii = 0; ii < nlocks; ii++) {
126
+ sslLockInit(ossl_locks + ii);
127
+ }
128
+ CRYPTO_set_locking_callback(opensslDoLock);
129
+ return REDIS_OK;
130
+ }
131
+ #endif /* HIREDIS_USE_CRYPTO_LOCKS */
132
+
133
+ int redisInitOpenSSL(void)
134
+ {
135
+ SSL_library_init();
136
+ #ifdef HIREDIS_USE_CRYPTO_LOCKS
137
+ initOpensslLocks();
138
+ #endif
139
+
140
+ return REDIS_OK;
141
+ }
142
+
143
+ static int maybeCheckWant(redisSSL *rssl, int rv) {
144
+ /**
145
+ * If the error is WANT_READ or WANT_WRITE, the appropriate flags are set
146
+ * and true is returned. False is returned otherwise
147
+ */
148
+ if (rv == SSL_ERROR_WANT_READ) {
149
+ rssl->wantRead = 1;
150
+ return 1;
151
+ } else if (rv == SSL_ERROR_WANT_WRITE) {
152
+ rssl->pendingWrite = 1;
153
+ return 1;
154
+ } else {
155
+ return 0;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * redisSSLContext helper context destruction.
161
+ */
162
+
163
+ const char *redisSSLContextGetError(redisSSLContextError error)
164
+ {
165
+ switch (error) {
166
+ case REDIS_SSL_CTX_NONE:
167
+ return "No Error";
168
+ case REDIS_SSL_CTX_CREATE_FAILED:
169
+ return "Failed to create OpenSSL SSL_CTX";
170
+ case REDIS_SSL_CTX_CERT_KEY_REQUIRED:
171
+ return "Client cert and key must both be specified or skipped";
172
+ case REDIS_SSL_CTX_CA_CERT_LOAD_FAILED:
173
+ return "Failed to load CA Certificate or CA Path";
174
+ case REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED:
175
+ return "Failed to load client certificate";
176
+ case REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED:
177
+ return "Failed to load private key";
178
+ default:
179
+ return "Unknown error code";
180
+ }
181
+ }
182
+
183
+ void redisFreeSSLContext(redisSSLContext *ctx)
184
+ {
185
+ if (!ctx)
186
+ return;
187
+
188
+ if (ctx->server_name) {
189
+ hi_free(ctx->server_name);
190
+ ctx->server_name = NULL;
191
+ }
192
+
193
+ if (ctx->ssl_ctx) {
194
+ SSL_CTX_free(ctx->ssl_ctx);
195
+ ctx->ssl_ctx = NULL;
196
+ }
197
+
198
+ hi_free(ctx);
199
+ }
200
+
201
+
202
+ /**
203
+ * redisSSLContext helper context initialization.
204
+ */
205
+
206
+ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath,
207
+ const char *cert_filename, const char *private_key_filename,
208
+ const char *server_name, redisSSLContextError *error)
209
+ {
210
+ redisSSLContext *ctx = hi_calloc(1, sizeof(redisSSLContext));
211
+ if (ctx == NULL)
212
+ goto error;
213
+
214
+ ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
215
+ if (!ctx->ssl_ctx) {
216
+ if (error) *error = REDIS_SSL_CTX_CREATE_FAILED;
217
+ goto error;
218
+ }
219
+
220
+ SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
221
+ SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
222
+
223
+ if ((cert_filename != NULL && private_key_filename == NULL) ||
224
+ (private_key_filename != NULL && cert_filename == NULL)) {
225
+ if (error) *error = REDIS_SSL_CTX_CERT_KEY_REQUIRED;
226
+ goto error;
227
+ }
228
+
229
+ if (capath || cacert_filename) {
230
+ if (!SSL_CTX_load_verify_locations(ctx->ssl_ctx, cacert_filename, capath)) {
231
+ if (error) *error = REDIS_SSL_CTX_CA_CERT_LOAD_FAILED;
232
+ goto error;
233
+ }
234
+ }
235
+
236
+ if (cert_filename) {
237
+ if (!SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, cert_filename)) {
238
+ if (error) *error = REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED;
239
+ goto error;
240
+ }
241
+ if (!SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, private_key_filename, SSL_FILETYPE_PEM)) {
242
+ if (error) *error = REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED;
243
+ goto error;
244
+ }
245
+ }
246
+
247
+ if (server_name)
248
+ ctx->server_name = hi_strdup(server_name);
249
+
250
+ return ctx;
251
+
252
+ error:
253
+ redisFreeSSLContext(ctx);
254
+ return NULL;
255
+ }
256
+
257
+ int redisInitiateSSLContinue(redisContext *c) {
258
+ if (!c->privctx) {
259
+ __redisSetError(c, REDIS_ERR_OTHER, "redisContext is not associated");
260
+ return REDIS_ERR;
261
+ }
262
+
263
+ redisSSL *rssl = (redisSSL *)c->privctx;
264
+ ERR_clear_error();
265
+ int rv = SSL_connect(rssl->ssl);
266
+ if (rv == 1) {
267
+ c->privctx = rssl;
268
+ return REDIS_OK;
269
+ }
270
+
271
+ rv = SSL_get_error(rssl->ssl, rv);
272
+ if (((c->flags & REDIS_BLOCK) == 0) &&
273
+ (rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {
274
+ maybeCheckWant(rssl, rv);
275
+ c->privctx = rssl;
276
+ return REDIS_OK;
277
+ }
278
+
279
+ if (c->err == 0) {
280
+ char err[512];
281
+ if (rv == SSL_ERROR_SYSCALL)
282
+ snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",strerror(errno));
283
+ else {
284
+ unsigned long e = ERR_peek_last_error();
285
+ snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",
286
+ ERR_reason_error_string(e));
287
+ }
288
+ __redisSetError(c, REDIS_ERR_IO, err);
289
+ }
290
+ return REDIS_ERR;
291
+ }
292
+
293
+ /**
294
+ * SSL Connection initialization.
295
+ */
296
+
297
+
298
+ static int redisSSLConnect(redisContext *c, SSL *ssl) {
299
+ if (c->privctx) {
300
+ __redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated");
301
+ return REDIS_ERR;
302
+ }
303
+
304
+ redisSSL *rssl = hi_calloc(1, sizeof(redisSSL));
305
+ if (rssl == NULL) {
306
+ __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
307
+ return REDIS_ERR;
308
+ }
309
+
310
+ c->funcs = &redisContextSSLFuncs;
311
+ rssl->ssl = ssl;
312
+
313
+ SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
314
+ SSL_set_fd(rssl->ssl, c->fd);
315
+ SSL_set_connect_state(rssl->ssl);
316
+
317
+ ERR_clear_error();
318
+ int rv = SSL_connect(rssl->ssl);
319
+ if (rv == 1) {
320
+ c->privctx = rssl;
321
+ return REDIS_OK;
322
+ }
323
+
324
+ rv = SSL_get_error(rssl->ssl, rv);
325
+ if (((c->flags & REDIS_BLOCK) == 0) &&
326
+ (rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {
327
+ maybeCheckWant(rssl, rv);
328
+ c->privctx = rssl;
329
+ return REDIS_OK;
330
+ }
331
+
332
+ if (c->err == 0) {
333
+ char err[512];
334
+ if (rv == SSL_ERROR_SYSCALL)
335
+ snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",strerror(errno));
336
+ else {
337
+ unsigned long e = ERR_peek_last_error();
338
+ snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",
339
+ ERR_reason_error_string(e));
340
+ }
341
+ __redisSetError(c, REDIS_ERR_IO, err);
342
+ }
343
+
344
+ hi_free(rssl);
345
+ return REDIS_ERR;
346
+ }
347
+
348
+ redisSSL *redisGetSSLSocket(redisContext *c) {
349
+ return c->privctx;
350
+ }
351
+
352
+ /**
353
+ * A wrapper around redisSSLConnect() for users who manage their own context and
354
+ * create their own SSL object.
355
+ */
356
+
357
+ int redisInitiateSSL(redisContext *c, SSL *ssl) {
358
+ return redisSSLConnect(c, ssl);
359
+ }
360
+
361
+ /**
362
+ * A wrapper around redisSSLConnect() for users who use redisSSLContext and don't
363
+ * manage their own SSL objects.
364
+ */
365
+
366
+ int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx)
367
+ {
368
+ if (!c || !redis_ssl_ctx)
369
+ return REDIS_ERR;
370
+
371
+ /* We want to verify that redisSSLConnect() won't fail on this, as it will
372
+ * not own the SSL object in that case and we'll end up leaking.
373
+ */
374
+ if (c->privctx)
375
+ return REDIS_ERR;
376
+
377
+ SSL *ssl = SSL_new(redis_ssl_ctx->ssl_ctx);
378
+ if (!ssl) {
379
+ __redisSetError(c, REDIS_ERR_OTHER, "Couldn't create new SSL instance");
380
+ goto error;
381
+ }
382
+
383
+ if (redis_ssl_ctx->server_name) {
384
+ if (!SSL_set_tlsext_host_name(ssl, redis_ssl_ctx->server_name)) {
385
+ __redisSetError(c, REDIS_ERR_OTHER, "Failed to set server_name/SNI");
386
+ goto error;
387
+ }
388
+ }
389
+
390
+ return redisSSLConnect(c, ssl);
391
+
392
+ error:
393
+ if (ssl)
394
+ SSL_free(ssl);
395
+ return REDIS_ERR;
396
+ }
397
+
398
+ /**
399
+ * Implementation of redisContextFuncs for SSL connections.
400
+ */
401
+
402
+ static void redisSSLFree(void *privctx){
403
+ redisSSL *rsc = privctx;
404
+
405
+ if (!rsc) return;
406
+ if (rsc->ssl) {
407
+ SSL_free(rsc->ssl);
408
+ rsc->ssl = NULL;
409
+ }
410
+ hi_free(rsc);
411
+ }
412
+
413
+ static ssize_t redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
414
+ redisSSL *rssl = c->privctx;
415
+
416
+ int nread = SSL_read(rssl->ssl, buf, bufcap);
417
+ if (nread > 0) {
418
+ return nread;
419
+ } else if (nread == 0) {
420
+ __redisSetError(c, REDIS_ERR_EOF, "Server closed the connection");
421
+ return -1;
422
+ } else {
423
+ int err = SSL_get_error(rssl->ssl, nread);
424
+ if (c->flags & REDIS_BLOCK) {
425
+ /**
426
+ * In blocking mode, we should never end up in a situation where
427
+ * we get an error without it being an actual error, except
428
+ * in the case of EINTR, which can be spuriously received from
429
+ * debuggers or whatever.
430
+ */
431
+ if (errno == EINTR) {
432
+ return 0;
433
+ } else {
434
+ const char *msg = NULL;
435
+ if (errno == EAGAIN) {
436
+ msg = "Resource temporarily unavailable";
437
+ }
438
+ __redisSetError(c, REDIS_ERR_IO, msg);
439
+ return -1;
440
+ }
441
+ }
442
+
443
+ /**
444
+ * We can very well get an EWOULDBLOCK/EAGAIN, however
445
+ */
446
+ if (maybeCheckWant(rssl, err)) {
447
+ return 0;
448
+ } else {
449
+ __redisSetError(c, REDIS_ERR_IO, NULL);
450
+ return -1;
451
+ }
452
+ }
453
+ }
454
+
455
+ static ssize_t redisSSLWrite(redisContext *c) {
456
+ redisSSL *rssl = c->privctx;
457
+
458
+ size_t len = rssl->lastLen ? rssl->lastLen : sdslen(c->obuf);
459
+ int rv = SSL_write(rssl->ssl, c->obuf, len);
460
+
461
+ if (rv > 0) {
462
+ rssl->lastLen = 0;
463
+ } else if (rv < 0) {
464
+ rssl->lastLen = len;
465
+
466
+ int err = SSL_get_error(rssl->ssl, rv);
467
+ if ((c->flags & REDIS_BLOCK) == 0 && maybeCheckWant(rssl, err)) {
468
+ return 0;
469
+ } else {
470
+ __redisSetError(c, REDIS_ERR_IO, NULL);
471
+ return -1;
472
+ }
473
+ }
474
+ return rv;
475
+ }
476
+
477
+ static void redisSSLAsyncRead(redisAsyncContext *ac) {
478
+ int rv;
479
+ redisSSL *rssl = ac->c.privctx;
480
+ redisContext *c = &ac->c;
481
+
482
+ rssl->wantRead = 0;
483
+
484
+ if (rssl->pendingWrite) {
485
+ int done;
486
+
487
+ /* This is probably just a write event */
488
+ rssl->pendingWrite = 0;
489
+ rv = redisBufferWrite(c, &done);
490
+ if (rv == REDIS_ERR) {
491
+ __redisAsyncDisconnect(ac);
492
+ return;
493
+ } else if (!done) {
494
+ _EL_ADD_WRITE(ac);
495
+ }
496
+ }
497
+
498
+ rv = redisBufferRead(c);
499
+ if (rv == REDIS_ERR) {
500
+ __redisAsyncDisconnect(ac);
501
+ } else {
502
+ _EL_ADD_READ(ac);
503
+ redisProcessCallbacks(ac);
504
+ }
505
+ }
506
+
507
+ static void redisSSLAsyncWrite(redisAsyncContext *ac) {
508
+ int rv, done = 0;
509
+ redisSSL *rssl = ac->c.privctx;
510
+ redisContext *c = &ac->c;
511
+
512
+ rssl->pendingWrite = 0;
513
+ rv = redisBufferWrite(c, &done);
514
+ if (rv == REDIS_ERR) {
515
+ __redisAsyncDisconnect(ac);
516
+ return;
517
+ }
518
+
519
+ if (!done) {
520
+ if (rssl->wantRead) {
521
+ /* Need to read-before-write */
522
+ rssl->pendingWrite = 1;
523
+ _EL_DEL_WRITE(ac);
524
+ } else {
525
+ /* No extra reads needed, just need to write more */
526
+ _EL_ADD_WRITE(ac);
527
+ }
528
+ } else {
529
+ /* Already done! */
530
+ _EL_DEL_WRITE(ac);
531
+ }
532
+
533
+ /* Always reschedule a read */
534
+ _EL_ADD_READ(ac);
535
+ }
536
+
537
+ redisContextFuncs redisContextSSLFuncs = {
538
+ .free_privctx = redisSSLFree,
539
+ .async_read = redisSSLAsyncRead,
540
+ .async_write = redisSSLAsyncWrite,
541
+ .read = redisSSLRead,
542
+ .write = redisSSLWrite
543
+ };
544
+