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_memcache.h" 24#include "apr_network_io.h" 25 26#if APR_HAVE_STDLIB_H 27#include <stdlib.h> /* for exit() */ 28#endif 29 30#define HOST "localhost" 31#define PORT 11211 32 33/* the total number of items to use for set/get testing */ 34#define TDATA_SIZE 3000 35 36/* some smaller subset of TDATA_SIZE used for multiget testing */ 37#define TDATA_SET 100 38 39/* our custom hash function just returns this all the time */ 40#define HASH_FUNC_RESULT 510 41 42/* all keys will be prefixed with this */ 43const char prefix[] = "testmemcache"; 44 45/* text for values we store */ 46const char txt[] = 47"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis at" 48"lacus in ligula hendrerit consectetuer. Vestibulum tristique odio" 49"iaculis leo. In massa arcu, ultricies a, laoreet nec, hendrerit non," 50"neque. Nulla sagittis sapien ac risus. Morbi ligula dolor, vestibulum" 51"nec, viverra id, placerat dapibus, arcu. Curabitur egestas feugiat" 52"tellus. Donec dignissim. Nunc ante. Curabitur id lorem. In mollis" 53"tortor sit amet eros auctor dapibus. Proin nulla sem, tristique in," 54"convallis id, iaculis feugiat cras amet."; 55 56/* 57 * this datatype is for our custom server determination function. this might 58 * be useful if you don't want to rely on simply hashing keys to determine 59 * where a key belongs, but instead want to write something fancy, or use some 60 * other kind of configuration data, i.e. a hash plus some data about a 61 * namespace, or whatever. see my_server_func, and test_memcache_user_funcs 62 * for the examples. 63 */ 64typedef struct { 65 const char *someval; 66 apr_uint32_t which_server; 67} my_hash_server_baton; 68 69 70/* this could do something fancy and return some hash result. 71 * for simplicity, just return the same value, so we can test it later on. 72 * if you wanted to use some external hashing library or functions for 73 * consistent hashing, for example, this would be a good place to do it. 74 */ 75static apr_uint32_t my_hash_func(void *baton, const char *data, 76 apr_size_t data_len) 77{ 78 79 return HASH_FUNC_RESULT; 80} 81 82/* 83 * a fancy function to determine which server to use given some kind of data 84 * and a hash value. this example actually ignores the hash value itself 85 * and pulls some number from the *baton, which is a struct that has some 86 * kind of meaningful stuff in it. 87 */ 88static apr_memcache_server_t *my_server_func(void *baton, 89 apr_memcache_t *mc, 90 const apr_uint32_t hash) 91{ 92 apr_memcache_server_t *ms = NULL; 93 my_hash_server_baton *mhsb = (my_hash_server_baton *)baton; 94 95 if(mc->ntotal == 0) { 96 return NULL; 97 } 98 99 if(mc->ntotal < mhsb->which_server) { 100 return NULL; 101 } 102 103 ms = mc->live_servers[mhsb->which_server - 1]; 104 105 return ms; 106} 107 108apr_uint16_t firsttime = 0; 109static int randval(apr_uint32_t high) 110{ 111 apr_uint32_t i = 0; 112 double d = 0; 113 114 if (firsttime == 0) { 115 srand((unsigned) (getpid())); 116 firsttime = 1; 117 } 118 119 d = (double) rand() / ((double) RAND_MAX + 1); 120 i = (int) (d * (high - 0 + 1)); 121 122 return i > 0 ? i : 1; 123} 124 125/* 126 * general test to make sure we can create the memcache struct and add 127 * some servers, but not more than we tell it we can add 128 */ 129 130static void test_memcache_create(abts_case * tc, void *data) 131{ 132 apr_pool_t *pool = p; 133 apr_status_t rv; 134 apr_memcache_t *memcache; 135 apr_memcache_server_t *server, *s; 136 apr_uint32_t max_servers = 10; 137 apr_uint32_t i; 138 apr_uint32_t hash; 139 140 rv = apr_memcache_create(pool, max_servers, 0, &memcache); 141 ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS); 142 143 for (i = 1; i <= max_servers; i++) { 144 apr_port_t port; 145 146 port = PORT + i; 147 rv = 148 apr_memcache_server_create(pool, HOST, PORT + i, 0, 1, 1, 60, &server); 149 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS); 150 151 rv = apr_memcache_add_server(memcache, server); 152 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS); 153 154 s = apr_memcache_find_server(memcache, HOST, port); 155 ABTS_PTR_EQUAL(tc, server, s); 156 157 rv = apr_memcache_disable_server(memcache, s); 158 ABTS_ASSERT(tc, "server disable failed", rv == APR_SUCCESS); 159 160 rv = apr_memcache_enable_server(memcache, s); 161 ABTS_ASSERT(tc, "server enable failed", rv == APR_SUCCESS); 162 163 hash = apr_memcache_hash(memcache, prefix, strlen(prefix)); 164 ABTS_ASSERT(tc, "hash failed", hash > 0); 165 166 s = apr_memcache_find_server_hash(memcache, hash); 167 ABTS_PTR_NOTNULL(tc, s); 168 } 169 170 rv = apr_memcache_server_create(pool, HOST, PORT, 0, 1, 1, 60, &server); 171 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS); 172 173 rv = apr_memcache_add_server(memcache, server); 174 ABTS_ASSERT(tc, "server add should have failed", rv != APR_SUCCESS); 175 176} 177 178/* install our own custom hashing and server selection routines. */ 179 180static int create_test_hash(apr_pool_t *p, apr_hash_t *h) 181{ 182 int i; 183 184 for (i = 0; i < TDATA_SIZE; i++) { 185 char *k, *v; 186 187 k = apr_pstrcat(p, prefix, apr_itoa(p, i), NULL); 188 v = apr_pstrndup(p, txt, randval((apr_uint32_t)strlen(txt))); 189 190 apr_hash_set(h, k, APR_HASH_KEY_STRING, v); 191 } 192 193 return i; 194} 195 196static void test_memcache_user_funcs(abts_case * tc, void *data) 197{ 198 apr_pool_t *pool = p; 199 apr_status_t rv; 200 apr_memcache_t *memcache; 201 apr_memcache_server_t *found; 202 apr_uint32_t max_servers = 10; 203 apr_uint32_t hres; 204 apr_uint32_t i; 205 my_hash_server_baton *baton = 206 apr_pcalloc(pool, sizeof(my_hash_server_baton)); 207 208 rv = apr_memcache_create(pool, max_servers, 0, &memcache); 209 ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS); 210 211 /* as noted above, install our custom hash function, and call 212 * apr_memcache_hash. the return value should be our predefined number, 213 * and our function just ignores the other args, for simplicity. 214 */ 215 memcache->hash_func = my_hash_func; 216 217 hres = apr_memcache_hash(memcache, "whatever", sizeof("whatever") - 1); 218 ABTS_INT_EQUAL(tc, HASH_FUNC_RESULT, hres); 219 220 /* add some servers */ 221 for(i = 1; i <= 10; i++) { 222 apr_memcache_server_t *ms; 223 224 rv = apr_memcache_server_create(pool, HOST, i, 0, 1, 1, 60, &ms); 225 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS); 226 227 rv = apr_memcache_add_server(memcache, ms); 228 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS); 229 } 230 231 /* 232 * set 'which_server' in our server_baton to find the third server 233 * which should have the same port. 234 */ 235 baton->which_server = 3; 236 memcache->server_func = my_server_func; 237 memcache->server_baton = baton; 238 found = apr_memcache_find_server_hash(memcache, 0); 239 ABTS_ASSERT(tc, "wrong server found", found->port == baton->which_server); 240} 241 242/* test non data related commands like stats and version */ 243static void test_memcache_meta(abts_case * tc, void *data) 244{ 245 apr_pool_t *pool = p; 246 apr_memcache_t *memcache; 247 apr_memcache_server_t *server; 248 apr_memcache_stats_t *stats; 249 char *result; 250 apr_status_t rv; 251 252 rv = apr_memcache_create(pool, 1, 0, &memcache); 253 ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS); 254 255 rv = apr_memcache_server_create(pool, HOST, PORT, 0, 1, 1, 60, &server); 256 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS); 257 258 rv = apr_memcache_add_server(memcache, server); 259 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS); 260 261 rv = apr_memcache_version(server, pool, &result); 262 ABTS_PTR_NOTNULL(tc, result); 263 264 rv = apr_memcache_stats(server, p, &stats); 265 ABTS_PTR_NOTNULL(tc, stats); 266 267 ABTS_STR_NEQUAL(tc, stats->version, result, 5); 268 269 /* 270 * no way to know exactly what will be in most of these, so 271 * just make sure there is something. 272 */ 273 274 ABTS_ASSERT(tc, "pid", stats->pid >= 0); 275 ABTS_ASSERT(tc, "time", stats->time >= 0); 276 /* ABTS_ASSERT(tc, "pointer_size", stats->pointer_size >= 0); */ 277 ABTS_ASSERT(tc, "rusage_user", stats->rusage_user >= 0); 278 ABTS_ASSERT(tc, "rusage_system", stats->rusage_system >= 0); 279 280 ABTS_ASSERT(tc, "curr_items", stats->curr_items >= 0); 281 ABTS_ASSERT(tc, "total_items", stats->total_items >= 0); 282 ABTS_ASSERT(tc, "bytes", stats->bytes >= 0); 283 284 ABTS_ASSERT(tc, "curr_connections", stats->curr_connections >= 0); 285 ABTS_ASSERT(tc, "total_connections", stats->total_connections >= 0); 286 ABTS_ASSERT(tc, "connection_structures", 287 stats->connection_structures >= 0); 288 289 ABTS_ASSERT(tc, "cmd_get", stats->cmd_get >= 0); 290 ABTS_ASSERT(tc, "cmd_set", stats->cmd_set >= 0); 291 ABTS_ASSERT(tc, "get_hits", stats->get_hits >= 0); 292 ABTS_ASSERT(tc, "get_misses", stats->get_misses >= 0); 293 294 /* ABTS_ASSERT(tc, "evictions", stats->evictions >= 0); */ 295 296 ABTS_ASSERT(tc, "bytes_read", stats->bytes_read >= 0); 297 ABTS_ASSERT(tc, "bytes_written", stats->bytes_written >= 0); 298 ABTS_ASSERT(tc, "limit_maxbytes", stats->limit_maxbytes >= 0); 299 300 /* ABTS_ASSERT(tc, "threads", stats->threads >= 0); */ 301} 302 303/* test add and replace calls */ 304 305static void test_memcache_addreplace(abts_case * tc, void *data) 306{ 307 apr_pool_t *pool = p; 308 apr_status_t rv; 309 apr_memcache_t *memcache; 310 apr_memcache_server_t *server; 311 apr_hash_t *tdata; 312 apr_hash_index_t *hi; 313 char *result; 314 apr_size_t len; 315 316 rv = apr_memcache_create(pool, 1, 0, &memcache); 317 ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS); 318 319 rv = apr_memcache_server_create(pool, HOST, PORT, 0, 1, 1, 60, &server); 320 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS); 321 322 rv = apr_memcache_add_server(memcache, server); 323 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS); 324 325 tdata = apr_hash_make(p); 326 create_test_hash(pool, tdata); 327 328 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) { 329 const void *k; 330 void *v; 331 const char *key; 332 333 apr_hash_this(hi, &k, NULL, &v); 334 key = k; 335 336 /* doesn't exist yet, fail */ 337 rv = apr_memcache_replace(memcache, key, v, strlen(v) - 1, 0, 27); 338 ABTS_ASSERT(tc, "replace should have failed", rv != APR_SUCCESS); 339 340 /* doesn't exist yet, succeed */ 341 rv = apr_memcache_add(memcache, key, v, strlen(v), 0, 27); 342 ABTS_ASSERT(tc, "add failed", rv == APR_SUCCESS); 343 344 /* exists now, succeed */ 345 rv = apr_memcache_replace(memcache, key, "new", sizeof("new") - 1, 0, 27); 346 ABTS_ASSERT(tc, "replace failed", rv == APR_SUCCESS); 347 348 /* make sure its different */ 349 rv = apr_memcache_getp(memcache, pool, key, &result, &len, NULL); 350 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS); 351 ABTS_STR_NEQUAL(tc, result, "new", 3); 352 353 /* exists now, fail */ 354 rv = apr_memcache_add(memcache, key, v, strlen(v), 0, 27); 355 ABTS_ASSERT(tc, "add should have failed", rv != APR_SUCCESS); 356 357 /* clean up */ 358 rv = apr_memcache_delete(memcache, key, 0); 359 ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS); 360 } 361} 362 363/* basic tests of the increment and decrement commands */ 364static void test_memcache_incrdecr(abts_case * tc, void *data) 365{ 366 apr_pool_t *pool = p; 367 apr_status_t rv; 368 apr_memcache_t *memcache; 369 apr_memcache_server_t *server; 370 apr_uint32_t new; 371 char *result; 372 apr_size_t len; 373 apr_uint32_t i; 374 375 rv = apr_memcache_create(pool, 1, 0, &memcache); 376 ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS); 377 378 rv = apr_memcache_server_create(pool, HOST, PORT, 0, 1, 1, 60, &server); 379 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS); 380 381 rv = apr_memcache_add_server(memcache, server); 382 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS); 383 384 rv = apr_memcache_set(memcache, prefix, "271", sizeof("271") - 1, 0, 27); 385 ABTS_ASSERT(tc, "set failed", rv == APR_SUCCESS); 386 387 for( i = 1; i <= TDATA_SIZE; i++) { 388 apr_uint32_t expect; 389 390 rv = apr_memcache_getp(memcache, pool, prefix, &result, &len, NULL); 391 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS); 392 393 expect = i + atoi(result); 394 395 rv = apr_memcache_incr(memcache, prefix, i, &new); 396 ABTS_ASSERT(tc, "incr failed", rv == APR_SUCCESS); 397 398 ABTS_INT_EQUAL(tc, expect, new); 399 400 rv = apr_memcache_decr(memcache, prefix, i, &new); 401 ABTS_ASSERT(tc, "decr failed", rv == APR_SUCCESS); 402 ABTS_INT_EQUAL(tc, atoi(result), new); 403 404 } 405 406 rv = apr_memcache_getp(memcache, pool, prefix, &result, &len, NULL); 407 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS); 408 409 ABTS_INT_EQUAL(tc, 271, atoi(result)); 410 411 rv = apr_memcache_delete(memcache, prefix, 0); 412 ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS); 413} 414 415/* test the multiget functionality */ 416static void test_memcache_multiget(abts_case * tc, void *data) 417{ 418 apr_pool_t *pool = p; 419 apr_pool_t *tmppool; 420 apr_status_t rv; 421 apr_memcache_t *memcache; 422 apr_memcache_server_t *server; 423 apr_hash_t *tdata, *values; 424 apr_hash_index_t *hi; 425 apr_uint32_t i; 426 427 rv = apr_memcache_create(pool, 1, 0, &memcache); 428 ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS); 429 430 rv = apr_memcache_server_create(pool, HOST, PORT, 0, 1, 1, 60, &server); 431 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS); 432 433 rv = apr_memcache_add_server(memcache, server); 434 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS); 435 436 values = apr_hash_make(p); 437 tdata = apr_hash_make(p); 438 439 create_test_hash(pool, tdata); 440 441 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) { 442 const void *k; 443 void *v; 444 const char *key; 445 446 apr_hash_this(hi, &k, NULL, &v); 447 key = k; 448 449 rv = apr_memcache_set(memcache, key, v, strlen(v), 0, 27); 450 ABTS_ASSERT(tc, "set failed", rv == APR_SUCCESS); 451 } 452 453 rv = apr_pool_create(&tmppool, pool); 454 for (i = 0; i < TDATA_SET; i++) 455 apr_memcache_add_multget_key(pool, 456 apr_pstrcat(pool, prefix, 457 apr_itoa(pool, i), NULL), 458 &values); 459 460 rv = apr_memcache_multgetp(memcache, 461 tmppool, 462 pool, 463 values); 464 465 ABTS_ASSERT(tc, "multgetp failed", rv == APR_SUCCESS); 466 ABTS_ASSERT(tc, "multgetp returned too few results", 467 apr_hash_count(values) == TDATA_SET); 468 469 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) { 470 const void *k; 471 const char *key; 472 473 apr_hash_this(hi, &k, NULL, NULL); 474 key = k; 475 476 rv = apr_memcache_delete(memcache, key, 0); 477 ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS); 478 } 479 480} 481 482/* test setting and getting */ 483 484static void test_memcache_setget(abts_case * tc, void *data) 485{ 486 apr_pool_t *pool = p; 487 apr_status_t rv; 488 apr_memcache_t *memcache; 489 apr_memcache_server_t *server; 490 apr_hash_t *tdata; 491 apr_hash_index_t *hi; 492 char *result; 493 apr_size_t len; 494 495 rv = apr_memcache_create(pool, 1, 0, &memcache); 496 ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS); 497 498 rv = apr_memcache_server_create(pool, HOST, PORT, 0, 1, 1, 60, &server); 499 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS); 500 501 rv = apr_memcache_add_server(memcache, server); 502 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS); 503 504 tdata = apr_hash_make(pool); 505 506 create_test_hash(pool, tdata); 507 508 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) { 509 const void *k; 510 void *v; 511 const char *key; 512 513 apr_hash_this(hi, &k, NULL, &v); 514 key = k; 515 516 rv = apr_memcache_set(memcache, key, v, strlen(v), 0, 27); 517 ABTS_ASSERT(tc, "set failed", rv == APR_SUCCESS); 518 rv = apr_memcache_getp(memcache, pool, key, &result, &len, NULL); 519 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS); 520 } 521 522 rv = apr_memcache_getp(memcache, pool, "nothere3423", &result, &len, NULL); 523 524 ABTS_ASSERT(tc, "get should have failed", rv != APR_SUCCESS); 525 526 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) { 527 const void *k; 528 const char *key; 529 530 apr_hash_this(hi, &k, NULL, NULL); 531 key = k; 532 533 rv = apr_memcache_delete(memcache, key, 0); 534 ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS); 535 } 536} 537 538/* use apr_socket stuff to see if there is in fact a memcached server 539 * running on PORT. 540 */ 541static apr_status_t check_mc(void) 542{ 543 apr_pool_t *pool = p; 544 apr_status_t rv; 545 apr_socket_t *sock = NULL; 546 apr_sockaddr_t *sa; 547 struct iovec vec[2]; 548 apr_size_t written; 549 char buf[128]; 550 apr_size_t len; 551 552 rv = apr_socket_create(&sock, APR_INET, SOCK_STREAM, 0, pool); 553 if(rv != APR_SUCCESS) { 554 return rv; 555 } 556 557 rv = apr_sockaddr_info_get(&sa, HOST, APR_INET, PORT, 0, pool); 558 if(rv != APR_SUCCESS) { 559 return rv; 560 } 561 562 rv = apr_socket_timeout_set(sock, 1 * APR_USEC_PER_SEC); 563 if (rv != APR_SUCCESS) { 564 return rv; 565 } 566 567 rv = apr_socket_connect(sock, sa); 568 if (rv != APR_SUCCESS) { 569 return rv; 570 } 571 572 rv = apr_socket_timeout_set(sock, -1); 573 if (rv != APR_SUCCESS) { 574 return rv; 575 } 576 577 vec[0].iov_base = "version"; 578 vec[0].iov_len = sizeof("version") - 1; 579 580 vec[1].iov_base = "\r\n"; 581 vec[1].iov_len = sizeof("\r\n") -1; 582 583 rv = apr_socket_sendv(sock, vec, 2, &written); 584 if (rv != APR_SUCCESS) { 585 return rv; 586 } 587 588 len = sizeof(buf); 589 rv = apr_socket_recv(sock, buf, &len); 590 if(rv != APR_SUCCESS) { 591 return rv; 592 } 593 594 if(strncmp(buf, "VERSION", sizeof("VERSION")-1) != 0) { 595 rv = APR_EGENERAL; 596 } 597 598 apr_socket_close(sock); 599 return rv; 600} 601 602abts_suite *testmemcache(abts_suite * suite) 603{ 604 apr_status_t rv; 605 suite = ADD_SUITE(suite); 606 /* check for a running memcached on the typical port before 607 * trying to run the tests. succeed if we don't find one. 608 */ 609 rv = check_mc(); 610 if (rv == APR_SUCCESS) { 611 abts_run_test(suite, test_memcache_create, NULL); 612 abts_run_test(suite, test_memcache_user_funcs, NULL); 613 abts_run_test(suite, test_memcache_meta, NULL); 614 abts_run_test(suite, test_memcache_setget, NULL); 615 abts_run_test(suite, test_memcache_multiget, NULL); 616 abts_run_test(suite, test_memcache_addreplace, NULL); 617 abts_run_test(suite, test_memcache_incrdecr, NULL); 618 } 619 else { 620 abts_log_message("Error %d occurred attempting to reach memcached " 621 "on %s:%d. Skipping apr_memcache tests...", 622 rv, HOST, PORT); 623 } 624 625 return suite; 626} 627