Asterisk - The Open Source Telephony Project GIT-master-0644429
Data Structures | Macros | Functions | Variables
test_http_media_cache.c File Reference

Tests for the HTTP media cache backend. More...

#include "asterisk.h"
#include <fcntl.h>
#include "asterisk/module.h"
#include "asterisk/http.h"
#include "asterisk/bucket.h"
#include "asterisk/test.h"
Include dependency graph for test_http_media_cache.c:

Go to the source code of this file.

Data Structures

struct  test_options
 

Macros

#define CATEGORY   "/res/http_media_cache/"
 
#define SET_OR_APPEND_CACHE_CONTROL(str)
 
#define TEST_URI   "test_media_cache"
 
#define VALIDATE_EXPIRES(test, bucket_file, expected, delta)
 
#define VALIDATE_STR_METADATA(test, bucket_file, key, expected)
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
 AST_TEST_DEFINE (create_nominal)
 
 AST_TEST_DEFINE (retrieve_cache_control_age)
 
 AST_TEST_DEFINE (retrieve_cache_control_directives)
 
 AST_TEST_DEFINE (retrieve_content_type)
 
 AST_TEST_DEFINE (retrieve_etag)
 
 AST_TEST_DEFINE (retrieve_etag_expired)
 
 AST_TEST_DEFINE (retrieve_expires)
 
 AST_TEST_DEFINE (retrieve_nominal)
 
 AST_TEST_DEFINE (retrieve_parsed_uri)
 
static void bucket_file_cleanup (void *obj)
 
static int http_callback (struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
 
static int load_module (void)
 
static int pre_test_cb (struct ast_test_info *info, struct ast_test *test)
 
static int process_config (int reload)
 
static int reload_module (void)
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "HTTP Media Cache Backend Tests" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .reload = reload_module, .unload = unload_module, .requires = "res_http_media_cache", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static struct test_options options
 
static char server_uri [512]
 
static struct ast_http_uri test_uri
 

Detailed Description

Tests for the HTTP media cache backend.

Author
Matt Jordan <mjordan@digium.com> 

Definition in file test_http_media_cache.c.

Macro Definition Documentation

◆ CATEGORY

#define CATEGORY   "/res/http_media_cache/"

Definition at line 44 of file test_http_media_cache.c.

◆ SET_OR_APPEND_CACHE_CONTROL

#define SET_OR_APPEND_CACHE_CONTROL (   str)

Definition at line 84 of file test_http_media_cache.c.

◆ TEST_URI

#define TEST_URI   "test_media_cache"

Definition at line 46 of file test_http_media_cache.c.

◆ VALIDATE_EXPIRES

#define VALIDATE_EXPIRES (   test,
  bucket_file,
  expected,
  delta 
)

Definition at line 67 of file test_http_media_cache.c.

◆ VALIDATE_STR_METADATA

#define VALIDATE_STR_METADATA (   test,
  bucket_file,
  key,
  expected 
)
Value:
do { \
RAII_VAR(struct ast_bucket_metadata *, metadata, ast_bucket_file_metadata_get((bucket_file), (key)), ao2_cleanup); \
ast_test_validate(test, metadata != NULL); \
ast_test_validate(test, !strcmp(metadata->value, (expected))); \
} while (0)
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
struct ast_bucket_metadata * ast_bucket_file_metadata_get(struct ast_bucket_file *file, const char *name)
Retrieve a metadata attribute from a file.
Definition: bucket.c:359
#define NULL
Definition: resample.c:96
Bucket metadata structure, AO2 key value pair.
Definition: bucket.h:47

Definition at line 78 of file test_http_media_cache.c.

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 779 of file test_http_media_cache.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 779 of file test_http_media_cache.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 779 of file test_http_media_cache.c.

◆ AST_TEST_DEFINE() [1/9]

AST_TEST_DEFINE ( create_nominal  )

Definition at line 647 of file test_http_media_cache.c.

648{
649 RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
650 struct timeval now = ast_tvnow();
651 char uri[1024];
652
653 switch (cmd) {
654 case TEST_INIT:
655 info->name = __func__;
656 info->category = CATEGORY;
657 info->summary = "Test nominal creation";
658 info->description =
659 "Test nominal creation of a resource.";
660 return AST_TEST_NOT_RUN;
661 case TEST_EXECUTE:
662 break;
663 }
664
665 options.send_file = 1;
666 options.status_code = 200;
667 options.status_text = "OK";
668
669 snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav");
670
671 bucket_file = ast_bucket_file_alloc(uri);
672 ast_test_validate(test, bucket_file != NULL);
673 ast_test_validate(test, ast_bucket_file_temporary_create(bucket_file) == 0);
674 ast_test_validate(test, ast_bucket_file_create(bucket_file) == 0);
675 VALIDATE_EXPIRES(test, bucket_file, now.tv_sec, 3);
676
677 return AST_TEST_PASS;
678}
struct ast_bucket_file * ast_bucket_file_alloc(const char *uri)
Allocate a new bucket file.
Definition: bucket.c:663
int ast_bucket_file_temporary_create(struct ast_bucket_file *file)
Common file snapshot creation callback for creating a temporary file.
Definition: bucket.c:899
int ast_bucket_file_create(struct ast_bucket_file *file)
Create a new bucket file in backend storage.
Definition: bucket.c:725
def info(msg)
Bucket file structure, contains reference to file and information about it.
Definition: bucket.h:78
const char * status_text
@ TEST_INIT
Definition: test.h:200
@ TEST_EXECUTE
Definition: test.h:201
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_NOT_RUN
Definition: test.h:194
#define VALIDATE_EXPIRES(test, bucket_file, expected, delta)
static void bucket_file_cleanup(void *obj)
#define CATEGORY
static struct test_options options
static char server_uri[512]
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941

References ast_bucket_file_alloc(), ast_bucket_file_create(), ast_bucket_file_temporary_create(), AST_TEST_NOT_RUN, AST_TEST_PASS, ast_tvnow(), bucket_file_cleanup(), CATEGORY, sip_to_pjsip::info(), NULL, options, RAII_VAR, test_options::send_file, server_uri, test_options::status_code, test_options::status_text, TEST_EXECUTE, TEST_INIT, and VALIDATE_EXPIRES.

◆ AST_TEST_DEFINE() [2/9]

AST_TEST_DEFINE ( retrieve_cache_control_age  )

Definition at line 379 of file test_http_media_cache.c.

380{
381 RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
382 struct timeval now = ast_tvnow();
383 char uri[1024];
384
385 switch (cmd) {
386 case TEST_INIT:
387 info->name = __func__;
388 info->category = CATEGORY;
389 info->summary = "Test retrieval of a resource with age specifiers in Cache-Control";
390 info->description =
391 "This test covers retrieval of a resource with the Cache-Control header,\n"
392 "which specifies max-age and/or s-maxage. The test verifies proper precedence\n"
393 "ordering of the header attributes, along with its relation if the Expires\n"
394 "header is present.";
395 return AST_TEST_NOT_RUN;
396 case TEST_EXECUTE:
397 break;
398 }
399
400 snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav");
401
402 options.send_file = 1;
403 options.status_code = 200;
404 options.status_text = "OK";
405
406 ast_test_status_update(test, "Testing max-age...\n");
408 bucket_file = ast_bucket_file_retrieve(uri);
409 ast_test_validate(test, bucket_file != NULL);
410 VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 300, 3);
411 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
412 bucket_file_cleanup(bucket_file);
413
414 ast_test_status_update(test, "Testing s-maxage...\n");
415 now = ast_tvnow();
418 bucket_file = ast_bucket_file_retrieve(uri);
419 ast_test_validate(test, bucket_file != NULL);
420 VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 300, 3);
421 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
422 bucket_file_cleanup(bucket_file);
423
424 ast_test_status_update(test, "Testing max-age and s-maxage...\n");
425 now = ast_tvnow();
428 bucket_file = ast_bucket_file_retrieve(uri);
429 ast_test_validate(test, bucket_file != NULL);
430 VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 600, 3);
431 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
432 bucket_file_cleanup(bucket_file);
433
434 ast_test_status_update(test, "Testing max-age and Expires...\n");
435 now = ast_tvnow();
438 options.expires.tv_sec = now.tv_sec + 3000;
439 bucket_file = ast_bucket_file_retrieve(uri);
440 ast_test_validate(test, bucket_file != NULL);
441 VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 300, 3);
442 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
443 bucket_file_cleanup(bucket_file);
444
445 ast_test_status_update(test, "Testing s-maxage and Expires...\n");
446 now = ast_tvnow();
449 options.expires.tv_sec = now.tv_sec + 3000;
450 bucket_file = ast_bucket_file_retrieve(uri);
451 ast_test_validate(test, bucket_file != NULL);
452 VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 300, 3);
453 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
454 bucket_file_cleanup(bucket_file);
455
456 ast_test_status_update(test, "Testing s-maxage and Expires...\n");
457 now = ast_tvnow();
460 options.expires.tv_sec = now.tv_sec + 3000;
461 bucket_file = ast_bucket_file_retrieve(uri);
462 ast_test_validate(test, bucket_file != NULL);
463 VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 300, 3);
464 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
465 bucket_file_cleanup(bucket_file);
466
467 ast_test_status_update(test, "Testing max-age, s-maxage, and Expires...\n");
468 now = ast_tvnow();
471 options.expires.tv_sec = now.tv_sec + 3000;
472 bucket_file = ast_bucket_file_retrieve(uri);
473 ast_test_validate(test, bucket_file != NULL);
474 VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 600, 3);
475 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
476
477 return AST_TEST_PASS;
478}
struct ast_bucket_file * ast_bucket_file_retrieve(const char *uri)
Retrieve a bucket file.
Definition: bucket.c:815
int ast_bucket_file_is_stale(struct ast_bucket_file *file)
Retrieve whether or not the backing datastore views the bucket file as stale.
Definition: bucket.c:824
struct test_options::@500 cache_control
struct timeval expires
#define ast_test_status_update(a, b, c...)
Definition: test.h:129

References ast_bucket_file_is_stale(), ast_bucket_file_retrieve(), AST_TEST_NOT_RUN, AST_TEST_PASS, ast_test_status_update, ast_tvnow(), bucket_file_cleanup(), test_options::cache_control, CATEGORY, test_options::expires, sip_to_pjsip::info(), test_options::maxage, NULL, options, RAII_VAR, test_options::s_maxage, test_options::send_file, server_uri, test_options::status_code, test_options::status_text, TEST_EXECUTE, TEST_INIT, and VALIDATE_EXPIRES.

◆ AST_TEST_DEFINE() [3/9]

AST_TEST_DEFINE ( retrieve_cache_control_directives  )

Definition at line 297 of file test_http_media_cache.c.

298{
299 RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
300 struct timeval now = ast_tvnow();
301 char uri[1024];
302
303 switch (cmd) {
304 case TEST_INIT:
305 info->name = __func__;
306 info->category = CATEGORY;
307 info->summary = "Test retrieval of a resource with Cache-Control directives that affect staleness";
308 info->description =
309 "This test covers retrieval of a resource with the Cache-Control header,\n"
310 "which specifies no-cache and/or must-revalidate.";
311 return AST_TEST_NOT_RUN;
312 case TEST_EXECUTE:
313 break;
314 }
315
316 snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav");
317
318 options.send_file = 1;
319 options.status_code = 200;
320 options.status_text = "OK";
321
322 ast_test_status_update(test, "Testing no-cache...\n");
324 bucket_file = ast_bucket_file_retrieve(uri);
325 ast_test_validate(test, bucket_file != NULL);
326 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 1);
327 bucket_file_cleanup(bucket_file);
328
329 ast_test_status_update(test, "Testing no-cache with ETag...\n");
331 options.etag = "123456789";
332 bucket_file = ast_bucket_file_retrieve(uri);
333 ast_test_validate(test, bucket_file != NULL);
334 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
335 bucket_file_cleanup(bucket_file);
336
337 options.etag = NULL;
338
339 ast_test_status_update(test, "Testing no-cache with max-age...\n");
342 bucket_file = ast_bucket_file_retrieve(uri);
343 ast_test_validate(test, bucket_file != NULL);
344 VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 300, 3);
345 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 1);
346 bucket_file_cleanup(bucket_file);
347
350
351 ast_test_status_update(test, "Testing must-revalidate...\n");
353 bucket_file = ast_bucket_file_retrieve(uri);
354 ast_test_validate(test, bucket_file != NULL);
355 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 1);
356 bucket_file_cleanup(bucket_file);
357
358 ast_test_status_update(test, "Testing must-revalidate with ETag...\n");
360 options.etag = "123456789";
361 bucket_file = ast_bucket_file_retrieve(uri);
362 ast_test_validate(test, bucket_file != NULL);
363 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
364 bucket_file_cleanup(bucket_file);
365
366 options.etag = NULL;
367
368 ast_test_status_update(test, "Testing must-revalidate with max-age...\n");
371 bucket_file = ast_bucket_file_retrieve(uri);
372 ast_test_validate(test, bucket_file != NULL);
373 VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 300, 3);
374 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 1);
375
376 return AST_TEST_PASS;
377}

References ast_bucket_file_is_stale(), ast_bucket_file_retrieve(), AST_TEST_NOT_RUN, AST_TEST_PASS, ast_test_status_update, ast_tvnow(), bucket_file_cleanup(), test_options::cache_control, CATEGORY, test_options::etag, sip_to_pjsip::info(), test_options::maxage, test_options::must_revalidate, test_options::no_cache, NULL, options, RAII_VAR, test_options::send_file, server_uri, test_options::status_code, test_options::status_text, TEST_EXECUTE, TEST_INIT, and VALIDATE_EXPIRES.

◆ AST_TEST_DEFINE() [4/9]

AST_TEST_DEFINE ( retrieve_content_type  )

Definition at line 228 of file test_http_media_cache.c.

229{
230 RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
231 char uri[1024];
232
233 switch (cmd) {
234 case TEST_INIT:
235 info->name = __func__;
236 info->category = CATEGORY;
237 info->summary = "Test retrieval of a resource with a Content-Type header";
238 info->description =
239 "This test covers retrieval of a resource whose URL does not end with\n"
240 "a parseable extension and whose response includes a Content-Type\n"
241 "header that we recognize.";
242 return AST_TEST_NOT_RUN;
243 case TEST_EXECUTE:
244 break;
245 }
246
247 options.send_file = 1;
248 options.status_code = 200;
249 options.status_text = "OK";
250 options.content_type = "audio/wav";
251
252 snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav?account_id=1234");
253
254 bucket_file = ast_bucket_file_retrieve(uri);
255 ast_test_validate(test, bucket_file != NULL);
256 ast_test_validate(test, !strcmp(uri, ast_sorcery_object_get_id(bucket_file)));
257 ast_test_validate(test, !ast_strlen_zero(bucket_file->path));
258 VALIDATE_STR_METADATA(test, bucket_file, "ext", ".wav");
259
260 return AST_TEST_PASS;
261}
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2317
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
const char * content_type
#define VALIDATE_STR_METADATA(test, bucket_file, key, expected)

References ast_bucket_file_retrieve(), ast_sorcery_object_get_id(), ast_strlen_zero(), AST_TEST_NOT_RUN, AST_TEST_PASS, bucket_file_cleanup(), CATEGORY, test_options::content_type, sip_to_pjsip::info(), NULL, options, RAII_VAR, test_options::send_file, server_uri, test_options::status_code, test_options::status_text, TEST_EXECUTE, TEST_INIT, and VALIDATE_STR_METADATA.

◆ AST_TEST_DEFINE() [5/9]

AST_TEST_DEFINE ( retrieve_etag  )

Definition at line 571 of file test_http_media_cache.c.

572{
573 RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
574 struct timeval now = ast_tvnow();
575 char uri[1024];
576
577 switch (cmd) {
578 case TEST_INIT:
579 info->name = __func__;
580 info->category = CATEGORY;
581 info->summary = "Test retrieval with an ETag";
582 info->description =
583 "This test covers retrieving a resource that has an ETag.\n"
584 "After retrieval of the resource, staleness is checked. With\n"
585 "matching ETags, we expect the resource to not be stale. When\n"
586 "the ETag does not match, we expect the resource to be stale.";
587 return AST_TEST_NOT_RUN;
588 case TEST_EXECUTE:
589 break;
590 }
591
592 options.send_file = 1;
593 options.status_code = 200;
594 options.status_text = "OK";
595 options.etag = "123456789";
596
597 snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav");
598
599 bucket_file = ast_bucket_file_retrieve(uri);
600 ast_test_validate(test, bucket_file != NULL);
601 ast_test_validate(test, !strcmp(uri, ast_sorcery_object_get_id(bucket_file)));
602 ast_test_validate(test, !ast_strlen_zero(bucket_file->path));
603 VALIDATE_STR_METADATA(test, bucket_file, "etag", options.etag);
604 VALIDATE_EXPIRES(test, bucket_file, now.tv_sec, 3);
605
606 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
607
608 options.etag = "99999999";
609 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 1);
610
611 return AST_TEST_PASS;
612}

References ast_bucket_file_is_stale(), ast_bucket_file_retrieve(), ast_sorcery_object_get_id(), ast_strlen_zero(), AST_TEST_NOT_RUN, AST_TEST_PASS, ast_tvnow(), bucket_file_cleanup(), CATEGORY, test_options::etag, sip_to_pjsip::info(), NULL, options, RAII_VAR, test_options::send_file, server_uri, test_options::status_code, test_options::status_text, TEST_EXECUTE, TEST_INIT, VALIDATE_EXPIRES, and VALIDATE_STR_METADATA.

◆ AST_TEST_DEFINE() [6/9]

AST_TEST_DEFINE ( retrieve_etag_expired  )

Definition at line 480 of file test_http_media_cache.c.

481{
482 RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
483 struct timeval now = ast_tvnow();
484 char uri[1024];
485
486 switch (cmd) {
487 case TEST_INIT:
488 info->name = __func__;
489 info->category = CATEGORY;
490 info->summary = "Test retrieval of an expired resource with an ETag";
491 info->description =
492 "This test covers a staleness check of a resource with an ETag\n"
493 "that has also expired. It guarantees that even if a resource\n"
494 "is expired, we will still not consider it stale if the resource\n"
495 "has not changed per the ETag value.";
496 return AST_TEST_NOT_RUN;
497 case TEST_EXECUTE:
498 break;
499 }
500
501 options.send_file = 1;
502 options.status_code = 200;
503 options.status_text = "OK";
504 options.etag = "123456789";
505 options.expires.tv_sec = now.tv_sec - 1;
506
507 snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav");
508
509 bucket_file = ast_bucket_file_retrieve(uri);
510 ast_test_validate(test, bucket_file != NULL);
511 ast_test_validate(test, !strcmp(uri, ast_sorcery_object_get_id(bucket_file)));
512 ast_test_validate(test, !ast_strlen_zero(bucket_file->path));
513 VALIDATE_STR_METADATA(test, bucket_file, "etag", options.etag);
514 VALIDATE_EXPIRES(test, bucket_file, now.tv_sec - 1, 3);
515
516 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
517
518 return AST_TEST_PASS;
519}

References ast_bucket_file_is_stale(), ast_bucket_file_retrieve(), ast_sorcery_object_get_id(), ast_strlen_zero(), AST_TEST_NOT_RUN, AST_TEST_PASS, ast_tvnow(), bucket_file_cleanup(), CATEGORY, test_options::etag, test_options::expires, sip_to_pjsip::info(), NULL, options, RAII_VAR, test_options::send_file, server_uri, test_options::status_code, test_options::status_text, TEST_EXECUTE, TEST_INIT, VALIDATE_EXPIRES, and VALIDATE_STR_METADATA.

◆ AST_TEST_DEFINE() [7/9]

AST_TEST_DEFINE ( retrieve_expires  )

Definition at line 521 of file test_http_media_cache.c.

522{
523 RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
524 struct timeval now = ast_tvnow();
525 char uri[1024];
526
527 switch (cmd) {
528 case TEST_INIT:
529 info->name = __func__;
530 info->category = CATEGORY;
531 info->summary = "Test retrieval with explicit expiration";
532 info->description =
533 "This test covers retrieving a resource that has an Expires.\n"
534 "After retrieval of the resource, staleness is checked. With\n"
535 "a non-expired resource, we expect the resource to not be stale.\n"
536 "When the expiration has occurred, we expect the staleness check\n"
537 "to fail.";
538 return AST_TEST_NOT_RUN;
539 case TEST_EXECUTE:
540 break;
541 }
542
543 options.send_file = 1;
544 options.status_code = 200;
545 options.status_text = "OK";
546 options.expires.tv_sec = now.tv_sec + 3000;
547
548 snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav");
549
550 bucket_file = ast_bucket_file_retrieve(uri);
551 ast_test_validate(test, bucket_file != NULL);
552 ast_test_validate(test, !strcmp(uri, ast_sorcery_object_get_id(bucket_file)));
553 ast_test_validate(test, !ast_strlen_zero(bucket_file->path));
554 VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 3000, 3);
555
556 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
557
558 /* Clean up previous result */
559 bucket_file_cleanup(bucket_file);
560
561 options.expires.tv_sec = now.tv_sec - 1;
562 bucket_file = ast_bucket_file_retrieve(uri);
563 ast_test_validate(test, bucket_file != NULL);
564 VALIDATE_EXPIRES(test, bucket_file, now.tv_sec - 1, 3);
565
566 ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 1);
567
568 return AST_TEST_PASS;
569}

References ast_bucket_file_is_stale(), ast_bucket_file_retrieve(), ast_sorcery_object_get_id(), ast_strlen_zero(), AST_TEST_NOT_RUN, AST_TEST_PASS, ast_tvnow(), bucket_file_cleanup(), CATEGORY, test_options::expires, sip_to_pjsip::info(), NULL, options, RAII_VAR, test_options::send_file, server_uri, test_options::status_code, test_options::status_text, TEST_EXECUTE, TEST_INIT, and VALIDATE_EXPIRES.

◆ AST_TEST_DEFINE() [8/9]

AST_TEST_DEFINE ( retrieve_nominal  )

Definition at line 614 of file test_http_media_cache.c.

615{
616 RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
617 struct timeval now = ast_tvnow();
618 char uri[1024];
619
620 switch (cmd) {
621 case TEST_INIT:
622 info->name = __func__;
623 info->category = CATEGORY;
624 info->summary = "Test nominal retrieval";
625 info->description =
626 "Test nominal retrieval of a resource.";
627 return AST_TEST_NOT_RUN;
628 case TEST_EXECUTE:
629 break;
630 }
631
632 options.send_file = 1;
633 options.status_code = 200;
634 options.status_text = "OK";
635
636 snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav");
637
638 bucket_file = ast_bucket_file_retrieve(uri);
639 ast_test_validate(test, bucket_file != NULL);
640 ast_test_validate(test, !strcmp(uri, ast_sorcery_object_get_id(bucket_file)));
641 ast_test_validate(test, !ast_strlen_zero(bucket_file->path));
642 VALIDATE_EXPIRES(test, bucket_file, now.tv_sec, 3);
643
644 return AST_TEST_PASS;
645}

References ast_bucket_file_retrieve(), ast_sorcery_object_get_id(), ast_strlen_zero(), AST_TEST_NOT_RUN, AST_TEST_PASS, ast_tvnow(), bucket_file_cleanup(), CATEGORY, sip_to_pjsip::info(), NULL, options, RAII_VAR, test_options::send_file, server_uri, test_options::status_code, test_options::status_text, TEST_EXECUTE, TEST_INIT, and VALIDATE_EXPIRES.

◆ AST_TEST_DEFINE() [9/9]

AST_TEST_DEFINE ( retrieve_parsed_uri  )

Definition at line 263 of file test_http_media_cache.c.

264{
265 RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
266 char uri[1024];
267
268 switch (cmd) {
269 case TEST_INIT:
270 info->name = __func__;
271 info->category = CATEGORY;
272 info->summary = "Test retrieval of a resource with a complex URI";
273 info->description =
274 "This test covers retrieval of a resource whose URL does not end with\n"
275 "a parseable extension, but the path portion of the URL does end with\n"
276 "parseable extension.";
277 return AST_TEST_NOT_RUN;
278 case TEST_EXECUTE:
279 break;
280 }
281
282 options.send_file = 1;
283 options.status_code = 200;
284 options.status_text = "OK";
285
286 snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav?account_id=1234");
287
288 bucket_file = ast_bucket_file_retrieve(uri);
289 ast_test_validate(test, bucket_file != NULL);
290 ast_test_validate(test, !strcmp(uri, ast_sorcery_object_get_id(bucket_file)));
291 ast_test_validate(test, !ast_strlen_zero(bucket_file->path));
292 VALIDATE_STR_METADATA(test, bucket_file, "ext", ".wav");
293
294 return AST_TEST_PASS;
295}

References ast_bucket_file_retrieve(), ast_sorcery_object_get_id(), ast_strlen_zero(), AST_TEST_NOT_RUN, AST_TEST_PASS, bucket_file_cleanup(), CATEGORY, sip_to_pjsip::info(), NULL, options, RAII_VAR, test_options::send_file, server_uri, test_options::status_code, test_options::status_text, TEST_EXECUTE, TEST_INIT, and VALIDATE_STR_METADATA.

◆ bucket_file_cleanup()

static void bucket_file_cleanup ( void *  obj)
static

Definition at line 218 of file test_http_media_cache.c.

219{
220 struct ast_bucket_file *bucket_file = obj;
221
222 if (bucket_file) {
223 ast_bucket_file_delete(bucket_file);
224 ao2_ref(bucket_file, -1);
225 }
226}
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
int ast_bucket_file_delete(struct ast_bucket_file *file)
Delete a bucket file from backend storage.
Definition: bucket.c:844

References ao2_ref, and ast_bucket_file_delete().

Referenced by AST_TEST_DEFINE().

◆ http_callback()

static int http_callback ( struct ast_tcptls_session_instance ser,
const struct ast_http_uri urih,
const char *  uri,
enum ast_http_method  method,
struct ast_variable get_params,
struct ast_variable headers 
)
static

Definition at line 92 of file test_http_media_cache.c.

93{
94 char file_name[64] = "/tmp/test-media-cache-XXXXXX";
95 struct ast_str *http_header = ast_str_create(128);
96 struct ast_str *cache_control = ast_str_create(128);
97 int fd = -1;
98 int unmodified = 0;
99 int send_file = options.send_file && method == AST_HTTP_GET;
100
101 if (!http_header) {
102 goto error;
103 }
104
105 if (send_file) {
106 char buf[1024];
107
108 fd = mkstemp(file_name);
109 if (fd == -1) {
110 ast_log(LOG_ERROR, "Unable to open temp file for testing: %s (%d)", strerror(errno), errno);
111 goto error;
112 }
113
114 memset(buf, 1, sizeof(buf));
115 if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
116 ast_log(LOG_ERROR, "Failed to write expected number of bytes to pipe\n");
117 close(fd);
118 goto error;
119 }
120 close(fd);
121
122 fd = open(file_name, 0);
123 if (fd == -1) {
124 ast_log(LOG_ERROR, "Unable to open temp file for testing: %s (%d)", strerror(errno), errno);
125 goto error;
126 }
127 }
128
130 ast_str_append(&http_header, 0, "Content-Type: %s\r\n", options.content_type);
131 }
132
134 SET_OR_APPEND_CACHE_CONTROL(cache_control);
135 ast_str_append(&cache_control, 0, "max-age=%d", options.cache_control.maxage);
136 }
137
139 SET_OR_APPEND_CACHE_CONTROL(cache_control);
140 ast_str_append(&cache_control, 0, "s-maxage=%d", options.cache_control.s_maxage);
141 }
142
144 SET_OR_APPEND_CACHE_CONTROL(cache_control);
145 ast_str_append(&cache_control, 0, "%s", "no-cache");
146 }
147
149 SET_OR_APPEND_CACHE_CONTROL(cache_control);
150 ast_str_append(&cache_control, 0, "%s", "must-revalidate");
151 }
152
153 if (ast_str_strlen(cache_control)) {
154 ast_str_append(&http_header, 0, "%s\r\n", ast_str_buffer(cache_control));
155 }
156
157 if (options.expires.tv_sec) {
158 struct ast_tm now_time;
159 char tmbuf[64];
160
161 ast_localtime(&options.expires, &now_time, NULL);
162 ast_strftime(tmbuf, sizeof(tmbuf), "%a, %d %b %Y %T %z", &now_time);
163 ast_str_append(&http_header, 0, "Expires: %s\r\n", tmbuf);
164 }
165
167 struct ast_variable *v;
168
169 ast_str_append(&http_header, 0, "ETag: %s\r\n", options.etag);
170 for (v = headers; v; v = v->next) {
171 if (!strcasecmp(v->name, "If-None-Match") && !strcasecmp(v->value, options.etag)) {
172 unmodified = 1;
173 break;
174 }
175 }
176 }
177
178 if (!unmodified) {
179 ast_http_send(ser, method, options.status_code, options.status_text, http_header, NULL, send_file ? fd : 0, 1);
180 } else {
181 ast_http_send(ser, method, 304, "Not Modified", http_header, NULL, 0, 1);
182 }
183
184 if (send_file) {
185 close(fd);
186 unlink(file_name);
187 }
188
189 ast_free(cache_control);
190
191 return 0;
192
193error:
194 ast_free(http_header);
195 ast_free(cache_control);
197 ast_http_error(ser, 418, "I'm a Teapot", "Please don't ask me to brew coffee.");
198
199 return 0;
200}
#define ast_free(a)
Definition: astmm.h:180
#define ast_log
Definition: astobj2.c:42
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method method, int status_code, const char *status_title, struct ast_str *http_header, struct ast_str *out, int fd, unsigned int static_content)
Generic function for sending HTTP/1.1 response.
Definition: http.c:459
@ AST_HTTP_GET
Definition: http.h:60
void ast_http_request_close_on_completion(struct ast_tcptls_session_instance *ser)
Request the HTTP connection be closed after this HTTP request.
Definition: http.c:840
void ast_http_error(struct ast_tcptls_session_instance *ser, int status, const char *title, const char *text)
Send HTTP error message and close socket.
Definition: http.c:651
#define LOG_ERROR
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
int errno
const char * method
Definition: res_pjsip.c:1279
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
Support for dynamic strings.
Definition: strings.h:623
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
#define SET_OR_APPEND_CACHE_CONTROL(str)
int error(const char *format,...)
Definition: utils/frame.c:999

References ast_free, ast_http_error(), AST_HTTP_GET, ast_http_request_close_on_completion(), ast_http_send(), ast_localtime(), ast_log, ast_str_append(), ast_str_buffer(), ast_str_create, ast_str_strlen(), ast_strftime(), ast_strlen_zero(), buf, test_options::cache_control, test_options::content_type, errno, error(), test_options::etag, test_options::expires, LOG_ERROR, test_options::maxage, method, test_options::must_revalidate, ast_variable::name, ast_variable::next, test_options::no_cache, NULL, options, test_options::s_maxage, test_options::send_file, SET_OR_APPEND_CACHE_CONTROL, test_options::status_code, test_options::status_text, and ast_variable::value.

◆ load_module()

static int load_module ( void  )
static

Definition at line 729 of file test_http_media_cache.c.

730{
731 if (process_config(0)) {
733 }
734
737 }
738
739 AST_TEST_REGISTER(create_nominal);
740
741 AST_TEST_REGISTER(retrieve_nominal);
742 AST_TEST_REGISTER(retrieve_etag);
743 AST_TEST_REGISTER(retrieve_expires);
744 AST_TEST_REGISTER(retrieve_etag_expired);
745 AST_TEST_REGISTER(retrieve_cache_control_age);
746 AST_TEST_REGISTER(retrieve_cache_control_directives);
747 AST_TEST_REGISTER(retrieve_parsed_uri);
748 AST_TEST_REGISTER(retrieve_content_type);
749
750 ast_test_register_init(CATEGORY, pre_test_cb);
751
753}
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition: http.c:676
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
static int process_config(int reload)
static int pre_test_cb(struct ast_test_info *info, struct ast_test *test)
static struct ast_http_uri test_uri

References ast_http_uri_link(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, AST_TEST_REGISTER, CATEGORY, pre_test_cb(), process_config(), and test_uri.

◆ pre_test_cb()

static int pre_test_cb ( struct ast_test_info info,
struct ast_test *  test 
)
static

Definition at line 211 of file test_http_media_cache.c.

212{
213 memset(&options, 0, sizeof(options));
214
215 return 0;
216}

References options.

Referenced by load_module().

◆ process_config()

static int process_config ( int  reload)
static

Definition at line 681 of file test_http_media_cache.c.

682{
683 struct ast_config *config;
684 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
685 const char *bindaddr;
686 const char *bindport;
687 const char *prefix;
688 const char *enabled;
689
690 config = ast_config_load("http.conf", config_flags);
692 return -1;
693 } else if (config == CONFIG_STATUS_FILEUNCHANGED) {
694 return 0;
695 }
696
697 enabled = ast_config_option(config, "general", "enabled");
698 if (!enabled || ast_false(enabled)) {
700 return -1;
701 }
702
703 /* Construct our Server URI */
704 bindaddr = ast_config_option(config, "general", "bindaddr");
705 if (!bindaddr) {
707 return -1;
708 }
709
710 bindport = ast_config_option(config, "general", "bindport");
711 if (!bindport) {
712 bindport = "8088";
713 }
714
715 prefix = ast_config_option(config, "general", "prefix");
716
717 snprintf(server_uri, sizeof(server_uri), "http://%s:%s%s/%s", bindaddr, bindport, S_OR(prefix, ""), TEST_URI);
718
720
721 return 0;
722}
struct ast_sockaddr bindaddr
Definition: chan_ooh323.c:353
static const char config[]
Definition: chan_ooh323.c:111
static int enabled
Definition: dnsmgr.c:91
static char prefix[MAX_PREFIX]
Definition: http.c:144
#define ast_config_load(filename, flags)
Load a config file.
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
const char * ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
Retrieve a configuration variable within the configuration set.
Definition: main/config.c:774
@ CONFIG_FLAG_FILEUNCHANGED
static int reload(void)
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"....
Definition: utils.c:2216
Structure used to handle boolean flags.
Definition: utils.h:199
#define TEST_URI

References ast_config_destroy(), ast_config_load, ast_config_option(), ast_false(), bindaddr, config, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, enabled, prefix, reload(), S_OR, server_uri, and TEST_URI.

Referenced by load_module(), and reload_module().

◆ reload_module()

static int reload_module ( void  )
static

Definition at line 724 of file test_http_media_cache.c.

725{
726 return process_config(1);
727}

References process_config().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 755 of file test_http_media_cache.c.

756{
758
759 AST_TEST_UNREGISTER(create_nominal);
760
761 AST_TEST_UNREGISTER(retrieve_nominal);
762 AST_TEST_UNREGISTER(retrieve_etag);
763 AST_TEST_UNREGISTER(retrieve_expires);
764 AST_TEST_UNREGISTER(retrieve_etag_expired);
765 AST_TEST_UNREGISTER(retrieve_cache_control_age);
766 AST_TEST_UNREGISTER(retrieve_cache_control_directives);
767 AST_TEST_UNREGISTER(retrieve_parsed_uri);
768 AST_TEST_UNREGISTER(retrieve_content_type);
769
770 return 0;
771}
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition: http.c:708
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128

References ast_http_uri_unlink(), AST_TEST_UNREGISTER, and test_uri.

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "HTTP Media Cache Backend Tests" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .reload = reload_module, .unload = unload_module, .requires = "res_http_media_cache", }
static

Definition at line 779 of file test_http_media_cache.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 779 of file test_http_media_cache.c.

◆ options

struct test_options options
static
Examples
app_skel.c.

Definition at line 63 of file test_http_media_cache.c.

Referenced by __ao2_alloc(), __ast_sorcery_apply_config(), __ssl_setup(), aeap_cli_show(), agent_connect_caller(), agent_login_exec(), app_exec(), apply_options(), aqm_exec(), ast_app_options2str64(), ast_app_parse_options(), ast_app_parse_options64(), ast_ari_bridges_record(), ast_ari_channels_record(), ast_cdr_fork(), ast_fax_maxrate(), ast_fax_minrate(), ast_get_enum(), ast_multicast_rtp_create_options(), ast_rtp_codecs_payloads_set_rtpmap_type(), ast_rtp_codecs_payloads_set_rtpmap_type_rate(), ast_rtp_lookup_mime_multiple2(), ast_rtp_lookup_mime_subtype2(), ast_say_enumeration(), ast_say_enumeration_full_da(), ast_say_enumeration_full_de(), ast_say_enumeration_full_he(), ast_say_enumeration_full_is(), ast_say_number(), ast_say_number_full_cs(), ast_say_number_full_da(), ast_say_number_full_de(), ast_say_number_full_es(), ast_say_number_full_fr(), ast_say_number_full_he(), ast_say_number_full_is(), ast_say_number_full_no(), ast_say_number_full_pl(), ast_say_number_full_pt(), ast_say_number_full_ru(), ast_say_number_full_se(), ast_say_number_full_ur(), ast_say_ordinal(), ast_smdi_md_message_wait(), ast_smdi_mwi_message_wait(), ast_smdi_mwi_message_wait_station(), ast_sorcery_init(), ast_start_mixmonitor(), AST_TEST_DEFINE(), ast_threadpool_create(), ast_websocket_client_create(), ast_websocket_client_create_with_options(), audiosocket_request(), auth_exec(), bridge_exec(), bridgewait_exec(), broadcast_exec(), cdr_prop_write_callback(), cdr_read_callback(), cdr_write(), cdr_write_callback(), chanavail_exec(), chanspy_exec(), cli_fax_show_settings(), conf_exec(), config_object_cli_show(), controlplayback_exec(), detect_write(), dial_exec_full(), directory_exec(), disa_exec(), do_say(), dundi_query_read(), dundifunc_read(), enable_jack_hook(), extenspy_exec(), feature_automixmonitor(), file_read(), file_write(), forkcdr_exec(), function_enum(), g722_decode_init(), g722_encode_init(), generate_rtpmap_attr(), generate_rtpmap_attr2(), geoloc_profile_read(), geoloc_profile_write(), get_codecs(), get_general_options(), handle_cli_presencestate_change(), handle_cli_presencestate_list(), handle_options(), handle_updates(), hash_ao2_container_init(), hash_ao2_insert_node(), hint_read(), http_callback(), internal_ao2_alloc(), json_decode_read(), leave_voicemail(), load_module(), manager_mixmonitor(), milliwatt_exec(), mixmonitor_exec(), multicast_rtp_request(), originate_exec(), page_exec(), page_state_callback(), park_and_announce_app_exec(), park_app_parse_data(), parse_data(), parse_options(), pbx_builtin_answer(), pbx_builtin_background(), pbx_builtin_incomplete(), pbx_builtin_saycharacters_case(), pbx_builtin_saynumber(), pbx_builtin_sayordinal(), pbx_builtin_waitexten(), pickupchan_exec(), playback_exec(), pqm_exec(), pre_session_setup(), pre_test_cb(), presence_read(), presence_write(), privacy_exec(), process_dahdi(), queue_exec(), queue_match(), rb_ao2_container_init(), rb_ao2_insert_node(), read_exec(), read_mf_exec(), read_sf_exec(), readexten_exec(), receivefax_exec(), record_exec(), recording_options_dtor(), say_enumeration_full(), say_full(), say_number_full(), sayunixtime_exec(), scan_exec(), search_directory_sub(), senddtmf_exec(), sendfax_exec(), session_details_new(), set_config(), set_general_options(), setup_profile_bridge(), setup_profile_caller(), setup_profile_paged(), sla_add_trunk_to_station(), sla_trunk_exec(), smdi_message_wait(), smdi_msg_find(), smdi_msg_retrieve_read(), sms_exec(), softhangup_exec(), sorcery_memory_cache_open(), speech_background(), start_mixmonitor_callback(), stasis_app_control_record(), stasis_app_recording_options_create(), test_make_nonsorted(), test_make_sorted(), threadpool_alloc(), unicast_rtp_request(), upqm_exec(), vmauthenticate(), volume_read(), volume_write(), wait_exec(), websocket_client_create(), xmpp_pubsub_build_publish_skeleton(), xmpp_pubsub_subscribe(), and zapateller_exec().

◆ server_uri

char server_uri[512]
static

◆ test_uri

struct ast_http_uri test_uri
static

Definition at line 202 of file test_http_media_cache.c.

Referenced by load_module(), and unload_module().