Apache HTTPD
testredis.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "testutil.h"
18#include "apr.h"
19#include "apu.h"
20#include "apr_general.h"
21#include "apr_strings.h"
22#include "apr_hash.h"
23#include "apr_redis.h"
24#include "apr_network_io.h"
25
26#include <stdio.h>
27#if APR_HAVE_STDLIB_H
28#include <stdlib.h> /* for exit() */
29#endif
30
31#define HOST "localhost"
32#define PORT 6379
33
34/* the total number of items to use for set/get testing */
35#define TDATA_SIZE 3000
36
37/* some smaller subset of TDATA_SIZE used for multiget testing */
38#define TDATA_SET 100
39
40/* our custom hash function just returns this all the time */
41#define HASH_FUNC_RESULT 510
42
43/* all keys will be prefixed with this */
44static const char prefix[] = "testredis";
45
46/* text for values we store */
47static const char txt[] =
48"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis at"
49"lacus in ligula hendrerit consectetuer. Vestibulum tristique odio"
50"iaculis leo. In massa arcu, ultricies a, laoreet nec, hendrerit non,"
51"neque. Nulla sagittis sapien ac risus. Morbi ligula dolor, vestibulum"
52"nec, viverra id, placerat dapibus, arcu. Curabitur egestas feugiat"
53"tellus. Donec dignissim. Nunc ante. Curabitur id lorem. In mollis"
54"tortor sit amet eros auctor dapibus. Proin nulla sem, tristique in,"
55"convallis id, iaculis feugiat cras amet.";
56
57/*
58 * this datatype is for our custom server determination function. this might
59 * be useful if you don't want to rely on simply hashing keys to determine
60 * where a key belongs, but instead want to write something fancy, or use some
61 * other kind of configuration data, i.e. a hash plus some data about a
62 * namespace, or whatever. see my_server_func, and test_redis_user_funcs
63 * for the examples.
64 */
65typedef struct {
66 const char *someval;
67 apr_uint32_t which_server;
69
70
71/* this could do something fancy and return some hash result.
72 * for simplicity, just return the same value, so we can test it later on.
73 * if you wanted to use some external hashing library or functions for
74 * consistent hashing, for example, this would be a good place to do it.
75 */
76static apr_uint32_t my_hash_func(void *baton, const char *data,
78{
79
80 return HASH_FUNC_RESULT;
81}
82
83/*
84 * a fancy function to determine which server to use given some kind of data
85 * and a hash value. this example actually ignores the hash value itself
86 * and pulls some number from the *baton, which is a struct that has some
87 * kind of meaningful stuff in it.
88 */
91 const apr_uint32_t hash)
92{
95
96 if(mc->ntotal == 0) {
97 return NULL;
98 }
99
100 if(mc->ntotal < mhsb->which_server) {
101 return NULL;
102 }
103
104 ms = mc->live_servers[mhsb->which_server - 1];
105
106 return ms;
107}
108
111{
112 apr_uint32_t i = 0;
113 double d = 0;
114
115 if (firsttime == 0) {
116 srand((unsigned) (getpid()));
117 firsttime = 1;
118 }
119
120 d = (double) rand() / ((double) RAND_MAX + 1);
121 i = (int) (d * (high - 0 + 1));
122
123 return i > 0 ? i : 1;
124}
125
126/*
127 * general test to make sure we can create the redis struct and add
128 * some servers, but not more than we tell it we can add
129 */
130
131static void test_redis_create(abts_case * tc, void *data)
132{
133 apr_pool_t *pool = p;
134 apr_status_t rv;
140
142 ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
143
144 for (i = 1; i <= max_servers; i++) {
146
147 port = PORT + i;
148 rv =
149 apr_redis_server_create(pool, HOST, PORT + i, 0, 1, 1, 60, 60, &server);
150 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
151
153 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
154
156 ABTS_PTR_EQUAL(tc, server, s);
157
159 ABTS_ASSERT(tc, "server disable failed", rv == APR_SUCCESS);
160
162 ABTS_ASSERT(tc, "server enable failed", rv == APR_SUCCESS);
163
165 ABTS_ASSERT(tc, "hash failed", hash > 0);
166
168 ABTS_PTR_NOTNULL(tc, s);
169 }
170
171 rv = apr_redis_server_create(pool, HOST, PORT, 0, 1, 1, 60, 60, &server);
172 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
173
175 ABTS_ASSERT(tc, "server add should have failed", rv != APR_SUCCESS);
176
177}
178
179/* install our own custom hashing and server selection routines. */
180
182{
183 int i;
184
185 for (i = 0; i < TDATA_SIZE; i++) {
186 char *k, *v;
187
188 k = apr_pstrcat(p, prefix, apr_itoa(p, i), NULL);
189 v = apr_pstrndup(p, txt, randval((apr_uint32_t)strlen(txt)));
190
192 }
193
194 return i;
195}
196
197static void test_redis_user_funcs(abts_case * tc, void *data)
198{
199 apr_pool_t *pool = p;
200 apr_status_t rv;
208
210 ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
211
212 /* as noted above, install our custom hash function, and call
213 * apr_redis_hash. the return value should be our predefined number,
214 * and our function just ignores the other args, for simplicity.
215 */
216 redis->hash_func = my_hash_func;
217
218 hres = apr_redis_hash(redis, "whatever", sizeof("whatever") - 1);
220
221 /* add some servers */
222 for(i = 1; i <= 10; i++) {
224
225 rv = apr_redis_server_create(pool, HOST, i, 0, 1, 1, 60, 60, &ms);
226 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
227
229 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
230 }
231
232 /*
233 * set 'which_server' in our server_baton to find the third server
234 * which should have the same port.
235 */
236 baton->which_server = 3;
237 redis->server_func = my_server_func;
238 redis->server_baton = baton;
240 ABTS_ASSERT(tc, "wrong server found", found->port == baton->which_server);
241}
242
243/* test non data related commands like stats and version */
244static void test_redis_meta(abts_case * tc, void *data)
245{
246 apr_pool_t *pool = p;
250 char *result;
251 apr_status_t rv;
252
253 rv = apr_redis_create(pool, 1, 0, &redis);
254 ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
255
256 rv = apr_redis_server_create(pool, HOST, PORT, 0, 1, 1, 60, 60, &server);
257 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
258
260 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
261
264
267
268 /*
269 * no way to know exactly what will be in most of these, so
270 * just make sure there is something.
271 */
272 ABTS_ASSERT(tc, "major", stats->major >= 1);
273 ABTS_ASSERT(tc, "minor", stats->minor >= 0);
274 ABTS_ASSERT(tc, "patch", stats->patch >= 0);
275 ABTS_ASSERT(tc, "process_id", stats->process_id >= 0);
276 ABTS_ASSERT(tc, "uptime_in_seconds", stats->uptime_in_seconds >= 0);
277 ABTS_ASSERT(tc, "arch_bits", stats->arch_bits >= 0);
278 ABTS_ASSERT(tc, "connected_clients", stats->connected_clients >= 0);
279 ABTS_ASSERT(tc, "blocked_clients", stats->blocked_clients >= 0);
280 ABTS_ASSERT(tc, "maxmemory", stats->maxmemory >= 0);
281 ABTS_ASSERT(tc, "used_memory", stats->used_memory >= 0);
282 ABTS_ASSERT(tc, "total_system_memory", stats->total_system_memory >= 0);
283 ABTS_ASSERT(tc, "total_connections_received", stats->total_connections_received >= 0);
284 ABTS_ASSERT(tc, "total_commands_processed", stats->total_commands_processed >= 0);
285 ABTS_ASSERT(tc, "total_net_input_bytes", stats->total_net_input_bytes >= 0);
286 ABTS_ASSERT(tc, "total_net_output_bytes", stats->total_net_output_bytes >= 0);
287 ABTS_ASSERT(tc, "keyspace_hits", stats->keyspace_hits >= 0);
288 ABTS_ASSERT(tc, "keyspace_misses", stats->keyspace_misses >= 0);
289 ABTS_ASSERT(tc, "role", stats->role >= 0);
290 ABTS_ASSERT(tc, "connected_slaves", stats->connected_slaves >= 0);
291 ABTS_ASSERT(tc, "used_cpu_sys", stats->used_cpu_sys >= 0);
292 ABTS_ASSERT(tc, "used_cpu_user", stats->used_cpu_user >= 0);
293 ABTS_ASSERT(tc, "cluster_enabled", stats->cluster_enabled >= 0);
294}
295
296
297/* basic tests of the increment and decrement commands */
298static void test_redis_incrdecr(abts_case * tc, void *data)
299{
300 apr_pool_t *pool = p;
301 apr_status_t rv;
304 apr_uint32_t new;
305 char *result;
308
309 rv = apr_redis_create(pool, 1, 0, &redis);
310 ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
311
312 rv = apr_redis_server_create(pool, HOST, PORT, 0, 1, 1, 60, 60, &server);
313 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
314
316 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
317
318 rv = apr_redis_set(redis, prefix, "271", sizeof("271") - 1, 27);
319 ABTS_ASSERT(tc, "set failed", rv == APR_SUCCESS);
320
321 for( i = 1; i <= TDATA_SIZE; i++) {
323
325 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
326
327 expect = i + atoi(result);
328
329 rv = apr_redis_incr(redis, prefix, i, &new);
330 ABTS_ASSERT(tc, "incr failed", rv == APR_SUCCESS);
331
332 ABTS_INT_EQUAL(tc, expect, new);
333
334 rv = apr_redis_decr(redis, prefix, i, &new);
335 ABTS_ASSERT(tc, "decr failed", rv == APR_SUCCESS);
336
337 ABTS_INT_EQUAL(tc, atoi(result), new);
338
339 }
340
342 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
343
344 ABTS_INT_EQUAL(tc, 271, atoi(result));
345
346 rv = apr_redis_delete(redis, prefix, 0);
347 ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS);
348}
349
350
351/* test setting and getting */
352
353static void test_redis_setget(abts_case * tc, void *data)
354{
355 apr_pool_t *pool = p;
356 apr_status_t rv;
361 char *result;
363
364 rv = apr_redis_create(pool, 1, 0, &redis);
365 ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
366
367 rv = apr_redis_server_create(pool, HOST, PORT, 0, 1, 1, 60, 60, &server);
368 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
369
371 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
372
374
376
377 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
378 const void *k;
379 void *v;
380 const char *key;
381
382 apr_hash_this(hi, &k, NULL, &v);
383 key = k;
384
385 rv = apr_redis_set(redis, key, v, strlen(v), 27);
386 ABTS_ASSERT(tc, "set failed", rv == APR_SUCCESS);
388 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
389 }
390
391 rv = apr_redis_getp(redis, pool, "nothere3423", &result, &len, NULL);
392
393 ABTS_ASSERT(tc, "get should have failed", rv != APR_SUCCESS);
394
395 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
396 const void *k;
397 const char *key;
398
399 apr_hash_this(hi, &k, NULL, NULL);
400 key = k;
401
402 rv = apr_redis_delete(redis, key, 0);
403 ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS);
404 }
405}
406
407/* test setting and getting */
408
409static void test_redis_setexget(abts_case * tc, void *data)
410{
411 apr_pool_t *pool = p;
412 apr_status_t rv;
417 char *result;
419
420 rv = apr_redis_create(pool, 1, 0, &redis);
421 ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
422
423 rv = apr_redis_server_create(pool, HOST, PORT, 0, 1, 1, 60, 60, &server);
424 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
425
427 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
428
430
432
433 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
434 const void *k;
435 void *v;
436 const char *key;
437
438 apr_hash_this(hi, &k, NULL, &v);
439 key = k;
440
442 ABTS_ASSERT(tc, "ping failed", rv == APR_SUCCESS);
443 rv = apr_redis_setex(redis, key, v, strlen(v), 10, 27);
444 ABTS_ASSERT(tc, "set failed", rv == APR_SUCCESS);
446 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
447 }
448
449 rv = apr_redis_getp(redis, pool, "nothere3423", &result, &len, NULL);
450
451 ABTS_ASSERT(tc, "get should have failed", rv != APR_SUCCESS);
452
453 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
454 const void *k;
455 const char *key;
456
457 apr_hash_this(hi, &k, NULL, NULL);
458 key = k;
459
460 rv = apr_redis_delete(redis, key, 0);
461 ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS);
462 }
463}
464
465/* use apr_socket stuff to see if there is in fact a Redis server
466 * running on PORT.
467 */
469{
470 apr_pool_t *pool = p;
471 apr_status_t rv;
474 struct iovec vec[2];
475 apr_size_t written;
476 char buf[128];
478
480 if(rv != APR_SUCCESS) {
481 return rv;
482 }
483
485 if(rv != APR_SUCCESS) {
486 return rv;
487 }
488
490 if (rv != APR_SUCCESS) {
491 return rv;
492 }
493
495 if (rv != APR_SUCCESS) {
496 return rv;
497 }
498
500 if (rv != APR_SUCCESS) {
501 return rv;
502 }
503
504 vec[0].iov_base = "PING";
505 vec[0].iov_len = sizeof("PING") - 1;
506
507 vec[1].iov_base = "\r\n";
508 vec[1].iov_len = sizeof("\r\n") -1;
509
510 rv = apr_socket_sendv(sock, vec, 2, &written);
511 if (rv != APR_SUCCESS) {
512 return rv;
513 }
514
515 len = sizeof(buf);
516 rv = apr_socket_recv(sock, buf, &len);
517 if(rv != APR_SUCCESS) {
518 return rv;
519 }
520 if(strncmp(buf, "+PONG", sizeof("+PONG")-1) != 0) {
521 rv = APR_EGENERAL;
522 }
523
525 return rv;
526}
527
529{
530 apr_status_t rv;
531 suite = ADD_SUITE(suite);
532 /* check for a running redis on the typical port before
533 * trying to run the tests. succeed if we don't find one.
534 */
535 rv = check_redis();
536 if (rv == APR_SUCCESS) {
542 /* abts_run_test(suite, test_redis_multiget, NULL); */
544 }
545 else {
546 abts_log_message("Error %d occurred attempting to reach Redis "
547 "on %s:%d. Skipping apr_redis tests...",
548 rv, HOST, PORT);
549 }
550
551 return suite;
552}
const char apr_size_t len
Definition ap_regex.h:187
void abts_run_test(abts_suite *ts, test_func f, void *value)
Definition abts.c:175
void abts_log_message(const char *fmt,...)
Definition abts.c:270
#define ABTS_PTR_EQUAL(a, b, c)
Definition abts.h:126
#define ADD_SUITE(suite)
Definition abts.h:67
#define ABTS_PTR_NOTNULL(a, b)
Definition abts.h:125
#define ABTS_ASSERT(a, b, c)
Definition abts.h:130
#define ABTS_INT_EQUAL(a, b, c)
Definition abts.h:109
APR Miscellaneous library routines.
APR Hash Tables.
APR Network library.
#define hash(h, r, b, n)
Definition apr_random.c:51
apr_redis_find_server_hash(apr_redis_t *rc, const apr_uint32_t hash)
Definition apr_redis.c:158
apr_redis_delete(apr_redis_t *rc, const char *key, apr_uint32_t timeout)
Definition apr_redis.c:1005
apr_redis_incr(apr_redis_t *rc, const char *key, apr_int32_t inc, apr_uint32_t *new_value)
Definition apr_redis.c:1365
apr_redis_ping(apr_redis_server_t *rs)
Definition apr_redis.c:1087
apr_redis_stats(apr_redis_server_t *rs, apr_pool_t *p, apr_redis_stats_t **stats)
Definition apr_redis.c:1488
apr_redis_version(apr_redis_server_t *rs, apr_pool_t *p, char **baton)
Definition apr_redis.c:1189
apr_redis_decr(apr_redis_t *rc, const char *key, apr_int32_t inc, apr_uint32_t *new_value)
Definition apr_redis.c:1371
Client interface for redis.
apr_size_t const unsigned char unsigned int unsigned int d
Definition apr_siphash.h:72
APR Strings library.
return found
Definition core.c:2840
const unsigned char * buf
Definition util_md5.h:50
const char apr_port_t port
Definition http_vhost.h:125
ap_vhost_iterate_conn_cb void * baton
Definition http_vhost.h:87
#define APR_EGENERAL
Definition apr_errno.h:313
apr_memcache_t * mc
const char const apr_size_t data_len
apr_memcache_server_t * server
apr_memcache_server_t * ms
apr_pool_t apr_memcache_stats_t ** stats
apr_size_t size
const char int apr_pool_t * pool
Definition apr_cstr.h:84
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
const char * key
const struct iovec * vec
void * data
apr_array_header_t ** result
const apr_hash_t * h
Definition apr_hash.h:97
#define APR_HASH_KEY_STRING
Definition apr_hash.h:47
apr_socket_t * sock
apr_sockaddr_t * sa
apr_uint16_t apr_port_t
#define APR_INET
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
const char * s
Definition apr_strings.h:95
#define APR_USEC_PER_SEC
Definition apr_time.h:60
apr_pool_t * p
Definition md_event.c:32
#define RAND_MAX
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
static int max_servers
Definition mod_status.c:89
apr_memcache_server_t ** live_servers
apr_uint16_t ntotal
static int create_test_hash(apr_pool_t *p, apr_hash_t *h)
Definition testredis.c:181
static void test_redis_setexget(abts_case *tc, void *data)
Definition testredis.c:409
static void test_redis_meta(abts_case *tc, void *data)
Definition testredis.c:244
#define HASH_FUNC_RESULT
Definition testredis.c:41
static apr_redis_server_t * my_server_func(void *baton, apr_redis_t *mc, const apr_uint32_t hash)
Definition testredis.c:89
static apr_uint32_t my_hash_func(void *baton, const char *data, apr_size_t data_len)
Definition testredis.c:76
static void test_redis_user_funcs(abts_case *tc, void *data)
Definition testredis.c:197
static void test_redis_setget(abts_case *tc, void *data)
Definition testredis.c:353
#define PORT
Definition testredis.c:32
abts_suite * testredis(abts_suite *suite)
Definition testredis.c:528
static int randval(apr_uint32_t high)
Definition testredis.c:110
static const char txt[]
Definition testredis.c:47
#define HOST
Definition testredis.c:31
static void test_redis_incrdecr(abts_case *tc, void *data)
Definition testredis.c:298
static apr_status_t check_redis(void)
Definition testredis.c:468
#define TDATA_SIZE
Definition testredis.c:35
static void test_redis_create(abts_case *tc, void *data)
Definition testredis.c:131
static apr_uint16_t firsttime
Definition testredis.c:109
apr_status_t apr_socket_sendv(apr_socket_t *sock, const struct iovec *vec, apr_int32_t nvec, apr_size_t *len)
Definition sendrecv.c:194
apr_status_t apr_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len)
Definition sendrecv.c:70
apr_status_t apr_socket_close(apr_socket_t *thesocket)
Definition sockets.c:211
apr_status_t apr_socket_connect(apr_socket_t *sock, apr_sockaddr_t *sa)
Definition sockets.c:388
apr_status_t apr_socket_create(apr_socket_t **new, int ofamily, int type, int protocol, apr_pool_t *cont)
Definition sockets.c:116
apr_status_t apr_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t)
Definition sockopt.c:75
typedef int(WSAAPI *apr_winapi_fpt_WSAPoll)(IN OUT LPWSAPOLLFD fdArray