Merge "doc/lang/language-criteria.txt: Link to wiki."
[asterisk/asterisk.git] / tests / test_http_media_cache.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2015, Matt Jordan
5  *
6  * Matt Jordan <mjordan@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  * \brief Tests for the HTTP media cache backend
22  *
23  * \author \verbatim Matt Jordan <mjordan@digium.com> \endverbatim
24  *
25  * \ingroup tests
26  */
27
28 /*** MODULEINFO
29         <depend>TEST_FRAMEWORK</depend>
30         <depend>curl</depend>
31         <depend>res_http_media_cache</depend>
32         <support_level>core</support_level>
33  ***/
34
35 #include "asterisk.h"
36
37 #include <fcntl.h>
38
39 #include "asterisk/module.h"
40 #include "asterisk/http.h"
41 #include "asterisk/bucket.h"
42 #include "asterisk/test.h"
43
44 #define CATEGORY "/res/http_media_cache/"
45
46 #define TEST_URI "test_media_cache"
47
48 struct test_options {
49         int status_code;
50         int send_file;
51         struct {
52                 int s_maxage;
53                 int maxage;
54                 int no_cache;
55                 int must_revalidate;
56         } cache_control;
57         struct timeval expires;
58         const char *status_text;
59         const char *etag;
60 };
61
62 static struct test_options options;
63
64 static char server_uri[512];
65
66 #define VALIDATE_EXPIRES(test, bucket_file, expected, delta) do { \
67         RAII_VAR(struct ast_bucket_metadata *, metadata, ast_bucket_file_metadata_get((bucket_file), "__actual_expires"), ao2_cleanup); \
68         int actual_expires; \
69         ast_test_validate(test, metadata != NULL); \
70         ast_test_validate(test, sscanf(metadata->value, "%d", &actual_expires) == 1); \
71         ast_test_status_update(test, "Checking %d >= %d and %d <= %d\n", \
72                         (int) ((expected) + (delta)), actual_expires, \
73                         (int) ((expected) - (delta)), actual_expires); \
74         ast_test_validate(test, (((expected) + (delta) >= actual_expires) && ((expected) - (delta) <= actual_expires))); \
75 } while (0)
76
77 #define VALIDATE_STR_METADATA(test, bucket_file, key, expected) do { \
78         RAII_VAR(struct ast_bucket_metadata *, metadata, ast_bucket_file_metadata_get((bucket_file), (key)), ao2_cleanup); \
79         ast_test_validate(test, metadata != NULL); \
80         ast_test_validate(test, !strcmp(metadata->value, (expected))); \
81 } while (0)
82
83 #define SET_OR_APPEND_CACHE_CONTROL(str) do { \
84         if (!ast_str_strlen((str))) { \
85                 ast_str_set(&(str), 0, "%s", "cache-control: "); \
86         } else { \
87                 ast_str_append(&(str), 0, "%s", ", "); \
88         } \
89 } while (0)
90
91 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)
92 {
93         char file_name[64] = "/tmp/test-media-cache-XXXXXX";
94         struct ast_str *http_header = ast_str_create(128);
95         struct ast_str *cache_control = ast_str_create(128);
96         int fd = -1;
97         int unmodified = 0;
98         int send_file = options.send_file && method == AST_HTTP_GET;
99
100         if (!http_header) {
101                 goto error;
102         }
103
104         if (send_file) {
105                 char buf[1024];
106
107                 fd = mkstemp(file_name);
108                 if (fd == -1) {
109                         ast_log(LOG_ERROR, "Unable to open temp file for testing: %s (%d)", strerror(errno), errno);
110                         goto error;
111                 }
112
113                 memset(buf, 1, sizeof(buf));
114                 if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
115                         ast_log(LOG_ERROR, "Failed to write expected number of bytes to pipe\n");
116                         close(fd);
117                         goto error;
118                 }
119                 close(fd);
120
121                 fd = open(file_name, 0);
122                 if (fd == -1) {
123                         ast_log(LOG_ERROR, "Unable to open temp file for testing: %s (%d)", strerror(errno), errno);
124                         goto error;
125                 }
126         }
127
128         if (options.cache_control.maxage) {
129                 SET_OR_APPEND_CACHE_CONTROL(cache_control);
130                 ast_str_append(&cache_control, 0, "max-age=%d", options.cache_control.maxage);
131         }
132
133         if (options.cache_control.s_maxage) {
134                 SET_OR_APPEND_CACHE_CONTROL(cache_control);
135                 ast_str_append(&cache_control, 0, "s-maxage=%d", options.cache_control.s_maxage);
136         }
137
138         if (options.cache_control.no_cache) {
139                 SET_OR_APPEND_CACHE_CONTROL(cache_control);
140                 ast_str_append(&cache_control, 0, "%s", "no-cache");
141         }
142
143         if (options.cache_control.must_revalidate) {
144                 SET_OR_APPEND_CACHE_CONTROL(cache_control);
145                 ast_str_append(&cache_control, 0, "%s", "must-revalidate");
146         }
147
148         if (ast_str_strlen(cache_control)) {
149                 ast_str_append(&http_header, 0, "%s\r\n", ast_str_buffer(cache_control));
150         }
151
152         if (options.expires.tv_sec) {
153                 struct ast_tm now_time;
154                 char tmbuf[64];
155
156                 ast_localtime(&options.expires, &now_time, NULL);
157                 ast_strftime(tmbuf, sizeof(tmbuf), "%a, %d %b %Y %T %z", &now_time);
158                 ast_str_append(&http_header, 0, "Expires: %s\r\n", tmbuf);
159         }
160
161         if (!ast_strlen_zero(options.etag)) {
162                 struct ast_variable *v;
163
164                 ast_str_append(&http_header, 0, "ETag: %s\r\n", options.etag);
165                 for (v = headers; v; v = v->next) {
166                         if (!strcasecmp(v->name, "If-None-Match") && !strcasecmp(v->value, options.etag)) {
167                                 unmodified = 1;
168                                 break;
169                         }
170                 }
171         }
172
173         if (!unmodified) {
174                 ast_http_send(ser, method, options.status_code, options.status_text, http_header, NULL, send_file ? fd : 0, 1);
175         } else {
176                 ast_http_send(ser, method, 304, "Not Modified", http_header, NULL, 0, 1);
177         }
178
179         if (send_file) {
180                 close(fd);
181                 unlink(file_name);
182         }
183
184         ast_free(cache_control);
185
186         return 0;
187
188 error:
189         ast_free(http_header);
190         ast_free(cache_control);
191         ast_http_request_close_on_completion(ser);
192         ast_http_error(ser, 418, "I'm a Teapot", "Please don't ask me to brew coffee.");
193
194         return 0;
195 }
196
197 static struct ast_http_uri test_uri = {
198         .description = "HTTP Media Cache Test URI",
199         .uri = TEST_URI,
200         .callback = http_callback,
201         .has_subtree = 1,
202         .data = NULL,
203         .key = __FILE__,
204 };
205
206 static int pre_test_cb(struct ast_test_info *info, struct ast_test *test)
207 {
208         memset(&options, 0, sizeof(options));
209
210         return 0;
211 }
212
213 static void bucket_file_cleanup(void *obj)
214 {
215         struct ast_bucket_file *bucket_file = obj;
216
217         if (bucket_file) {
218                 ast_bucket_file_delete(bucket_file);
219                 ao2_ref(bucket_file, -1);
220         }
221 }
222
223 AST_TEST_DEFINE(retrieve_cache_control_directives)
224 {
225         RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
226         struct timeval now = ast_tvnow();
227         char uri[1024];
228
229         switch (cmd) {
230         case TEST_INIT:
231                 info->name = __func__;
232                 info->category = CATEGORY;
233                 info->summary = "Test retrieval of a resource with Cache-Control directives that affect staleness";
234                 info->description =
235                         "This test covers retrieval of a resource with the Cache-Control header,\n"
236                         "which specifies no-cache and/or must-revalidate.";
237                 return AST_TEST_NOT_RUN;
238         case TEST_EXECUTE:
239                 break;
240         }
241
242         snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav");
243
244         options.send_file = 1;
245         options.status_code = 200;
246         options.status_text = "OK";
247
248         ast_test_status_update(test, "Testing no-cache...\n");
249         options.cache_control.no_cache = 1;
250         bucket_file = ast_bucket_file_retrieve(uri);
251         ast_test_validate(test, bucket_file != NULL);
252         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 1);
253         bucket_file_cleanup(bucket_file);
254
255         ast_test_status_update(test, "Testing no-cache with ETag...\n");
256         options.cache_control.no_cache = 1;
257         options.etag = "123456789";
258         bucket_file = ast_bucket_file_retrieve(uri);
259         ast_test_validate(test, bucket_file != NULL);
260         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
261         bucket_file_cleanup(bucket_file);
262
263         options.etag = NULL;
264
265         ast_test_status_update(test, "Testing no-cache with max-age...\n");
266         options.cache_control.no_cache = 1;
267         options.cache_control.maxage = 300;
268         bucket_file = ast_bucket_file_retrieve(uri);
269         ast_test_validate(test, bucket_file != NULL);
270         VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 300, 3);
271         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 1);
272         bucket_file_cleanup(bucket_file);
273
274         options.cache_control.maxage = 0;
275         options.cache_control.no_cache = 0;
276
277         ast_test_status_update(test, "Testing must-revalidate...\n");
278         options.cache_control.must_revalidate = 1;
279         bucket_file = ast_bucket_file_retrieve(uri);
280         ast_test_validate(test, bucket_file != NULL);
281         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 1);
282         bucket_file_cleanup(bucket_file);
283
284         ast_test_status_update(test, "Testing must-revalidate with ETag...\n");
285         options.cache_control.must_revalidate = 1;
286         options.etag = "123456789";
287         bucket_file = ast_bucket_file_retrieve(uri);
288         ast_test_validate(test, bucket_file != NULL);
289         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
290         bucket_file_cleanup(bucket_file);
291
292         options.etag = NULL;
293
294         ast_test_status_update(test, "Testing must-revalidate with max-age...\n");
295         options.cache_control.must_revalidate = 1;
296         options.cache_control.maxage = 300;
297         bucket_file = ast_bucket_file_retrieve(uri);
298         ast_test_validate(test, bucket_file != NULL);
299         VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 300, 3);
300         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 1);
301
302         return AST_TEST_PASS;
303 }
304
305 AST_TEST_DEFINE(retrieve_cache_control_age)
306 {
307         RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
308         struct timeval now = ast_tvnow();
309         char uri[1024];
310
311         switch (cmd) {
312         case TEST_INIT:
313                 info->name = __func__;
314                 info->category = CATEGORY;
315                 info->summary = "Test retrieval of a resource with age specifiers in Cache-Control";
316                 info->description =
317                         "This test covers retrieval of a resource with the Cache-Control header,\n"
318                         "which specifies max-age and/or s-maxage. The test verifies proper precedence\n"
319                         "ordering of the header attributes, along with its relation if the Expires\n"
320                         "header is present.";
321                 return AST_TEST_NOT_RUN;
322         case TEST_EXECUTE:
323                 break;
324         }
325
326         snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav");
327
328         options.send_file = 1;
329         options.status_code = 200;
330         options.status_text = "OK";
331
332         ast_test_status_update(test, "Testing max-age...\n");
333         options.cache_control.maxage = 300;
334         bucket_file = ast_bucket_file_retrieve(uri);
335         ast_test_validate(test, bucket_file != NULL);
336         VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 300, 3);
337         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
338         bucket_file_cleanup(bucket_file);
339
340         ast_test_status_update(test, "Testing s-maxage...\n");
341         now = ast_tvnow();
342         options.cache_control.maxage = 0;
343         options.cache_control.s_maxage = 300;
344         bucket_file = ast_bucket_file_retrieve(uri);
345         ast_test_validate(test, bucket_file != NULL);
346         VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 300, 3);
347         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
348         bucket_file_cleanup(bucket_file);
349
350         ast_test_status_update(test, "Testing max-age and s-maxage...\n");
351         now = ast_tvnow();
352         options.cache_control.maxage = 300;
353         options.cache_control.s_maxage = 600;
354         bucket_file = ast_bucket_file_retrieve(uri);
355         ast_test_validate(test, bucket_file != NULL);
356         VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 600, 3);
357         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
358         bucket_file_cleanup(bucket_file);
359
360         ast_test_status_update(test, "Testing max-age and Expires...\n");
361         now = ast_tvnow();
362         options.cache_control.maxage = 300;
363         options.cache_control.s_maxage = 0;
364         options.expires.tv_sec = now.tv_sec + 3000;
365         bucket_file = ast_bucket_file_retrieve(uri);
366         ast_test_validate(test, bucket_file != NULL);
367         VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 300, 3);
368         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
369         bucket_file_cleanup(bucket_file);
370
371         ast_test_status_update(test, "Testing s-maxage and Expires...\n");
372         now = ast_tvnow();
373         options.cache_control.maxage = 0;
374         options.cache_control.s_maxage = 300;
375         options.expires.tv_sec = now.tv_sec + 3000;
376         bucket_file = ast_bucket_file_retrieve(uri);
377         ast_test_validate(test, bucket_file != NULL);
378         VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 300, 3);
379         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
380         bucket_file_cleanup(bucket_file);
381
382         ast_test_status_update(test, "Testing s-maxage and Expires...\n");
383         now = ast_tvnow();
384         options.cache_control.maxage = 0;
385         options.cache_control.s_maxage = 300;
386         options.expires.tv_sec = now.tv_sec + 3000;
387         bucket_file = ast_bucket_file_retrieve(uri);
388         ast_test_validate(test, bucket_file != NULL);
389         VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 300, 3);
390         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
391         bucket_file_cleanup(bucket_file);
392
393         ast_test_status_update(test, "Testing max-age, s-maxage, and Expires...\n");
394         now = ast_tvnow();
395         options.cache_control.maxage = 300;
396         options.cache_control.s_maxage = 600;
397         options.expires.tv_sec = now.tv_sec + 3000;
398         bucket_file = ast_bucket_file_retrieve(uri);
399         ast_test_validate(test, bucket_file != NULL);
400         VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 600, 3);
401         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
402
403         return AST_TEST_PASS;
404 }
405
406 AST_TEST_DEFINE(retrieve_etag_expired)
407 {
408         RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
409         struct timeval now = ast_tvnow();
410         char uri[1024];
411
412         switch (cmd) {
413         case TEST_INIT:
414                 info->name = __func__;
415                 info->category = CATEGORY;
416                 info->summary = "Test retrieval of an expired resource with an ETag";
417                 info->description =
418                         "This test covers a staleness check of a resource with an ETag\n"
419                         "that has also expired. It guarantees that even if a resource\n"
420                         "is expired, we will still not consider it stale if the resource\n"
421                         "has not changed per the ETag value.";
422                 return AST_TEST_NOT_RUN;
423         case TEST_EXECUTE:
424                 break;
425         }
426
427         options.send_file = 1;
428         options.status_code = 200;
429         options.status_text = "OK";
430         options.etag = "123456789";
431         options.expires.tv_sec = now.tv_sec - 1;
432
433         snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav");
434
435         bucket_file = ast_bucket_file_retrieve(uri);
436         ast_test_validate(test, bucket_file != NULL);
437         ast_test_validate(test, !strcmp(uri, ast_sorcery_object_get_id(bucket_file)));
438         ast_test_validate(test, !ast_strlen_zero(bucket_file->path));
439         VALIDATE_STR_METADATA(test, bucket_file, "etag", options.etag);
440         VALIDATE_EXPIRES(test, bucket_file, now.tv_sec - 1, 3);
441
442         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
443
444         return AST_TEST_PASS;
445 }
446
447 AST_TEST_DEFINE(retrieve_expires)
448 {
449         RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
450         struct timeval now = ast_tvnow();
451         char uri[1024];
452
453         switch (cmd) {
454         case TEST_INIT:
455                 info->name = __func__;
456                 info->category = CATEGORY;
457                 info->summary = "Test retrieval with explicit expiration";
458                 info->description =
459                         "This test covers retrieving a resource that has an Expires.\n"
460                         "After retrieval of the resource, staleness is checked. With\n"
461                         "a non-expired resource, we expect the resource to not be stale.\n"
462                         "When the expiration has occurred, we expect the staleness check\n"
463                         "to fail.";
464                 return AST_TEST_NOT_RUN;
465         case TEST_EXECUTE:
466                 break;
467         }
468
469         options.send_file = 1;
470         options.status_code = 200;
471         options.status_text = "OK";
472         options.expires.tv_sec = now.tv_sec + 3000;
473
474         snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav");
475
476         bucket_file = ast_bucket_file_retrieve(uri);
477         ast_test_validate(test, bucket_file != NULL);
478         ast_test_validate(test, !strcmp(uri, ast_sorcery_object_get_id(bucket_file)));
479         ast_test_validate(test, !ast_strlen_zero(bucket_file->path));
480         VALIDATE_EXPIRES(test, bucket_file, now.tv_sec + 3000, 3);
481
482         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
483
484         /* Clean up previous result */
485         bucket_file_cleanup(bucket_file);
486
487         options.expires.tv_sec = now.tv_sec - 1;
488         bucket_file = ast_bucket_file_retrieve(uri);
489         ast_test_validate(test, bucket_file != NULL);
490         VALIDATE_EXPIRES(test, bucket_file, now.tv_sec - 1, 3);
491
492         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 1);
493
494         return AST_TEST_PASS;
495 }
496
497 AST_TEST_DEFINE(retrieve_etag)
498 {
499         RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
500         struct timeval now = ast_tvnow();
501         char uri[1024];
502
503         switch (cmd) {
504         case TEST_INIT:
505                 info->name = __func__;
506                 info->category = CATEGORY;
507                 info->summary = "Test retrieval with an ETag";
508                 info->description =
509                         "This test covers retrieving a resource that has an ETag.\n"
510                         "After retrieval of the resource, staleness is checked. With\n"
511                         "matching ETags, we expect the resource to not be stale. When\n"
512                         "the ETag does not match, we expect the resource to be stale.";
513                 return AST_TEST_NOT_RUN;
514         case TEST_EXECUTE:
515                 break;
516         }
517
518         options.send_file = 1;
519         options.status_code = 200;
520         options.status_text = "OK";
521         options.etag = "123456789";
522
523         snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav");
524
525         bucket_file = ast_bucket_file_retrieve(uri);
526         ast_test_validate(test, bucket_file != NULL);
527         ast_test_validate(test, !strcmp(uri, ast_sorcery_object_get_id(bucket_file)));
528         ast_test_validate(test, !ast_strlen_zero(bucket_file->path));
529         VALIDATE_STR_METADATA(test, bucket_file, "etag", options.etag);
530         VALIDATE_EXPIRES(test, bucket_file, now.tv_sec, 3);
531
532         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 0);
533
534         options.etag = "99999999";
535         ast_test_validate(test, ast_bucket_file_is_stale(bucket_file) == 1);
536
537         return AST_TEST_PASS;
538 }
539
540 AST_TEST_DEFINE(retrieve_nominal)
541 {
542         RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
543         struct timeval now = ast_tvnow();
544         char uri[1024];
545
546         switch (cmd) {
547         case TEST_INIT:
548                 info->name = __func__;
549                 info->category = CATEGORY;
550                 info->summary = "Test nominal retrieval";
551                 info->description =
552                         "Test nominal retrieval of a resource.";
553                 return AST_TEST_NOT_RUN;
554         case TEST_EXECUTE:
555                 break;
556         }
557
558         options.send_file = 1;
559         options.status_code = 200;
560         options.status_text = "OK";
561
562         snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav");
563
564         bucket_file = ast_bucket_file_retrieve(uri);
565         ast_test_validate(test, bucket_file != NULL);
566         ast_test_validate(test, !strcmp(uri, ast_sorcery_object_get_id(bucket_file)));
567         ast_test_validate(test, !ast_strlen_zero(bucket_file->path));
568         VALIDATE_EXPIRES(test, bucket_file, now.tv_sec, 3);
569
570         return AST_TEST_PASS;
571 }
572
573 AST_TEST_DEFINE(create_nominal)
574 {
575         RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);
576         struct timeval now = ast_tvnow();
577         char uri[1024];
578
579         switch (cmd) {
580         case TEST_INIT:
581                 info->name = __func__;
582                 info->category = CATEGORY;
583                 info->summary = "Test nominal creation";
584                 info->description =
585                         "Test nominal creation of a resource.";
586                 return AST_TEST_NOT_RUN;
587         case TEST_EXECUTE:
588                 break;
589         }
590
591         options.send_file = 1;
592         options.status_code = 200;
593         options.status_text = "OK";
594
595         snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav");
596
597         bucket_file = ast_bucket_file_alloc(uri);
598         ast_test_validate(test, bucket_file != NULL);
599         ast_test_validate(test, ast_bucket_file_temporary_create(bucket_file) == 0);
600         ast_test_validate(test, ast_bucket_file_create(bucket_file) == 0);
601         VALIDATE_EXPIRES(test, bucket_file, now.tv_sec, 3);
602
603         return AST_TEST_PASS;
604 }
605
606
607 static int process_config(int reload)
608 {
609         struct ast_config *config;
610         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
611         const char *bindaddr;
612         const char *bindport;
613         const char *prefix;
614         const char *enabled;
615
616         config = ast_config_load("http.conf", config_flags);
617         if (!config || config == CONFIG_STATUS_FILEINVALID) {
618                 return -1;
619         } else if (config == CONFIG_STATUS_FILEUNCHANGED) {
620                 return 0;
621         }
622
623         enabled = ast_config_option(config, "general", "enabled");
624         if (!enabled || ast_false(enabled)) {
625                 ast_config_destroy(config);
626                 return -1;
627         }
628
629         /* Construct our Server URI */
630         bindaddr = ast_config_option(config, "general", "bindaddr");
631         if (!bindaddr) {
632                 ast_config_destroy(config);
633                 return -1;
634         }
635
636         bindport = ast_config_option(config, "general", "bindport");
637         if (!bindport) {
638                 bindport = "8088";
639         }
640
641         prefix = ast_config_option(config, "general", "prefix");
642
643         snprintf(server_uri, sizeof(server_uri), "http://%s:%s%s/%s", bindaddr, bindport, S_OR(prefix, ""), TEST_URI);
644
645         ast_config_destroy(config);
646
647         return 0;
648 }
649
650 static int reload_module(void)
651 {
652         return process_config(1);
653 }
654
655 static int load_module(void)
656 {
657         if (process_config(0)) {
658                 return AST_MODULE_LOAD_DECLINE;
659         }
660
661         if (ast_http_uri_link(&test_uri)) {
662                 return AST_MODULE_LOAD_DECLINE;
663         }
664
665         AST_TEST_REGISTER(create_nominal);
666
667         AST_TEST_REGISTER(retrieve_nominal);
668         AST_TEST_REGISTER(retrieve_etag);
669         AST_TEST_REGISTER(retrieve_expires);
670         AST_TEST_REGISTER(retrieve_etag_expired);
671         AST_TEST_REGISTER(retrieve_cache_control_age);
672         AST_TEST_REGISTER(retrieve_cache_control_directives);
673
674         ast_test_register_init(CATEGORY, pre_test_cb);
675
676         return AST_MODULE_LOAD_SUCCESS;
677 }
678
679 static int unload_module(void)
680 {
681         ast_http_uri_unlink(&test_uri);
682
683         AST_TEST_UNREGISTER(create_nominal);
684
685         AST_TEST_UNREGISTER(retrieve_nominal);
686         AST_TEST_UNREGISTER(retrieve_etag);
687         AST_TEST_UNREGISTER(retrieve_expires);
688         AST_TEST_UNREGISTER(retrieve_etag_expired);
689         AST_TEST_UNREGISTER(retrieve_cache_control_age);
690         AST_TEST_UNREGISTER(retrieve_cache_control_directives);
691
692         return 0;
693 }
694
695 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "HTTP Media Cache Backend Tests",
696                 .support_level = AST_MODULE_SUPPORT_CORE,
697                 .load = load_module,
698                 .reload = reload_module,
699                 .unload = unload_module,
700         );